Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions cypress/e2e/cardColor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import { randUser } from '../utils/index.js'
import { sampleBoard } from '../utils/sampleBoard'

const user = randUser()
const boardData = sampleBoard()

const auth = {
user: user.userId,
password: user.password,
}

const useModal = (useModal) => {
return cy.request({
method: 'POST',
url: `${Cypress.env('baseUrl')}/ocs/v2.php/apps/deck/api/v1.0/config/cardDetailsInModal?format=json`,
auth,
body: { value: useModal },
}).then((response) => {
expect(response.status).to.eq(200)
})
}

describe('Card color', function () {
let boardId
before(function () {
cy.createUser(user)
cy.login(user)
cy.createExampleBoard({
user,
board: boardData,
}).then((board) => {
boardId = board.id
})
})

beforeEach(function () {
cy.login(user)
})

it('Set a color', function () {
cy.visit(`/apps/deck/#/board/${boardId}`)

const newCardTitle = 'Card with color'

cy.get('.button-vue[aria-label*="Add card"]')
.first().click()
cy.get('.stack__card-add form input#new-stack-input-main')
.type(newCardTitle)
cy.get('.stack__card-add form input[type=submit]')
.first().click()

cy.get(`.card:contains("${newCardTitle}")`).should('be.visible')
.click()

cy.get('.app-sidebar-header .action-item__menutoggle').click()
cy.get('.v-popper__popper button:contains("Change card color")').click()
cy.get('.color-picker__simple-color-circle').first().should('be.visible')
.click()
cy.get('.color-picker__navigation').find('button:contains("Choose")')
.click()

cy.get(`.card:contains("${newCardTitle}")`).should('have.css', 'background-color', 'rgb(182, 70, 157)')
})
})
8 changes: 4 additions & 4 deletions lib/Controller/CardApiController.php
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ public function get() {
*
* Get a specific card.
*/
public function create($title, $type = 'plain', $order = 999, $description = '', $duedate = null, $startdate = null, $labels = [], $users = []) {
$card = $this->cardService->create($title, $this->request->getParam('stackId'), $type, $order, $this->userId, $description, $duedate, $startdate);
public function create($title, $type = 'plain', $order = 999, $description = '', $duedate = null, $startdate = null, $labels = [], $users = [], ?string $color = null) {
$card = $this->cardService->create($title, $this->request->getParam('stackId'), $type, $order, $this->userId, $description, $duedate, $startdate, $color);

foreach ($labels as $labelId) {
$this->cardService->assignLabel($card->getId(), $labelId);
Expand All @@ -88,9 +88,9 @@ public function create($title, $type = 'plain', $order = 999, $description = '',
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function update(string $title, $type, string $owner, string $description = '', int $order = 0, $duedate = null, $startdate = null, $archived = null): DataResponse {
public function update(string $title, $type, string $owner, string $description = '', int $order = 0, $duedate = null, $startdate = null, $archived = null, ?string $color = null): DataResponse {
$done = array_key_exists('done', $this->request->getParams()) ? new OptionalNullableValue($this->request->getParam('done', null)) : null;
$card = $this->cardService->update($this->request->getParam('cardId'), $title, $this->request->getParam('stackId'), $type, $owner, $description, $order, $duedate, 0, $archived, $done, $startdate);
$card = $this->cardService->update($this->request->getParam('cardId'), $title, $this->request->getParam('stackId'), $type, $owner, $description, $order, $duedate, 0, $archived, $done, $startdate, $color);
return new DataResponse($card, HTTP::STATUS_OK);
}

Expand Down
8 changes: 4 additions & 4 deletions lib/Controller/CardController.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ public function rename(int $cardId, string $title): Card {
}

#[NoAdminRequired]
public function create(string $title, int $stackId, string $type = 'plain', int $order = 999, string $description = '', $duedate = null, $startdate = null, array $labels = [], array $users = []): Card {
$card = $this->cardService->create($title, $stackId, $type, $order, $this->userId, $description, $duedate, $startdate);
public function create(string $title, int $stackId, string $type = 'plain', int $order = 999, string $description = '', $duedate = null, $startdate = null, array $labels = [], array $users = [], ?string $color = null): Card {
$card = $this->cardService->create($title, $stackId, $type, $order, $this->userId, $description, $duedate, $startdate, $color);

foreach ($labels as $label) {
$this->assignLabel($card->getId(), $label);
Expand All @@ -64,11 +64,11 @@ public function create(string $title, int $stackId, string $type = 'plain', int
* @param $duedate
*/
#[NoAdminRequired]
public function update(int $id, string $title, int $stackId, string $type, int $order, string $description, $duedate, $deletedAt, $archived = null, $startdate = null): Card {
public function update(int $id, string $title, int $stackId, string $type, int $order, string $description, $duedate, $deletedAt, $archived = null, $startdate = null, ?string $color = null): Card {
$done = array_key_exists('done', $this->request->getParams())
? new OptionalNullableValue($this->request->getParam('done', null))
: null;
return $this->cardService->update($id, $title, $stackId, $type, $this->userId, $description, $order, $duedate, $deletedAt, $archived, $done, $startdate);
return $this->cardService->update($id, $title, $stackId, $type, $this->userId, $description, $order, $duedate, $deletedAt, $archived, $done, $startdate, $color);
}

#[NoAdminRequired]
Expand Down
9 changes: 5 additions & 4 deletions lib/Controller/CardOcsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public function __construct(

#[NoAdminRequired]
#[PublicPage]
public function create(string $title, int $stackId, ?int $boardId = null, ?string $type = 'plain', ?string $owner = null, ?int $order = 999, ?string $description = '', $duedate = null, $startdate = null, ?array $labels = [], ?array $users = []) {
public function create(string $title, int $stackId, ?int $boardId = null, ?string $type = 'plain', ?string $owner = null, ?int $order = 999, ?string $description = '', $duedate = null, $startdate = null, ?array $labels = [], ?array $users = [], ?string $color = null) {
if ($boardId) {
$board = $this->boardService->find($boardId, false);
if ($board->getExternalId()) {
Expand All @@ -47,7 +47,7 @@ public function create(string $title, int $stackId, ?int $boardId = null, ?strin
if (!$owner) {
$owner = $this->userId;
}
$card = $this->cardService->create($title, $stackId, $type, $order, $owner, $description, $duedate, $startdate);
$card = $this->cardService->create($title, $stackId, $type, $order, $owner, $description, $duedate, $startdate, $color);

// foreach ($labels as $label) {
// $this->assignLabel($card->getId(), $label);
Expand Down Expand Up @@ -113,7 +113,7 @@ public function removeLabel(?int $boardId, int $cardId, int $labelId): DataRespo

#[NoAdminRequired]
#[PublicPage]
public function update(int $id, string $title, int $stackId, string $type, int $order, string $description, $duedate, $deletedAt, int $boardId, array|string|null $owner = null, $archived = null, $startdate = null): DataResponse {
public function update(int $id, string $title, int $stackId, string $type, int $order, string $description, $duedate, $deletedAt, int $boardId, array|string|null $owner = null, $archived = null, $startdate = null, ?string $color = null): DataResponse {
$done = array_key_exists('done', $this->request->getParams())
? new OptionalNullableValue($this->request->getParam('done', null))
: null;
Expand Down Expand Up @@ -154,7 +154,8 @@ public function update(int $id, string $title, int $stackId, string $type, int $
$deletedAt,
$archived,
$done,
$startdate
$startdate,
$color
));
}

Expand Down
3 changes: 3 additions & 0 deletions lib/Db/Card.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
* @method void setTitle(string $title)
* @method string getDescription()
* @method string getDescriptionPrev()
* @method string getColor()
* @method string setColor(string $color)
* @method int getStackId()
* @method void setStackId(int $stackId)
* @method int getOrder()
Expand Down Expand Up @@ -67,6 +69,7 @@ class Card extends RelationalEntity {

protected string $title = '';
protected $description;
protected $color = null;
protected $descriptionPrev;
protected $stackId;
protected $type;
Expand Down
30 changes: 30 additions & 0 deletions lib/Migration/Version11002Date20260317170905.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<?php

/**
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

declare(strict_types=1);
namespace OCA\Deck\Migration;

use Closure;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

class Version11002Date20260317170905 extends SimpleMigrationStep {
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options) {
$schema = $schemaClosure();

if ($schema->hasTable('deck_cards')) {
$table = $schema->getTable('deck_cards');
if (!$table->hasColumn('color')) {
$table->addColumn('color', 'string', [
'notnull' => false,
'length' => 6,
]);
}
}
return $schema;
}
}
6 changes: 4 additions & 2 deletions lib/Service/CardService.php
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ public function findCalendarEntries(int $boardId): array {
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadrequestException
*/
public function create(string $title, int $stackId, string $type, int $order, string $owner, string $description = '', $duedate = null, $startdate = null): Card {
public function create(string $title, int $stackId, string $type, int $order, string $owner, string $description = '', $duedate = null, $startdate = null, ?string $color = null): Card {
$this->cardServiceValidator->check(compact('title', 'stackId', 'type', 'order', 'owner'));

$this->permissionService->checkPermission($this->stackMapper, $stackId, Acl::PERMISSION_EDIT);
Expand All @@ -201,6 +201,7 @@ public function create(string $title, int $stackId, string $type, int $order, st
$card->setDescription($description);
$card->setDuedate($duedate);
$card->setStartdate($startdate);
$card->setColor($color);
$card = $this->cardMapper->insert($card);

$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_CARD_CREATE, [], $card->getOwner());
Expand Down Expand Up @@ -243,7 +244,7 @@ public function delete(int $id): Card {
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function update(int $id, string $title, int $stackId, string $type, string $owner, string $description = '', int $order = 0, ?string $duedate = null, ?int $deletedAt = null, ?bool $archived = null, ?OptionalNullableValue $done = null, ?string $startdate = null): Card {
public function update(int $id, string $title, int $stackId, string $type, string $owner, string $description = '', int $order = 0, ?string $duedate = null, ?int $deletedAt = null, ?bool $archived = null, ?OptionalNullableValue $done = null, ?string $startdate = null, ?string $color = null): Card {
$this->cardServiceValidator->check(compact('id', 'title', 'stackId', 'type', 'owner', 'order'));

$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT, allowDeletedCard: true);
Expand Down Expand Up @@ -286,6 +287,7 @@ public function update(int $id, string $title, int $stackId, string $type, strin
$card->setOrder($order);
$card->setDuedate($duedate ? new \DateTime($duedate) : null);
$card->setStartdate($startdate ? new \DateTime($startdate) : null);
$card->setColor($color);
$resetDuedateNotification = false;
if (
$card->getDuedate() === null
Expand Down
4 changes: 4 additions & 0 deletions src/components/cards/CardItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<AttachmentDragAndDrop v-if="card" :card-id="card.id" class="drop-upload--card">
<div :ref="`card${card.id}`"
:class="{'compact': compactMode, 'current-card': currentCard, 'no-labels': !hasLabels, 'card__editable': canEdit, 'card__archived': card.archived, 'card__highlight': highlight}"
:style="{backgroundColor: color}"
tag="div"
:tabindex="0"
class="card"
Expand Down Expand Up @@ -193,6 +194,9 @@ export default {
}
return this.hasBadges
},
color() {
return this.card.color ? '#' + this.card.color : null
},
},
watch: {
currentCard(newValue) {
Expand Down
26 changes: 24 additions & 2 deletions src/components/cards/CardMenuEntries.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@

<template>
<div>
<NcColorPicker v-model="editingCardColor" clearable @submit="updateCardColor">
<NcActionButton @click="openColorPicker">
<template #icon>
<SelectColor :fill-color="cardColor" :size="20" decorative />
</template>
{{ t('deck', 'Change card color') }}
</NcActionButton>
</NcColorPicker>
<NcActionButton v-if="!hideDetailsEntry" :close-after-click="true" @click="openCard">
<CardBulletedIcon slot="icon" :size="20" decorative />
{{ t('deck', 'Card details') }}
Expand Down Expand Up @@ -62,11 +70,12 @@
</div>
</template>
<script>
import { NcActionButton } from '@nextcloud/vue'
import { NcActionButton, NcColorPicker } from '@nextcloud/vue'
import { mapGetters, mapState } from 'vuex'
import ArchiveIcon from 'vue-material-design-icons/ArchiveOutline.vue'
import CardBulletedIcon from 'vue-material-design-icons/CardBulletedOutline.vue'
import PencilIcon from 'vue-material-design-icons/PencilOutline.vue'
import SelectColor from 'vue-material-design-icons/Circle.vue'
import { generateUrl } from '@nextcloud/router'
import { getCurrentUser } from '@nextcloud/auth'
import { showUndo } from '@nextcloud/dialogs'
Expand All @@ -77,7 +86,7 @@ import { useActionsStore } from '../../stores/actions.js'

export default {
name: 'CardMenuEntries',
components: { NcActionButton, ArchiveIcon, CardBulletedIcon, PencilIcon },
components: { NcColorPicker, NcActionButton, ArchiveIcon, CardBulletedIcon, PencilIcon, SelectColor },
props: {
card: {
type: Object,
Expand All @@ -101,6 +110,7 @@ export default {
selectedBoard: '',
selectedStack: '',
stacksFromBoard: [],
editingCardColor: '',
}
},
computed: {
Expand Down Expand Up @@ -142,6 +152,9 @@ export default {
link: window.location.protocol + '//' + window.location.host + generateUrl('/apps/deck/') + `card/${this.card.id}`,
}
},
cardColor() {
return this.card.color ? '#' + this.card.color : ''
},
},
methods: {
openCard() {
Expand Down Expand Up @@ -192,6 +205,15 @@ export default {
openCardMoveDialog() {
emit('deck:card:show-move-dialog', this.card)
},
openColorPicker() {
this.editingCardColor = this.card.color ? '#' + this.card.color : ''
},
updateCardColor(val) {
this.$store.dispatch('updateCardColor', {
...this.card,
color: val ? val.substring(1) : null,
})
},
},
}
</script>
5 changes: 5 additions & 0 deletions src/store/card.js
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,11 @@ export default function cardModuleFactory() {
const updatedCard = await apiClient.updateCard(card, stack.boardId)
commit('updateCardProperty', { property: 'startdate', card: updatedCard })
},
async updateCardColor({ commit, getters }, card) {
const stack = getters.stackById(card.stackId)
const updatedCard = await apiClient.updateCard(card, stack.boardId)
commit('updateCardProperty', { property: 'color', card: updatedCard })
},

addCardData({ commit }, cardData) {
const card = { ...cardData }
Expand Down
Loading
Loading