Feat: Highlight cards with important labels
Signed-off-by: Kostiantyn Miakshyn <molodchick@gmail.com>
This commit is contained in:
49
docs/API.md
49
docs/API.md
@@ -212,28 +212,32 @@ Returns an array of board items
|
|||||||
"color": "31CC7C",
|
"color": "31CC7C",
|
||||||
"boardId": 10,
|
"boardId": 10,
|
||||||
"cardId": null,
|
"cardId": null,
|
||||||
"id": 37
|
"id": 37,
|
||||||
|
"customSettings": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "To review",
|
"title": "To review",
|
||||||
"color": "317CCC",
|
"color": "317CCC",
|
||||||
"boardId": 10,
|
"boardId": 10,
|
||||||
"cardId": null,
|
"cardId": null,
|
||||||
"id": 38
|
"id": 38,
|
||||||
|
"customSettings": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Action needed",
|
"title": "Action needed",
|
||||||
"color": "FF7A66",
|
"color": "FF7A66",
|
||||||
"boardId": 10,
|
"boardId": 10,
|
||||||
"cardId": null,
|
"cardId": null,
|
||||||
"id": 39
|
"id": 39,
|
||||||
|
"customSettings": { "isImportant": true }
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Later",
|
"title": "Later",
|
||||||
"color": "F1DB50",
|
"color": "F1DB50",
|
||||||
"boardId": 10,
|
"boardId": 10,
|
||||||
"cardId": null,
|
"cardId": null,
|
||||||
"id": 40
|
"id": 40,
|
||||||
|
"customSettings": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"acl": [],
|
"acl": [],
|
||||||
@@ -282,28 +286,32 @@ A 403 response might be returned if the users ability to create new boards has b
|
|||||||
"color": "31CC7C",
|
"color": "31CC7C",
|
||||||
"boardId": "10",
|
"boardId": "10",
|
||||||
"cardId": null,
|
"cardId": null,
|
||||||
"id": 37
|
"id": 37,
|
||||||
|
"customSettings": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "To review",
|
"title": "To review",
|
||||||
"color": "317CCC",
|
"color": "317CCC",
|
||||||
"boardId": "10",
|
"boardId": "10",
|
||||||
"cardId": null,
|
"cardId": null,
|
||||||
"id": 38
|
"id": 38,
|
||||||
|
"customSettings": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Action needed",
|
"title": "Action needed",
|
||||||
"color": "FF7A66",
|
"color": "FF7A66",
|
||||||
"boardId": "10",
|
"boardId": "10",
|
||||||
"cardId": null,
|
"cardId": null,
|
||||||
"id": 39
|
"id": 39,
|
||||||
|
"customSettings": {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Later",
|
"title": "Later",
|
||||||
"color": "F1DB50",
|
"color": "F1DB50",
|
||||||
"boardId": "10",
|
"boardId": "10",
|
||||||
"cardId": null,
|
"cardId": null,
|
||||||
"id": 40
|
"id": 40,
|
||||||
|
"customSettings": {}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"acl": [],
|
"acl": [],
|
||||||
@@ -867,7 +875,8 @@ The request can fail with a bad request response for the following reasons:
|
|||||||
"color": "31CC7C",
|
"color": "31CC7C",
|
||||||
"boardId": "2",
|
"boardId": "2",
|
||||||
"cardId": null,
|
"cardId": null,
|
||||||
"id": 5
|
"id": 5,
|
||||||
|
"customSettings": { "isImportant": false }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -875,16 +884,18 @@ The request can fail with a bad request response for the following reasons:
|
|||||||
|
|
||||||
#### Request parameters
|
#### Request parameters
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| --------- | ------- | ---------------------------------------- |
|
|----------------|---------|------------------------------------------------------------------------------|
|
||||||
| boardId | Integer | The id of the board the label belongs to |
|
| boardId | Integer | The id of the board the label belongs to |
|
||||||
|
| customSettings | Object | An key-value structure, currently supported only bool property `isImportant` |
|
||||||
|
|
||||||
#### Request data
|
#### Request data
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"title": "Finished",
|
"title": "Finished",
|
||||||
"color": "31CC7C"
|
"color": "31CC7C",
|
||||||
|
"customSettings": { "isImportant": false }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -896,10 +907,11 @@ The request can fail with a bad request response for the following reasons:
|
|||||||
|
|
||||||
#### Request parameters
|
#### Request parameters
|
||||||
|
|
||||||
| Parameter | Type | Description |
|
| Parameter | Type | Description |
|
||||||
| --------- | ------- | ---------------------------------------- |
|
| --------- | ------- |-----------------------------------------------------------------------------------|
|
||||||
| boardId | Integer | The id of the board the label belongs to |
|
| boardId | Integer | The id of the board the label belongs to |
|
||||||
| labelId | Integer | The id of the label |
|
| labelId | Integer | The id of the label |
|
||||||
|
| customSettings | Object | An key-value structure, currently supported only bool property `isImportant` |
|
||||||
|
|
||||||
|
|
||||||
#### Request data
|
#### Request data
|
||||||
@@ -907,7 +919,8 @@ The request can fail with a bad request response for the following reasons:
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"title": "Finished",
|
"title": "Finished",
|
||||||
"color": "31CC7C"
|
"color": "31CC7C",
|
||||||
|
"customSettings": { }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -50,10 +50,12 @@ class LabelApiController extends ApiController {
|
|||||||
*
|
*
|
||||||
* @params $title
|
* @params $title
|
||||||
* @params $color
|
* @params $color
|
||||||
|
* @param array<string, scalar> $customSettings
|
||||||
|
*
|
||||||
* Create a new label
|
* Create a new label
|
||||||
*/
|
*/
|
||||||
public function create($title, $color) {
|
public function create($title, $color, array $customSettings = []) {
|
||||||
$label = $this->labelService->create($title, $color, $this->request->getParam('boardId'));
|
$label = $this->labelService->create($title, $color, $this->request->getParam('boardId'), $customSettings);
|
||||||
return new DataResponse($label, HTTP::STATUS_OK);
|
return new DataResponse($label, HTTP::STATUS_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,10 +66,12 @@ class LabelApiController extends ApiController {
|
|||||||
*
|
*
|
||||||
* @params $title
|
* @params $title
|
||||||
* @params $color
|
* @params $color
|
||||||
|
* @param array<string, scalar> $customSettings
|
||||||
|
*
|
||||||
* Update a specific label
|
* Update a specific label
|
||||||
*/
|
*/
|
||||||
public function update($title, $color) {
|
public function update($title, $color, array $customSettings = []) {
|
||||||
$label = $this->labelService->update($this->request->getParam('labelId'), $title, $color);
|
$label = $this->labelService->update($this->request->getParam('labelId'), $title, $color, $customSettings);
|
||||||
return new DataResponse($label, HTTP::STATUS_OK);
|
return new DataResponse($label, HTTP::STATUS_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,10 +25,11 @@ class LabelController extends Controller {
|
|||||||
* @param $title
|
* @param $title
|
||||||
* @param $color
|
* @param $color
|
||||||
* @param $boardId
|
* @param $boardId
|
||||||
|
* @param array<string, scalar> $customSettings
|
||||||
* @return \OCP\AppFramework\Db\Entity
|
* @return \OCP\AppFramework\Db\Entity
|
||||||
*/
|
*/
|
||||||
public function create($title, $color, $boardId) {
|
public function create($title, $color, $boardId, array $customSettings = []) {
|
||||||
return $this->labelService->create($title, $color, $boardId);
|
return $this->labelService->create($title, $color, $boardId, $customSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -36,10 +37,11 @@ class LabelController extends Controller {
|
|||||||
* @param $id
|
* @param $id
|
||||||
* @param $title
|
* @param $title
|
||||||
* @param $color
|
* @param $color
|
||||||
|
* @param array<string, scalar> $customSettings
|
||||||
* @return \OCP\AppFramework\Db\Entity
|
* @return \OCP\AppFramework\Db\Entity
|
||||||
*/
|
*/
|
||||||
public function update($id, $title, $color) {
|
public function update($id, $title, $color, array $customSettings = []) {
|
||||||
return $this->labelService->update($id, $title, $color);
|
return $this->labelService->update($id, $title, $color, $customSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ namespace OCA\Deck\Db;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @method getTitle(): string
|
* @method getTitle(): string
|
||||||
|
* @method getCustomSettings(): string
|
||||||
|
* @method setCustomSettings(string $customSettings)
|
||||||
*/
|
*/
|
||||||
class Label extends RelationalEntity {
|
class Label extends RelationalEntity {
|
||||||
protected $title;
|
protected $title;
|
||||||
@@ -16,15 +18,32 @@ class Label extends RelationalEntity {
|
|||||||
protected $boardId;
|
protected $boardId;
|
||||||
protected $cardId;
|
protected $cardId;
|
||||||
protected $lastModified;
|
protected $lastModified;
|
||||||
|
protected $customSettings;
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->addType('id', 'integer');
|
$this->addType('id', 'integer');
|
||||||
$this->addType('boardId', 'integer');
|
$this->addType('boardId', 'integer');
|
||||||
$this->addType('cardId', 'integer');
|
$this->addType('cardId', 'integer');
|
||||||
$this->addType('lastModified', 'integer');
|
$this->addType('lastModified', 'integer');
|
||||||
|
$this->addType('customSettings', 'string');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getETag() {
|
public function getETag() {
|
||||||
return md5((string)$this->getLastModified());
|
return md5((string)$this->getLastModified());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCustomSettingsArray(): array {
|
||||||
|
return $this->customSettings ? json_decode($this->customSettings, true) : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCustomSettingsArray(array $customSettings): void {
|
||||||
|
$this->setCustomSettings(json_encode($customSettings ?: new \stdClass()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function jsonSerialize(): array {
|
||||||
|
$data = parent::jsonSerialize();
|
||||||
|
$data['customSettings'] = $this->getCustomSettingsArray() ?: new \stdClass();
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
32
lib/Migration/Version20000Date20250907000000.php
Normal file
32
lib/Migration/Version20000Date20250907000000.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: 2022 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\DB\ISchemaWrapper;
|
||||||
|
use OCP\DB\Types;
|
||||||
|
use OCP\Migration\IOutput;
|
||||||
|
use OCP\Migration\SimpleMigrationStep;
|
||||||
|
|
||||||
|
class Version20000Date20250907000000 extends SimpleMigrationStep {
|
||||||
|
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
|
||||||
|
/** @var ISchemaWrapper $schema */
|
||||||
|
$schema = $schemaClosure();
|
||||||
|
|
||||||
|
$table = $schema->getTable('deck_labels');
|
||||||
|
if (!$table->hasColumn('custom_settings')) {
|
||||||
|
$table->addColumn('custom_settings', Types::JSON, [
|
||||||
|
'notnull' => false,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $schema;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -340,7 +340,7 @@ class CardService {
|
|||||||
// clone labels that are assigned to card but don't exist in new board
|
// clone labels that are assigned to card but don't exist in new board
|
||||||
if (empty($filteredLabels)) {
|
if (empty($filteredLabels)) {
|
||||||
if ($this->permissionService->getPermissions($boardId)[Acl::PERMISSION_MANAGE] === true) {
|
if ($this->permissionService->getPermissions($boardId)[Acl::PERMISSION_MANAGE] === true) {
|
||||||
$newLabel = $this->labelService->create($label->getTitle(), $label->getColor(), $board->getId());
|
$newLabel = $this->labelService->create($label->getTitle(), $label->getColor(), $board->getId(), $label->getCustomSettingsArray());
|
||||||
$boardLabels[] = $label;
|
$boardLabels[] = $label;
|
||||||
$this->assignLabel($card->getId(), $newLabel->getId());
|
$this->assignLabel($card->getId(), $newLabel->getId());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ class LabelService {
|
|||||||
* @param $title
|
* @param $title
|
||||||
* @param $color
|
* @param $color
|
||||||
* @param $boardId
|
* @param $boardId
|
||||||
|
* @param array<string, scalar> $customSettings
|
||||||
* @return \OCP\AppFramework\Db\Entity
|
* @return \OCP\AppFramework\Db\Entity
|
||||||
* @throws StatusException
|
* @throws StatusException
|
||||||
* @throws \OCA\Deck\NoPermissionException
|
* @throws \OCA\Deck\NoPermissionException
|
||||||
@@ -69,7 +70,7 @@ class LabelService {
|
|||||||
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
||||||
* @throws BadRequestException
|
* @throws BadRequestException
|
||||||
*/
|
*/
|
||||||
public function create($title, $color, $boardId) {
|
public function create($title, $color, $boardId, array $customSettings = []) {
|
||||||
$this->labelServiceValidator->check(compact('title', 'color', 'boardId'));
|
$this->labelServiceValidator->check(compact('title', 'color', 'boardId'));
|
||||||
|
|
||||||
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE);
|
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE);
|
||||||
@@ -89,6 +90,7 @@ class LabelService {
|
|||||||
$label->setTitle($title);
|
$label->setTitle($title);
|
||||||
$label->setColor($color);
|
$label->setColor($color);
|
||||||
$label->setBoardId($boardId);
|
$label->setBoardId($boardId);
|
||||||
|
$label->setCustomSettingsArray($customSettings);
|
||||||
$this->changeHelper->boardChanged($boardId);
|
$this->changeHelper->boardChanged($boardId);
|
||||||
return $this->labelMapper->insert($label);
|
return $this->labelMapper->insert($label);
|
||||||
}
|
}
|
||||||
@@ -99,7 +101,7 @@ class LabelService {
|
|||||||
$originLabel = $this->find($labelId);
|
$originLabel = $this->find($labelId);
|
||||||
$filteredValues = array_values(array_filter($boardLabels, fn ($item) => $item->getTitle() === $originLabel->getTitle()));
|
$filteredValues = array_values(array_filter($boardLabels, fn ($item) => $item->getTitle() === $originLabel->getTitle()));
|
||||||
if (empty($filteredValues)) {
|
if (empty($filteredValues)) {
|
||||||
$label = $this->create($originLabel->getTitle(), $originLabel->getColor(), $targetBoardId);
|
$label = $this->create($originLabel->getTitle(), $originLabel->getColor(), $targetBoardId, $originLabel->getCustomSettingsArray());
|
||||||
return $label;
|
return $label;
|
||||||
}
|
}
|
||||||
return $originLabel;
|
return $originLabel;
|
||||||
@@ -130,6 +132,7 @@ class LabelService {
|
|||||||
* @param $id
|
* @param $id
|
||||||
* @param $title
|
* @param $title
|
||||||
* @param $color
|
* @param $color
|
||||||
|
* @param array<string, scalar> $customSettings
|
||||||
* @return \OCP\AppFramework\Db\Entity
|
* @return \OCP\AppFramework\Db\Entity
|
||||||
* @throws StatusException
|
* @throws StatusException
|
||||||
* @throws \OCA\Deck\NoPermissionException
|
* @throws \OCA\Deck\NoPermissionException
|
||||||
@@ -137,7 +140,7 @@ class LabelService {
|
|||||||
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
||||||
* @throws BadRequestException
|
* @throws BadRequestException
|
||||||
*/
|
*/
|
||||||
public function update($id, $title, $color) {
|
public function update($id, $title, $color, array $customSettings = []) {
|
||||||
$this->labelServiceValidator->check(compact('title', 'color', 'id'));
|
$this->labelServiceValidator->check(compact('title', 'color', 'id'));
|
||||||
|
|
||||||
$this->permissionService->checkPermission($this->labelMapper, $id, Acl::PERMISSION_MANAGE);
|
$this->permissionService->checkPermission($this->labelMapper, $id, Acl::PERMISSION_MANAGE);
|
||||||
@@ -161,6 +164,7 @@ class LabelService {
|
|||||||
|
|
||||||
$label->setTitle($title);
|
$label->setTitle($title);
|
||||||
$label->setColor($color);
|
$label->setColor($color);
|
||||||
|
$label->setCustomSettingsArray($customSettings);
|
||||||
$this->changeHelper->boardChanged($label->getBoardId());
|
$this->changeHelper->boardChanged($label->getBoardId());
|
||||||
return $this->labelMapper->update($label);
|
return $this->labelMapper->update($label);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -281,6 +281,7 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.board {
|
.board {
|
||||||
|
padding-left: $board-gap;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
<NcAppSidebar v-if="board != null"
|
<NcAppSidebar v-if="board != null"
|
||||||
:actions="[]"
|
:actions="[]"
|
||||||
:name="board.title"
|
:name="board.title"
|
||||||
|
style="width: 400px"
|
||||||
@close="closeSidebar">
|
@close="closeSidebar">
|
||||||
<NcAppSidebarTab id="sharing"
|
<NcAppSidebarTab id="sharing"
|
||||||
:order="0"
|
:order="0"
|
||||||
|
|||||||
@@ -15,7 +15,11 @@
|
|||||||
@input="updateColor">
|
@input="updateColor">
|
||||||
<div :style="{ backgroundColor: '#' + editingLabel.color }" class="color0 icon-colorpicker" />
|
<div :style="{ backgroundColor: '#' + editingLabel.color }" class="color0 icon-colorpicker" />
|
||||||
</NcColorPicker>
|
</NcColorPicker>
|
||||||
<input v-model="editingLabel.title" type="text">
|
<NcCheckboxRadioSwitch v-model="editingLabelIsImportant"
|
||||||
|
type="switch">
|
||||||
|
{{ t('deck', 'Important') }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
<input v-model="editingLabel.title" type="text" style="margin-right: 20px;">
|
||||||
<input :disabled="!editLabelObjValidated"
|
<input :disabled="!editLabelObjValidated"
|
||||||
type="submit"
|
type="submit"
|
||||||
value=""
|
value=""
|
||||||
@@ -34,10 +38,18 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div v-if="canManage && !isArchived" class="label-title" @click="clickEdit(label)">
|
<div v-if="canManage && !isArchived" class="label-title" @click="clickEdit(label)">
|
||||||
<span :style="{ backgroundColor: `#${label.color}`, color: textColor(label.color) }">{{ label.title }}</span>
|
<span :style="{
|
||||||
|
backgroundColor: `#${label.color}`,
|
||||||
|
color: textColor(label.color),
|
||||||
|
fontWeight: label.customSettings.isImportant ? 'bold' : 'normal'
|
||||||
|
}">{{ label.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="label-title">
|
<div v-else class="label-title">
|
||||||
<span :style="{ backgroundColor: `#${label.color}`, color: textColor(label.color) }">{{ label.title }}</span>
|
<span :style="{
|
||||||
|
backgroundColor: `#${label.color}`,
|
||||||
|
color: textColor(label.color),
|
||||||
|
fontWeight: label.customSettings.isImportant ? 'bold' : 'normal'
|
||||||
|
}">{{ label.title }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<NcActions v-if="canManage && !isArchived">
|
<NcActions v-if="canManage && !isArchived">
|
||||||
@@ -62,7 +74,11 @@
|
|||||||
@input="updateColor">
|
@input="updateColor">
|
||||||
<div :style="{ backgroundColor: '#' + addLabelObj.color }" class="color0 icon-colorpicker" />
|
<div :style="{ backgroundColor: '#' + addLabelObj.color }" class="color0 icon-colorpicker" />
|
||||||
</NcColorPicker>
|
</NcColorPicker>
|
||||||
<input v-model="addLabelObj.title" type="text">
|
<NcCheckboxRadioSwitch v-model="addLabelIsImportant"
|
||||||
|
type="switch">
|
||||||
|
{{ t('deck', 'Important') }}
|
||||||
|
</NcCheckboxRadioSwitch>
|
||||||
|
<input v-model="addLabelObj.title" type="text" style="margin-right: 20px;">
|
||||||
<input :disabled="!addLabelObjValidated"
|
<input :disabled="!addLabelObjValidated"
|
||||||
type="submit"
|
type="submit"
|
||||||
value=""
|
value=""
|
||||||
@@ -88,7 +104,7 @@
|
|||||||
|
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
import Color from '../../mixins/color.js'
|
import Color from '../../mixins/color.js'
|
||||||
import { NcColorPicker, NcActions, NcActionButton } from '@nextcloud/vue'
|
import { NcColorPicker, NcActions, NcActionButton, NcCheckboxRadioSwitch } from '@nextcloud/vue'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TagsTabSidebar',
|
name: 'TagsTabSidebar',
|
||||||
@@ -96,6 +112,7 @@ export default {
|
|||||||
NcColorPicker,
|
NcColorPicker,
|
||||||
NcActions,
|
NcActions,
|
||||||
NcActionButton,
|
NcActionButton,
|
||||||
|
NcCheckboxRadioSwitch,
|
||||||
},
|
},
|
||||||
mixins: [Color],
|
mixins: [Color],
|
||||||
data() {
|
data() {
|
||||||
@@ -139,7 +156,22 @@ export default {
|
|||||||
labelsSorted() {
|
labelsSorted() {
|
||||||
return [...this.labels].sort((a, b) => a.title.localeCompare(b.title))
|
return [...this.labels].sort((a, b) => a.title.localeCompare(b.title))
|
||||||
},
|
},
|
||||||
|
addLabelIsImportant: {
|
||||||
|
get() {
|
||||||
|
return this.addLabelObj?.customSettings?.isImportant || false
|
||||||
|
},
|
||||||
|
set(isImportant) {
|
||||||
|
this.addLabelObj.customSettings = { ...this.addLabelObj.customSettings, isImportant }
|
||||||
|
},
|
||||||
|
},
|
||||||
|
editingLabelIsImportant: {
|
||||||
|
get() {
|
||||||
|
return this.editingLabel?.customSettings?.isImportant
|
||||||
|
},
|
||||||
|
set(isImportant) {
|
||||||
|
this.editingLabel.customSettings = { ...this.editingLabel.customSettings, isImportant }
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
updateColor(c) {
|
updateColor(c) {
|
||||||
@@ -157,15 +189,23 @@ export default {
|
|||||||
this.$store.dispatch('removeLabelFromCurrentBoard', id)
|
this.$store.dispatch('removeLabelFromCurrentBoard', id)
|
||||||
},
|
},
|
||||||
updateLabel(label) {
|
updateLabel(label) {
|
||||||
this.$store.dispatch('updateLabelFromCurrentBoard', this.editingLabel)
|
const payload = {
|
||||||
|
...this.editingLabel,
|
||||||
|
customSettings: { ...this.editingLabel.customSettings },
|
||||||
|
}
|
||||||
|
this.$store.dispatch('updateLabelFromCurrentBoard', payload)
|
||||||
this.editingLabelId = null
|
this.editingLabelId = null
|
||||||
},
|
},
|
||||||
clickShowAddLabel() {
|
clickShowAddLabel() {
|
||||||
this.addLabelObj = { cardId: null, color: this.defaultColors[Math.floor(Math.random() * this.defaultColors.length)], title: '' }
|
this.addLabelObj = { cardId: null, color: this.defaultColors[Math.floor(Math.random() * this.defaultColors.length)], title: '', customSettings: {} }
|
||||||
this.addLabel = true
|
this.addLabel = true
|
||||||
},
|
},
|
||||||
clickAddLabel() {
|
clickAddLabel() {
|
||||||
this.$store.dispatch('addLabelToCurrentBoard', this.addLabelObj)
|
const payload = {
|
||||||
|
...this.addLabelObj,
|
||||||
|
customSettings: { ...this.addLabelObj.customSettings },
|
||||||
|
}
|
||||||
|
this.$store.dispatch('addLabelToCurrentBoard', payload)
|
||||||
this.addLabel = false
|
this.addLabel = false
|
||||||
this.addLabelObj = null
|
this.addLabelObj = null
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -6,10 +6,19 @@
|
|||||||
<template>
|
<template>
|
||||||
<AttachmentDragAndDrop v-if="card" :card-id="card.id" class="drop-upload--card">
|
<AttachmentDragAndDrop v-if="card" :card-id="card.id" class="drop-upload--card">
|
||||||
<div :ref="`card${card.id}`"
|
<div :ref="`card${card.id}`"
|
||||||
:class="{'compact': compactMode, 'current-card': currentCard, 'no-labels': !hasLabels, 'card__editable': canEdit, 'card__archived': card.archived, 'card__highlight': highlight}"
|
:class="{
|
||||||
|
'compact': compactMode,
|
||||||
|
'current-card': currentCard,
|
||||||
|
'no-labels': !hasLabels,
|
||||||
|
'card__editable': canEdit,
|
||||||
|
'card__archived': card.archived,
|
||||||
|
'card__highlight': highlight,
|
||||||
|
'card__important': !!importantColor,
|
||||||
|
}"
|
||||||
tag="div"
|
tag="div"
|
||||||
:tabindex="0"
|
:tabindex="0"
|
||||||
class="card"
|
class="card"
|
||||||
|
:style="{'box-shadow': importantColor ? `-5px 0px 0px 0px ${importantColor}` : null}"
|
||||||
@click="openCard"
|
@click="openCard"
|
||||||
@keyup.self="handleCardKeyboardShortcut"
|
@keyup.self="handleCardKeyboardShortcut"
|
||||||
@mouseenter="focus(card.id)">
|
@mouseenter="focus(card.id)">
|
||||||
@@ -133,6 +142,14 @@ export default {
|
|||||||
currentBoard: state => state.currentBoard,
|
currentBoard: state => state.currentBoard,
|
||||||
showCardCover: state => state.showCardCover,
|
showCardCover: state => state.showCardCover,
|
||||||
shortcutLock: state => state.shortcutLock,
|
shortcutLock: state => state.shortcutLock,
|
||||||
|
importantColor() {
|
||||||
|
for (const label of this.card.labels) {
|
||||||
|
if (label.customSettings.isImportant) {
|
||||||
|
return '#' + label.color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
}),
|
}),
|
||||||
...mapGetters([
|
...mapGetters([
|
||||||
'isArchived',
|
'isArchived',
|
||||||
@@ -421,6 +438,10 @@ export default {
|
|||||||
&.card__highlight {
|
&.card__highlight {
|
||||||
animation: highlight 2s;
|
animation: highlight 2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:not(.card__important) {
|
||||||
|
box-shadow: -5px 0px 0px 0px var(--color-main-background);
|
||||||
|
}
|
||||||
.card-labels {
|
.card-labels {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: end;
|
align-items: end;
|
||||||
|
|||||||
@@ -272,6 +272,7 @@ export default new Vuex.Store({
|
|||||||
|
|
||||||
labelToUpdate.title = newLabel.title
|
labelToUpdate.title = newLabel.title
|
||||||
labelToUpdate.color = newLabel.color
|
labelToUpdate.color = newLabel.color
|
||||||
|
labelToUpdate.customSettings = newLabel.customSettings
|
||||||
},
|
},
|
||||||
addLabelToCurrentBoard(state, newLabel) {
|
addLabelToCurrentBoard(state, newLabel) {
|
||||||
state.currentBoard.labels.push(newLabel)
|
state.currentBoard.labels.push(newLabel)
|
||||||
|
|||||||
@@ -45,11 +45,13 @@ class LabelTest extends TestCase {
|
|||||||
'lastModified' => null,
|
'lastModified' => null,
|
||||||
'color' => '000000',
|
'color' => '000000',
|
||||||
'ETag' => $label->getETag(),
|
'ETag' => $label->getETag(),
|
||||||
|
'customSettings' => new \stdClass(),
|
||||||
], $label->jsonSerialize());
|
], $label->jsonSerialize());
|
||||||
}
|
}
|
||||||
public function testJsonSerializeCard() {
|
public function testJsonSerializeCard() {
|
||||||
$label = $this->createLabel();
|
$label = $this->createLabel();
|
||||||
$label->setCardId(123);
|
$label->setCardId(123);
|
||||||
|
$label->setCustomSettingsArray(['isImportant' => true]);
|
||||||
$this->assertEquals([
|
$this->assertEquals([
|
||||||
'id' => 1,
|
'id' => 1,
|
||||||
'title' => 'My Label',
|
'title' => 'My Label',
|
||||||
@@ -58,6 +60,7 @@ class LabelTest extends TestCase {
|
|||||||
'lastModified' => null,
|
'lastModified' => null,
|
||||||
'color' => '000000',
|
'color' => '000000',
|
||||||
'ETag' => $label->getETag(),
|
'ETag' => $label->getETag(),
|
||||||
|
'customSettings' => ['isImportant' => true]
|
||||||
], $label->jsonSerialize());
|
], $label->jsonSerialize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,14 +75,16 @@ class LabelServiceTest extends TestCase {
|
|||||||
$label->setTitle('Label title');
|
$label->setTitle('Label title');
|
||||||
$label->setBoardId(123);
|
$label->setBoardId(123);
|
||||||
$label->setColor('00ff00');
|
$label->setColor('00ff00');
|
||||||
|
$label->setCustomSettingsArray(['isImportant' => true]);
|
||||||
$this->labelMapper->expects($this->once())
|
$this->labelMapper->expects($this->once())
|
||||||
->method('insert')
|
->method('insert')
|
||||||
->willReturn($label);
|
->willReturn($label);
|
||||||
$b = $this->labelService->create('Label title', '00ff00', 123);
|
$b = $this->labelService->create('Label title', '00ff00', 123, ['isImportant' => true]);
|
||||||
|
|
||||||
$this->assertEquals($b->getTitle(), 'Label title');
|
$this->assertEquals($b->getTitle(), 'Label title');
|
||||||
$this->assertEquals($b->getBoardId(), 123);
|
$this->assertEquals($b->getBoardId(), 123);
|
||||||
$this->assertEquals($b->getColor(), '00ff00');
|
$this->assertEquals($b->getColor(), '00ff00');
|
||||||
|
$this->assertEquals($b->getCustomSettingsArray(), ['isImportant' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -111,6 +113,7 @@ class LabelServiceTest extends TestCase {
|
|||||||
$label->setId(1);
|
$label->setId(1);
|
||||||
$label->setTitle('title');
|
$label->setTitle('title');
|
||||||
$label->setColor('00ff00');
|
$label->setColor('00ff00');
|
||||||
|
$label->setCustomSettingsArray(['isImportant' => true]);
|
||||||
$this->labelMapper->expects($this->once())
|
$this->labelMapper->expects($this->once())
|
||||||
->method('find')
|
->method('find')
|
||||||
->willReturn($label);
|
->willReturn($label);
|
||||||
@@ -119,6 +122,7 @@ class LabelServiceTest extends TestCase {
|
|||||||
$expectedLabel->setTitle('title');
|
$expectedLabel->setTitle('title');
|
||||||
$expectedLabel->setColor('00ff00');
|
$expectedLabel->setColor('00ff00');
|
||||||
$expectedLabel->setBoardId(1);
|
$expectedLabel->setBoardId(1);
|
||||||
|
$expectedLabel->setCustomSettingsArray(['isImportant' => true]);
|
||||||
$this->labelMapper->expects($this->once())
|
$this->labelMapper->expects($this->once())
|
||||||
->method('insert')
|
->method('insert')
|
||||||
->with($expectedLabel)
|
->with($expectedLabel)
|
||||||
|
|||||||
Reference in New Issue
Block a user