mirror of
https://github.com/hcengineering/platform.git
synced 2024-12-23 03:22:19 +03:00
Use tags for card labels (#1888)
This commit is contained in:
parent
4e69e4f0d1
commit
a224a154e5
@ -42,6 +42,8 @@
|
|||||||
"@anticrm/task": "~0.6.0",
|
"@anticrm/task": "~0.6.0",
|
||||||
"@anticrm/model-task": "~0.6.0",
|
"@anticrm/model-task": "~0.6.0",
|
||||||
"@anticrm/workbench": "~0.6.1",
|
"@anticrm/workbench": "~0.6.1",
|
||||||
"@anticrm/model-preference": "~0.6.0"
|
"@anticrm/model-preference": "~0.6.0",
|
||||||
|
"@anticrm/tags": "~0.6.2",
|
||||||
|
"@anticrm/model-tags": "~0.6.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
// To help typescript locate view plugin properly
|
// To help typescript locate view plugin properly
|
||||||
import type { Board, Card, CardLabel, MenuPage, CommonBoardPreference, CardCover } from '@anticrm/board'
|
import type { Board, Card, MenuPage, CommonBoardPreference, CardCover } from '@anticrm/board'
|
||||||
import type { Employee } from '@anticrm/contact'
|
import type { Employee } from '@anticrm/contact'
|
||||||
import { DOMAIN_MODEL, IndexKind, Markup, Ref, Type } from '@anticrm/core'
|
import { DOMAIN_MODEL, IndexKind, Markup, Ref, Type } from '@anticrm/core'
|
||||||
import {
|
import {
|
||||||
@ -33,7 +33,7 @@ import {
|
|||||||
import attachment from '@anticrm/model-attachment'
|
import attachment from '@anticrm/model-attachment'
|
||||||
import chunter from '@anticrm/model-chunter'
|
import chunter from '@anticrm/model-chunter'
|
||||||
import contact from '@anticrm/model-contact'
|
import contact from '@anticrm/model-contact'
|
||||||
import core, { TAttachedDoc, TDoc, TType } from '@anticrm/model-core'
|
import core, { TDoc, TType } from '@anticrm/model-core'
|
||||||
import task, { TSpaceWithStates, TTask } from '@anticrm/model-task'
|
import task, { TSpaceWithStates, TTask } from '@anticrm/model-task'
|
||||||
import view, { actionTemplates, createAction } from '@anticrm/model-view'
|
import view, { actionTemplates, createAction } from '@anticrm/model-view'
|
||||||
import workbench, { Application } from '@anticrm/model-workbench'
|
import workbench, { Application } from '@anticrm/model-workbench'
|
||||||
@ -49,14 +49,6 @@ export class TBoard extends TSpaceWithStates implements Board {
|
|||||||
background!: string
|
background!: string
|
||||||
}
|
}
|
||||||
|
|
||||||
@Model(board.class.CardLabel, core.class.AttachedDoc, DOMAIN_MODEL)
|
|
||||||
@UX(board.string.Labels)
|
|
||||||
export class TCardLabel extends TAttachedDoc implements CardLabel {
|
|
||||||
title!: string
|
|
||||||
color!: number
|
|
||||||
isHidden?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
function TypeCardCover (): Type<CardCover> {
|
function TypeCardCover (): Type<CardCover> {
|
||||||
return { _class: board.class.CardCover, label: board.string.Cover }
|
return { _class: board.class.CardCover, label: board.string.Cover }
|
||||||
}
|
}
|
||||||
@ -90,9 +82,6 @@ export class TCard extends TTask implements Card {
|
|||||||
@Index(IndexKind.FullText)
|
@Index(IndexKind.FullText)
|
||||||
description!: Markup
|
description!: Markup
|
||||||
|
|
||||||
@Prop(Collection(board.class.CardLabel), board.string.Labels)
|
|
||||||
labels!: Ref<CardLabel>[]
|
|
||||||
|
|
||||||
@Prop(TypeString(), board.string.Location)
|
@Prop(TypeString(), board.string.Location)
|
||||||
@Index(IndexKind.FullText)
|
@Index(IndexKind.FullText)
|
||||||
location?: string
|
location?: string
|
||||||
@ -121,7 +110,7 @@ export class TMenuPage extends TDoc implements MenuPage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createModel (builder: Builder): void {
|
export function createModel (builder: Builder): void {
|
||||||
builder.createModel(TBoard, TCard, TCardLabel, TMenuPage, TCommonBoardPreference, TCardCover)
|
builder.createModel(TBoard, TCard, TMenuPage, TCommonBoardPreference, TCardCover)
|
||||||
|
|
||||||
builder.createDoc(board.class.MenuPage, core.space.Model, {
|
builder.createDoc(board.class.MenuPage, core.space.Model, {
|
||||||
component: board.component.Archive,
|
component: board.component.Archive,
|
||||||
@ -216,14 +205,6 @@ export function createModel (builder: Builder): void {
|
|||||||
presenter: board.component.CardPresenter
|
presenter: board.component.CardPresenter
|
||||||
})
|
})
|
||||||
|
|
||||||
builder.mixin(board.class.CardLabel, core.class.Class, view.mixin.AttributePresenter, {
|
|
||||||
presenter: board.component.CardLabelPresenter
|
|
||||||
})
|
|
||||||
|
|
||||||
builder.mixin(board.class.CardLabel, core.class.Class, view.mixin.CollectionPresenter, {
|
|
||||||
presenter: board.component.CardLabelPresenter
|
|
||||||
})
|
|
||||||
|
|
||||||
builder.mixin(board.class.Board, core.class.Class, view.mixin.AttributePresenter, {
|
builder.mixin(board.class.Board, core.class.Class, view.mixin.AttributePresenter, {
|
||||||
presenter: board.component.BoardPresenter
|
presenter: board.component.BoardPresenter
|
||||||
})
|
})
|
||||||
|
@ -13,12 +13,29 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import { Doc, Ref, Space, TxOperations } from '@anticrm/core'
|
import {
|
||||||
import { MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model'
|
AttachedDoc,
|
||||||
|
Class,
|
||||||
|
Doc,
|
||||||
|
DOMAIN_TX,
|
||||||
|
generateId,
|
||||||
|
Ref,
|
||||||
|
Space,
|
||||||
|
TxCollectionCUD,
|
||||||
|
TxCreateDoc,
|
||||||
|
TxCUD,
|
||||||
|
TxOperations,
|
||||||
|
TxProcessor,
|
||||||
|
TxUpdateDoc
|
||||||
|
} from '@anticrm/core'
|
||||||
|
import { createOrUpdate, MigrateOperation, MigrationClient, MigrationUpgradeClient } from '@anticrm/model'
|
||||||
import core from '@anticrm/model-core'
|
import core from '@anticrm/model-core'
|
||||||
import { createKanbanTemplate, createSequence, DOMAIN_TASK } from '@anticrm/model-task'
|
import { createKanbanTemplate, createSequence, DOMAIN_TASK } from '@anticrm/model-task'
|
||||||
import task, { createKanban, KanbanTemplate } from '@anticrm/task'
|
import task, { createKanban, KanbanTemplate } from '@anticrm/task'
|
||||||
|
import { DOMAIN_TAGS } from '@anticrm/model-tags'
|
||||||
|
import tags, { TagElement, TagReference } from '@anticrm/tags'
|
||||||
import board from './plugin'
|
import board from './plugin'
|
||||||
|
import { Board, Card } from '@anticrm/board'
|
||||||
|
|
||||||
async function createSpace (tx: TxOperations): Promise<void> {
|
async function createSpace (tx: TxOperations): Promise<void> {
|
||||||
const current = await tx.findOne(core.class.Space, {
|
const current = await tx.findOne(core.class.Space, {
|
||||||
@ -74,12 +91,87 @@ async function createDefaults (tx: TxOperations): Promise<void> {
|
|||||||
await createSpace(tx)
|
await createSpace(tx)
|
||||||
await createSequence(tx, board.class.Card)
|
await createSequence(tx, board.class.Card)
|
||||||
await createDefaultKanban(tx)
|
await createDefaultKanban(tx)
|
||||||
|
await createOrUpdate(
|
||||||
|
tx,
|
||||||
|
tags.class.TagCategory,
|
||||||
|
tags.space.Tags,
|
||||||
|
{
|
||||||
|
icon: tags.icon.Tags,
|
||||||
|
label: 'Other',
|
||||||
|
targetClass: board.class.Card,
|
||||||
|
tags: [],
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
board.category.Other
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CardLabel extends AttachedDoc {
|
||||||
|
title: string
|
||||||
|
color: number
|
||||||
|
isHidden?: boolean
|
||||||
|
}
|
||||||
async function migrateLabels (client: MigrationClient): Promise<void> {
|
async function migrateLabels (client: MigrationClient): Promise<void> {
|
||||||
const cards = await client.find(DOMAIN_TASK, { _class: board.class.Card, labels: { $exists: false, $in: [null] } })
|
const objectClass = 'board:class:CardLabel' as Ref<Class<Doc>>
|
||||||
|
const txes = await client.find<TxCUD<CardLabel>>(DOMAIN_TX, { objectClass }, { sort: { modifiedOn: 1 } })
|
||||||
|
const collectionTxes = await client.find<TxCollectionCUD<Board, CardLabel>>(
|
||||||
|
DOMAIN_TX,
|
||||||
|
{ 'tx.objectClass': objectClass },
|
||||||
|
{ sort: { modifiedOn: 1 } }
|
||||||
|
)
|
||||||
|
await Promise.all([...txes, ...collectionTxes].map(({ _id }) => client.delete<Doc>(DOMAIN_TX, _id)))
|
||||||
|
const removed = txes.filter(({ _class }) => _class === core.class.TxRemoveDoc).map(({ objectId }) => objectId)
|
||||||
|
const createTxes = txes.filter(
|
||||||
|
({ _class, objectId }) => _class === core.class.TxCreateDoc && !removed.includes(objectId)
|
||||||
|
) as unknown as TxCreateDoc<CardLabel>[]
|
||||||
|
const cardLabels = createTxes.map((createTx) => {
|
||||||
|
const cardLabel = TxProcessor.createDoc2Doc(createTx)
|
||||||
|
const updateTxes = collectionTxes
|
||||||
|
.map(({ tx }) => tx)
|
||||||
|
.filter(
|
||||||
|
({ _class, objectId }) => _class === core.class.TxUpdateDoc && objectId === createTx.objectId
|
||||||
|
) as unknown as TxUpdateDoc<CardLabel>[]
|
||||||
|
return updateTxes.reduce((label, updateTx) => TxProcessor.updateDoc2Doc(label, updateTx), cardLabel)
|
||||||
|
})
|
||||||
|
await Promise.all(
|
||||||
|
cardLabels.map((cardLabel) =>
|
||||||
|
client.create<TagElement>(DOMAIN_TAGS, {
|
||||||
|
_class: tags.class.TagElement,
|
||||||
|
space: tags.space.Tags,
|
||||||
|
targetClass: board.class.Card,
|
||||||
|
category: board.category.Other,
|
||||||
|
_id: cardLabel._id as unknown as Ref<TagElement>,
|
||||||
|
modifiedBy: cardLabel.modifiedBy,
|
||||||
|
modifiedOn: cardLabel.modifiedOn,
|
||||||
|
title: cardLabel.title,
|
||||||
|
color: cardLabel.color,
|
||||||
|
description: ''
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
const cards = (await client.find<Card>(DOMAIN_TASK, { _class: board.class.Card })).filter((card) =>
|
||||||
|
Array.isArray(card.labels)
|
||||||
|
)
|
||||||
for (const card of cards) {
|
for (const card of cards) {
|
||||||
await client.update(DOMAIN_TASK, { _id: card._id }, { labels: [] })
|
const labelRefs = card.labels as unknown as Array<Ref<CardLabel>>
|
||||||
|
await client.update<Card>(DOMAIN_TASK, { _id: card._id }, { labels: labelRefs.length })
|
||||||
|
for (const labelRef of labelRefs) {
|
||||||
|
const cardLabel = cardLabels.find(({ _id }) => _id === labelRef)
|
||||||
|
if (cardLabel === undefined) continue
|
||||||
|
await client.create<TagReference>(DOMAIN_TAGS, {
|
||||||
|
_class: tags.class.TagReference,
|
||||||
|
attachedToClass: board.class.Card,
|
||||||
|
_id: generateId(),
|
||||||
|
attachedTo: card._id,
|
||||||
|
space: card.space,
|
||||||
|
tag: cardLabel._id as unknown as Ref<TagElement>,
|
||||||
|
title: cardLabel.title,
|
||||||
|
color: cardLabel.color,
|
||||||
|
modifiedBy: cardLabel.modifiedBy,
|
||||||
|
modifiedOn: cardLabel.modifiedOn,
|
||||||
|
collection: 'labels'
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,12 +15,4 @@
|
|||||||
|
|
||||||
import { Builder } from '@anticrm/model'
|
import { Builder } from '@anticrm/model'
|
||||||
|
|
||||||
import serverCore from '@anticrm/server-core'
|
export function createModel (builder: Builder): void {}
|
||||||
import core from '@anticrm/core'
|
|
||||||
import serverBoard from '@anticrm/server-board'
|
|
||||||
|
|
||||||
export function createModel (builder: Builder): void {
|
|
||||||
builder.createDoc(serverCore.class.Trigger, core.space.Model, {
|
|
||||||
trigger: serverBoard.trigger.OnLabelDelete
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -40,6 +40,7 @@
|
|||||||
"@anticrm/model-chunter": "~0.6.0",
|
"@anticrm/model-chunter": "~0.6.0",
|
||||||
"@anticrm/workbench": "~0.6.1",
|
"@anticrm/workbench": "~0.6.1",
|
||||||
"@anticrm/view": "~0.6.0",
|
"@anticrm/view": "~0.6.0",
|
||||||
"@anticrm/model-presentation": "~0.6.0"
|
"@anticrm/model-presentation": "~0.6.0",
|
||||||
|
"@anticrm/tags": "~0.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ import {
|
|||||||
WonStateTemplate
|
WonStateTemplate
|
||||||
} from '@anticrm/task'
|
} from '@anticrm/task'
|
||||||
import { AnyComponent } from '@anticrm/ui'
|
import { AnyComponent } from '@anticrm/ui'
|
||||||
|
import tags from '@anticrm/tags'
|
||||||
import task from './plugin'
|
import task from './plugin'
|
||||||
|
|
||||||
export { createKanbanTemplate, createSequence, taskOperation } from './migration'
|
export { createKanbanTemplate, createSequence, taskOperation } from './migration'
|
||||||
@ -123,6 +124,9 @@ export class TTask extends TAttachedDoc implements Task {
|
|||||||
|
|
||||||
@Prop(Collection(task.class.TodoItem), task.string.Todos)
|
@Prop(Collection(task.class.TodoItem), task.string.Todos)
|
||||||
todoItems!: number
|
todoItems!: number
|
||||||
|
|
||||||
|
@Prop(Collection(tags.class.TagReference, task.string.TaskLabels), task.string.TaskLabels)
|
||||||
|
labels!: number
|
||||||
}
|
}
|
||||||
|
|
||||||
@Model(task.class.TodoItem, core.class.AttachedDoc, DOMAIN_TASK, [task.interface.DocWithRank])
|
@Model(task.class.TodoItem, core.class.AttachedDoc, DOMAIN_TASK, [task.interface.DocWithRank])
|
||||||
@ -175,10 +179,6 @@ export class TIssue extends TTask implements Issue {
|
|||||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
|
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments, undefined, attachment.string.Files)
|
||||||
attachments!: number
|
attachments!: number
|
||||||
|
|
||||||
@Prop(TypeString(), task.string.TaskLabels)
|
|
||||||
@Index(IndexKind.FullText)
|
|
||||||
labels!: string
|
|
||||||
|
|
||||||
@Prop(TypeRef(contact.class.Employee), task.string.TaskAssignee)
|
@Prop(TypeRef(contact.class.Employee), task.string.TaskAssignee)
|
||||||
declare assignee: Ref<Employee> | null
|
declare assignee: Ref<Employee> | null
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@
|
|||||||
"@anticrm/workbench": "~0.6.1",
|
"@anticrm/workbench": "~0.6.1",
|
||||||
"svelte": "^3.47",
|
"svelte": "^3.47",
|
||||||
"@anticrm/kanban": "~0.6.0",
|
"@anticrm/kanban": "~0.6.0",
|
||||||
"@anticrm/preference": "~0.6.0"
|
"@anticrm/preference": "~0.6.0",
|
||||||
|
"@anticrm/tags": "~0.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,6 @@
|
|||||||
assignee: null,
|
assignee: null,
|
||||||
description: '',
|
description: '',
|
||||||
members: [],
|
members: [],
|
||||||
labels: [],
|
|
||||||
location: '',
|
location: '',
|
||||||
startDate: null,
|
startDate: null,
|
||||||
dueDate: null
|
dueDate: null
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
const isArchived = { $nin: [true] }
|
const isArchived = { $nin: [true] }
|
||||||
const query = createQuery()
|
const query = createQuery()
|
||||||
let states: Ref<State>[]
|
let states: Ref<State>[] = []
|
||||||
$: query.query(task.class.State, { space, isArchived }, (result) => {
|
$: query.query(task.class.State, { space, isArchived }, (result) => {
|
||||||
states = result.map(({ _id }) => _id)
|
states = result.map(({ _id }) => _id)
|
||||||
})
|
})
|
||||||
|
@ -52,7 +52,6 @@
|
|||||||
assignee: null,
|
assignee: null,
|
||||||
description: '',
|
description: '',
|
||||||
members: [],
|
members: [],
|
||||||
labels: [],
|
|
||||||
location: '',
|
location: '',
|
||||||
startDate: null,
|
startDate: null,
|
||||||
dueDate: null
|
dueDate: null
|
||||||
|
@ -13,8 +13,9 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
-->
|
-->
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Card, CardLabel } from '@anticrm/board'
|
import type { Card } from '@anticrm/board'
|
||||||
import { getClient } from '@anticrm/presentation'
|
import { createQuery, getClient } from '@anticrm/presentation'
|
||||||
|
import tags, { TagReference } from '@anticrm/tags'
|
||||||
import { Button, Icon, IconAdd } from '@anticrm/ui'
|
import { Button, Icon, IconAdd } from '@anticrm/ui'
|
||||||
import { invokeAction } from '@anticrm/view-resources'
|
import { invokeAction } from '@anticrm/view-resources'
|
||||||
|
|
||||||
@ -28,19 +29,16 @@
|
|||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
|
||||||
let labels: CardLabel[]
|
|
||||||
let labelsHandler: (e: Event) => void
|
let labelsHandler: (e: Event) => void
|
||||||
let isHovered: boolean = false
|
let isHovered: boolean = false
|
||||||
|
|
||||||
$: isCompact = $commonBoardPreference?.cardLabelsCompactMode
|
let labels: TagReference[] = []
|
||||||
|
const query = createQuery()
|
||||||
$: if (value.labels && value.labels.length > 0) {
|
$: query.query(tags.class.TagReference, { attachedTo: value._id }, (result) => {
|
||||||
client.findAll(board.class.CardLabel, { _id: { $in: value.labels } }).then((result) => {
|
labels = result
|
||||||
labels = isInline ? result.filter((l) => !l.isHidden) : result
|
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
labels = []
|
$: isCompact = $commonBoardPreference?.cardLabelsCompactMode
|
||||||
}
|
|
||||||
|
|
||||||
if (!isInline) {
|
if (!isInline) {
|
||||||
getCardActions(client, {
|
getCardActions(client, {
|
||||||
|
@ -1,81 +1,39 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
|
||||||
import { Board, CardLabel } from '@anticrm/board'
|
|
||||||
import { Ref } from '@anticrm/core'
|
|
||||||
import { getClient } from '@anticrm/presentation'
|
import { getClient } from '@anticrm/presentation'
|
||||||
import {
|
import { Label, Button, EditBox, Icon, IconBack, IconCheck, IconClose, hexColorToNumber } from '@anticrm/ui'
|
||||||
Label,
|
|
||||||
Button,
|
|
||||||
EditBox,
|
|
||||||
Icon,
|
|
||||||
IconBack,
|
|
||||||
IconCheck,
|
|
||||||
IconClose,
|
|
||||||
LinkWaterColor,
|
|
||||||
hexColorToNumber
|
|
||||||
} from '@anticrm/ui'
|
|
||||||
|
|
||||||
import board from '../../plugin'
|
import board from '../../plugin'
|
||||||
import { createCardLabel, getBoardAvailableColors } from '../../utils/BoardUtils'
|
import { createCardLabel, getBoardAvailableColors } from '../../utils/BoardUtils'
|
||||||
import ColorPresenter from '../presenters/ColorPresenter.svelte'
|
import ColorPresenter from '../presenters/ColorPresenter.svelte'
|
||||||
|
import { TagElement } from '@anticrm/tags'
|
||||||
|
|
||||||
export let object: CardLabel | undefined
|
export let object: TagElement | undefined
|
||||||
export let boardRef: Ref<Board>
|
|
||||||
export let onBack: () => void
|
export let onBack: () => void
|
||||||
|
|
||||||
let selected: {
|
let { title, color } = object ?? {}
|
||||||
color?: number
|
|
||||||
isHidden?: boolean
|
|
||||||
} = { color: object?.color }
|
|
||||||
|
|
||||||
let title = object?.title
|
|
||||||
const hiddenColor = hexColorToNumber(LinkWaterColor)
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
const colorGroups: number[][] = getBoardAvailableColors().reduce(
|
const colorGroups = (function chunk (colors: number[]): number[][] {
|
||||||
(result: number[][], currentValue: string) => {
|
return colors.length ? [colors.slice(0, 5), ...chunk(colors.slice(5))] : []
|
||||||
const last = result[result.length - 1]
|
})(getBoardAvailableColors().map(hexColorToNumber))
|
||||||
if (last.length >= 5) {
|
|
||||||
result.push([hexColorToNumber(currentValue)])
|
|
||||||
} else {
|
|
||||||
last.push(hexColorToNumber(currentValue))
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
},
|
|
||||||
[[]]
|
|
||||||
)
|
|
||||||
|
|
||||||
function selectColor (color: number, isHidden?: boolean) {
|
|
||||||
selected = { color, isHidden }
|
|
||||||
}
|
|
||||||
|
|
||||||
async function save () {
|
async function save () {
|
||||||
const { color, isHidden } = selected
|
if (!title || !color) {
|
||||||
if (!color) {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (object) {
|
||||||
if (object?._id) {
|
await client.update(object, { title, color })
|
||||||
await client.update(object, {
|
|
||||||
color,
|
|
||||||
title: title ?? '',
|
|
||||||
isHidden: isHidden ?? false
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
await createCardLabel(client, boardRef, color, title, isHidden)
|
await createCardLabel(client, { title, color })
|
||||||
}
|
}
|
||||||
|
|
||||||
onBack()
|
onBack()
|
||||||
}
|
}
|
||||||
|
|
||||||
async function remove () {
|
async function remove () {
|
||||||
if (!object?._id) {
|
if (!object) return
|
||||||
return
|
await client.remove(object)
|
||||||
}
|
|
||||||
|
|
||||||
await client.removeDoc(object._class, object.space, object._id)
|
|
||||||
|
|
||||||
onBack()
|
onBack()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
@ -118,10 +76,16 @@
|
|||||||
<div class="flex-col mt-1 mb-1 flex-gap-2">
|
<div class="flex-col mt-1 mb-1 flex-gap-2">
|
||||||
{#each colorGroups as colorGroup}
|
{#each colorGroups as colorGroup}
|
||||||
<div class="flex-row-stretch flex-gap-2">
|
<div class="flex-row-stretch flex-gap-2">
|
||||||
{#each colorGroup as color}
|
{#each colorGroup as c}
|
||||||
<div class="w-14">
|
<div class="w-14">
|
||||||
<ColorPresenter value={color} size="large" on:click={() => selectColor(color)}>
|
<ColorPresenter
|
||||||
{#if selected.color === color}
|
value={c}
|
||||||
|
size="large"
|
||||||
|
on:click={() => {
|
||||||
|
color = c
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{#if c === color}
|
||||||
<div class="flex-center flex-grow fs-title h-full">
|
<div class="flex-center flex-grow fs-title h-full">
|
||||||
<Icon icon={IconCheck} size="small" />
|
<Icon icon={IconCheck} size="small" />
|
||||||
</div>
|
</div>
|
||||||
@ -131,27 +95,12 @@
|
|||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
<div class="flex-row-stretch flex-gap-2">
|
|
||||||
<div class="w-14">
|
|
||||||
<ColorPresenter value={hiddenColor} size="large" on:click={() => selectColor(hiddenColor, true)}>
|
|
||||||
{#if selected.isHidden}
|
|
||||||
<div class="flex-center flex-grow fs-title h-full">
|
|
||||||
<Icon icon={IconCheck} size="small" />
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</ColorPresenter>
|
|
||||||
</div>
|
|
||||||
<div class="flex-col text-md">
|
|
||||||
<div class="fs-bold"><Label label={board.string.NoColor} /></div>
|
|
||||||
<div><Label label={board.string.NoColorInfo} /></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="ap-footer">
|
<div class="ap-footer">
|
||||||
{#if object?._id}
|
{#if object}
|
||||||
<Button size="small" kind="dangerous" label={board.string.Delete} on:click={remove} />
|
<Button size="small" kind="dangerous" label={board.string.Delete} on:click={remove} />
|
||||||
{/if}
|
{/if}
|
||||||
<Button label={board.string.Save} size="small" kind="primary" on:click={save} disabled={!selected.color} />
|
<Button label={board.string.Save} size="small" kind="primary" on:click={save} disabled={!color || !title} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Card, CardLabel } from '@anticrm/board'
|
import { Card } from '@anticrm/board'
|
||||||
import type { Ref } from '@anticrm/core'
|
import type { Ref } from '@anticrm/core'
|
||||||
import { getClient } from '@anticrm/presentation'
|
import tags, { TagElement, TagReference } from '@anticrm/tags'
|
||||||
|
import { createQuery, getClient } from '@anticrm/presentation'
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
EditBox,
|
EditBox,
|
||||||
@ -16,64 +17,44 @@
|
|||||||
import { createEventDispatcher } from 'svelte'
|
import { createEventDispatcher } from 'svelte'
|
||||||
|
|
||||||
import board from '../../plugin'
|
import board from '../../plugin'
|
||||||
import { getBoardLabels } from '../../utils/BoardUtils'
|
import { addCardLabel } from '../../utils/BoardUtils'
|
||||||
|
|
||||||
export let object: Card
|
export let object: Card
|
||||||
export let search: string | undefined = undefined
|
export let search: string = ''
|
||||||
export let onEdit: (label: CardLabel) => void
|
export let onEdit: (label: TagElement) => void
|
||||||
export let onCreate: () => void
|
export let onCreate: () => void
|
||||||
|
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
|
|
||||||
let boardCardLabels: CardLabel[] = []
|
let hovered: Ref<TagElement> | undefined = undefined
|
||||||
let filteredLabels: CardLabel[] = []
|
|
||||||
let hovered: Ref<CardLabel> | undefined = undefined
|
|
||||||
const dispatch = createEventDispatcher()
|
const dispatch = createEventDispatcher()
|
||||||
|
|
||||||
function applySearch () {
|
let labels: TagElement[] = []
|
||||||
if (!search || search.trim().length <= 0) {
|
const labelsQuery = createQuery()
|
||||||
filteredLabels = boardCardLabels
|
$: labelsQuery.query(
|
||||||
return
|
tags.class.TagElement,
|
||||||
|
{ title: { $like: '%' + search + '%' }, targetClass: board.class.Card },
|
||||||
|
(result) => {
|
||||||
|
labels = result
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
|
||||||
const text = search!.toUpperCase()
|
let cardLabels: TagReference[] = []
|
||||||
filteredLabels = boardCardLabels.filter((l) => l.title?.toUpperCase().includes(text) ?? false)
|
let cardLabelRefs: Ref<TagElement>[] = []
|
||||||
}
|
const cardLabelsQuery = createQuery()
|
||||||
|
$: cardLabelsQuery.query(tags.class.TagReference, { attachedTo: object._id }, (result) => {
|
||||||
async function fetchBoardLabels () {
|
cardLabels = result
|
||||||
if (object.space) {
|
cardLabelRefs = result.map(({ tag }) => tag)
|
||||||
boardCardLabels = await getBoardLabels(client, object.space)
|
|
||||||
applySearch()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function fetch () {
|
|
||||||
if (!object) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
object = (await client.findOne(object._class, { _id: object._id })) ?? object
|
|
||||||
}
|
|
||||||
|
|
||||||
async function toggle (label: CardLabel) {
|
|
||||||
if (!object) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (object?.labels?.includes(label._id)) {
|
|
||||||
await client.update(object, {
|
|
||||||
$pull: { labels: label._id as any } // TODO: fix as any
|
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
await client.update(object, {
|
|
||||||
$push: { labels: label._id }
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch()
|
async function toggle (label: TagElement) {
|
||||||
|
const cardLabel = cardLabels.find(({ tag }) => tag === label._id)
|
||||||
|
if (cardLabel) {
|
||||||
|
await client.remove(cardLabel)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
addCardLabel(client, object, label)
|
||||||
}
|
}
|
||||||
|
|
||||||
$: object.space && fetchBoardLabels()
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="antiPopup w-85 pb-2">
|
<div class="antiPopup w-85 pb-2">
|
||||||
@ -95,17 +76,12 @@
|
|||||||
<div class="ap-space bottom-divider" />
|
<div class="ap-space bottom-divider" />
|
||||||
<div class="flex-col ml-4 mt-2 mb-1 mr-2 flex-gap-1">
|
<div class="flex-col ml-4 mt-2 mb-1 mr-2 flex-gap-1">
|
||||||
<div class="p-2 mt-1 mb-1 border-bg-accent border-radius-1">
|
<div class="p-2 mt-1 mb-1 border-bg-accent border-radius-1">
|
||||||
<EditBox
|
<EditBox bind:value={search} maxWidth="100%" placeholder={board.string.SearchLabels} />
|
||||||
bind:value={search}
|
|
||||||
maxWidth="100%"
|
|
||||||
placeholder={board.string.SearchLabels}
|
|
||||||
on:change={() => applySearch()}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="text-md font-medium">
|
<div class="text-md font-medium">
|
||||||
<Label label={board.string.Labels} />
|
<Label label={board.string.Labels} />
|
||||||
</div>
|
</div>
|
||||||
{#each filteredLabels as label}
|
{#each labels as label}
|
||||||
<div
|
<div
|
||||||
class="flex-row-stretch"
|
class="flex-row-stretch"
|
||||||
on:mouseover={() => {
|
on:mouseover={() => {
|
||||||
@ -127,8 +103,8 @@
|
|||||||
style:box-shadow={hovered === label._id ? `-0.4rem 0 ${numberToRGB(label.color, 0.6)}` : ''}
|
style:box-shadow={hovered === label._id ? `-0.4rem 0 ${numberToRGB(label.color, 0.6)}` : ''}
|
||||||
on:click={() => toggle(label)}
|
on:click={() => toggle(label)}
|
||||||
>
|
>
|
||||||
{label.title ?? ''}
|
{label.title}
|
||||||
{#if object?.labels?.includes(label._id)}
|
{#if cardLabelRefs.includes(label._id)}
|
||||||
<div class="absolute flex-center h-full mr-2" style:top="0" style:right="0">
|
<div class="absolute flex-center h-full mr-2" style:top="0" style:right="0">
|
||||||
<Icon icon={IconCheck} size="small" />
|
<Icon icon={IconCheck} size="small" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Card, CardLabel } from '@anticrm/board'
|
import { Card } from '@anticrm/board'
|
||||||
|
import { TagElement } from '@anticrm/tags'
|
||||||
import CardLabelsEditor from './CardLabelsEditor.svelte'
|
import CardLabelsEditor from './CardLabelsEditor.svelte'
|
||||||
import CardLabelsPicker from './CardLabelsPicker.svelte'
|
import CardLabelsPicker from './CardLabelsPicker.svelte'
|
||||||
|
|
||||||
@ -7,22 +8,17 @@
|
|||||||
|
|
||||||
let editMode: {
|
let editMode: {
|
||||||
isEdit?: boolean
|
isEdit?: boolean
|
||||||
object?: CardLabel
|
object?: TagElement
|
||||||
} = {}
|
} = {}
|
||||||
let search: string | undefined = undefined
|
let search: string | undefined = undefined
|
||||||
|
|
||||||
function setEditMode (isEdit: boolean, object?: CardLabel) {
|
function setEditMode (isEdit: boolean, object?: TagElement) {
|
||||||
editMode = { isEdit, object }
|
editMode = { isEdit, object }
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if editMode.isEdit}
|
{#if editMode.isEdit}
|
||||||
<CardLabelsEditor
|
<CardLabelsEditor on:close object={editMode.object} onBack={() => setEditMode(false, undefined)} />
|
||||||
on:close
|
|
||||||
boardRef={value.space}
|
|
||||||
object={editMode.object}
|
|
||||||
onBack={() => setEditMode(false, undefined)}
|
|
||||||
/>
|
|
||||||
{:else}
|
{:else}
|
||||||
<CardLabelsPicker
|
<CardLabelsPicker
|
||||||
bind:search
|
bind:search
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
import RankSelect from '../selectors/RankSelect.svelte'
|
import RankSelect from '../selectors/RankSelect.svelte'
|
||||||
import { generateId, AttachedData } from '@anticrm/core'
|
import { generateId, AttachedData } from '@anticrm/core'
|
||||||
import task from '@anticrm/task'
|
import task from '@anticrm/task'
|
||||||
import { createMissingLabels } from '../../utils/BoardUtils'
|
|
||||||
|
|
||||||
export let value: Card
|
export let value: Card
|
||||||
const client = getClient()
|
const client = getClient()
|
||||||
@ -39,9 +38,6 @@
|
|||||||
|
|
||||||
const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true)
|
const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true)
|
||||||
|
|
||||||
const labels =
|
|
||||||
value.space !== selected.space ? await createMissingLabels(client, value, selected.space) : value.labels
|
|
||||||
|
|
||||||
const copy: AttachedData<Card> = {
|
const copy: AttachedData<Card> = {
|
||||||
state: selected.state,
|
state: selected.state,
|
||||||
doneState: null,
|
doneState: null,
|
||||||
@ -52,7 +48,7 @@
|
|||||||
description: '',
|
description: '',
|
||||||
members: [],
|
members: [],
|
||||||
location: '',
|
location: '',
|
||||||
labels: labels ?? [],
|
labels: value.labels,
|
||||||
startDate: value.startDate,
|
startDate: value.startDate,
|
||||||
dueDate: value.dueDate
|
dueDate: value.dueDate
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
import SpaceSelect from '../selectors/SpaceSelect.svelte'
|
import SpaceSelect from '../selectors/SpaceSelect.svelte'
|
||||||
import StateSelect from '../selectors/StateSelect.svelte'
|
import StateSelect from '../selectors/StateSelect.svelte'
|
||||||
import RankSelect from '../selectors/RankSelect.svelte'
|
import RankSelect from '../selectors/RankSelect.svelte'
|
||||||
import { createMissingLabels } from '../../utils/BoardUtils'
|
|
||||||
|
|
||||||
export let value: Card
|
export let value: Card
|
||||||
|
|
||||||
@ -28,7 +27,6 @@
|
|||||||
const update: DocumentUpdate<Card> = {}
|
const update: DocumentUpdate<Card> = {}
|
||||||
|
|
||||||
if (selected.space !== value.space) {
|
if (selected.space !== value.space) {
|
||||||
update.labels = await createMissingLabels(client, value, selected.space)
|
|
||||||
update.space = selected.space
|
update.space = selected.space
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { CardLabel } from '@anticrm/board'
|
import { TagReference } from '@anticrm/tags'
|
||||||
import ColorPresenter from './ColorPresenter.svelte'
|
import ColorPresenter from './ColorPresenter.svelte'
|
||||||
|
|
||||||
export let value: CardLabel
|
export let value: TagReference
|
||||||
export let isHovered: boolean = false
|
export let isHovered: boolean = false
|
||||||
export let size: 'tiny' | 'x-small' | 'small' | 'medium' | 'large' = 'medium'
|
export let size: 'tiny' | 'x-small' | 'small' | 'medium' | 'large' = 'medium'
|
||||||
</script>
|
</script>
|
||||||
@ -10,7 +10,7 @@
|
|||||||
{#if value}
|
{#if value}
|
||||||
<ColorPresenter value={value.color} {isHovered} {size} on:click>
|
<ColorPresenter value={value.color} {isHovered} {size} on:click>
|
||||||
{#if size !== 'tiny'}
|
{#if size !== 'tiny'}
|
||||||
<div class="flex-center h-full w-full fs-title text-sm pr-1 pl-1">{value.title ?? ''}</div>
|
<div class="flex-center h-full w-full fs-title text-sm pr-1 pl-1">{value.title}</div>
|
||||||
{/if}
|
{/if}
|
||||||
</ColorPresenter>
|
</ColorPresenter>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
import { readable } from 'svelte/store'
|
import { readable } from 'svelte/store'
|
||||||
import board, { Board, CardLabel, Card, CommonBoardPreference } from '@anticrm/board'
|
import board, { Board, Card, CommonBoardPreference } from '@anticrm/board'
|
||||||
import core, { Ref, TxOperations, Space } from '@anticrm/core'
|
import core, { Ref, TxOperations } from '@anticrm/core'
|
||||||
import type { KanbanTemplate, TodoItem } from '@anticrm/task'
|
import type { KanbanTemplate, TodoItem } from '@anticrm/task'
|
||||||
import preference from '@anticrm/preference'
|
import preference from '@anticrm/preference'
|
||||||
|
import tags, { TagElement } from '@anticrm/tags'
|
||||||
import { createKanban } from '@anticrm/task'
|
import { createKanban } from '@anticrm/task'
|
||||||
import { createQuery, getClient } from '@anticrm/presentation'
|
import { createQuery, getClient } from '@anticrm/presentation'
|
||||||
import {
|
import {
|
||||||
hexColorToNumber,
|
|
||||||
FernColor,
|
FernColor,
|
||||||
FlamingoColor,
|
FlamingoColor,
|
||||||
MalibuColor,
|
MalibuColor,
|
||||||
@ -34,14 +34,10 @@ export async function createBoard (
|
|||||||
members: []
|
members: []
|
||||||
})
|
})
|
||||||
|
|
||||||
await Promise.all([createBoardLabels(client, boardRef), createKanban(client, boardRef, templateId)])
|
await Promise.all([createKanban(client, boardRef, templateId)])
|
||||||
return boardRef
|
return boardRef
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getBoardLabels (client: TxOperations, boardRef: Ref<Board>): Promise<CardLabel[]> {
|
|
||||||
return await client.findAll(board.class.CardLabel, { attachedTo: boardRef })
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getBoardAvailableColors (): string[] {
|
export function getBoardAvailableColors (): string[] {
|
||||||
return [
|
return [
|
||||||
FernColor,
|
FernColor,
|
||||||
@ -57,71 +53,26 @@ export function getBoardAvailableColors (): string[] {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createBoardLabels (client: TxOperations, boardRef: Ref<Board>): Promise<void> {
|
|
||||||
await Promise.all([
|
|
||||||
createCardLabel(client, boardRef, hexColorToNumber(FernColor)),
|
|
||||||
createCardLabel(client, boardRef, hexColorToNumber(SeaBuckthornColor)),
|
|
||||||
createCardLabel(client, boardRef, hexColorToNumber(FlamingoColor)),
|
|
||||||
createCardLabel(client, boardRef, hexColorToNumber(MalibuColor)),
|
|
||||||
createCardLabel(client, boardRef, hexColorToNumber(MoodyBlueColor))
|
|
||||||
])
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function createCardLabel (
|
export async function createCardLabel (
|
||||||
client: TxOperations,
|
client: TxOperations,
|
||||||
boardRef: Ref<Board>,
|
{ title, color }: { title: string, color: number }
|
||||||
color: number,
|
|
||||||
title?: string,
|
|
||||||
isHidden?: boolean
|
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
await client.createDoc(board.class.CardLabel, core.space.Model, {
|
await client.createDoc(tags.class.TagElement, tags.space.Tags, {
|
||||||
attachedTo: boardRef,
|
title,
|
||||||
attachedToClass: board.class.Board,
|
|
||||||
collection: 'labels',
|
|
||||||
color,
|
color,
|
||||||
title: title ?? '',
|
targetClass: board.class.Card,
|
||||||
isHidden: isHidden ?? false
|
description: '',
|
||||||
|
category: board.category.Other
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const isEqualLabel = (l1: CardLabel, l2: CardLabel): boolean =>
|
export async function addCardLabel (client: TxOperations, card: Card, label: TagElement): Promise<void> {
|
||||||
l1.title === l2.title && l1.color === l2.color && (l1.isHidden ?? false) === (l2.isHidden ?? false)
|
const { title, color, _id: tag } = label
|
||||||
|
await client.addCollection(tags.class.TagReference, card.space, card._id, card._class, 'labels', {
|
||||||
export async function createMissingLabels (
|
title,
|
||||||
client: TxOperations,
|
color,
|
||||||
object: Card,
|
tag
|
||||||
targetBoard: Ref<Space>
|
|
||||||
): Promise<Array<Ref<CardLabel>> | undefined> {
|
|
||||||
const sourceBoardLabels = await getBoardLabels(client, object.space)
|
|
||||||
const targetBoardLabels = await getBoardLabels(client, targetBoard)
|
|
||||||
|
|
||||||
const missingLabels = sourceBoardLabels.filter((srcLabel) => {
|
|
||||||
if (!object.labels?.includes(srcLabel._id)) return false
|
|
||||||
|
|
||||||
return targetBoardLabels.findIndex((targetLabel) => isEqualLabel(targetLabel, srcLabel)) === -1
|
|
||||||
})
|
})
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
missingLabels.map(async (l) => await createCardLabel(client, targetBoard, l.color, l.title, l.isHidden))
|
|
||||||
)
|
|
||||||
|
|
||||||
const updatedTargetBoardLabels = await getBoardLabels(client, targetBoard)
|
|
||||||
|
|
||||||
const labelsUpdate = object.labels
|
|
||||||
?.map((srcLabelId) => {
|
|
||||||
const srcLabel = sourceBoardLabels.find((l) => l._id === srcLabelId)
|
|
||||||
|
|
||||||
if (srcLabel === undefined) return null
|
|
||||||
|
|
||||||
const targetLabel = updatedTargetBoardLabels.find((l) => isEqualLabel(l, srcLabel))
|
|
||||||
|
|
||||||
if (targetLabel === undefined) return null
|
|
||||||
|
|
||||||
return targetLabel._id
|
|
||||||
})
|
|
||||||
.filter((l) => l !== null) as Array<Ref<CardLabel>> | undefined
|
|
||||||
|
|
||||||
return labelsUpdate
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDateIcon (item: TodoItem): 'normal' | 'warning' | 'overdue' {
|
export function getDateIcon (item: TodoItem): 'normal' | 'warning' | 'overdue' {
|
||||||
|
@ -37,7 +37,6 @@ export async function createCard (
|
|||||||
rank: calcRank(lastOne, undefined),
|
rank: calcRank(lastOne, undefined),
|
||||||
assignee: null,
|
assignee: null,
|
||||||
description: '',
|
description: '',
|
||||||
labels: [],
|
|
||||||
...attribues
|
...attribues
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@
|
|||||||
"@anticrm/view": "~0.6.0",
|
"@anticrm/view": "~0.6.0",
|
||||||
"@anticrm/task": "~0.6.0",
|
"@anticrm/task": "~0.6.0",
|
||||||
"@anticrm/ui": "~0.6.0",
|
"@anticrm/ui": "~0.6.0",
|
||||||
"@anticrm/preference": "~0.6.0"
|
"@anticrm/preference": "~0.6.0",
|
||||||
|
"@anticrm/tags": "~0.6.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,13 +15,14 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import { Employee } from '@anticrm/contact'
|
import { Employee } from '@anticrm/contact'
|
||||||
import type { AttachedDoc, Class, Doc, Markup, Ref, Type } from '@anticrm/core'
|
import type { Class, Doc, Markup, Ref, Type } from '@anticrm/core'
|
||||||
import type { Asset, IntlString, Plugin } from '@anticrm/platform'
|
import type { Asset, IntlString, Plugin } from '@anticrm/platform'
|
||||||
import { plugin } from '@anticrm/platform'
|
import { plugin } from '@anticrm/platform'
|
||||||
import type { Preference } from '@anticrm/preference'
|
import type { Preference } from '@anticrm/preference'
|
||||||
import type { DoneState, KanbanTemplateSpace, SpaceWithStates, Task } from '@anticrm/task'
|
import type { DoneState, KanbanTemplateSpace, SpaceWithStates, Task } from '@anticrm/task'
|
||||||
import type { AnyComponent } from '@anticrm/ui'
|
import type { AnyComponent } from '@anticrm/ui'
|
||||||
import { Action, ActionCategory } from '@anticrm/view'
|
import { Action, ActionCategory } from '@anticrm/view'
|
||||||
|
import { TagCategory } from '@anticrm/tags'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -40,15 +41,6 @@ export interface BoardView extends SpaceWithStates {
|
|||||||
boards: Ref<Board>[]
|
boards: Ref<Board>[]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export interface CardLabel extends AttachedDoc {
|
|
||||||
title: string
|
|
||||||
color: number
|
|
||||||
isHidden?: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
@ -68,8 +60,6 @@ export interface Card extends Task {
|
|||||||
|
|
||||||
members?: Ref<Employee>[]
|
members?: Ref<Employee>[]
|
||||||
|
|
||||||
labels: Ref<CardLabel>[]
|
|
||||||
|
|
||||||
location?: string
|
location?: string
|
||||||
|
|
||||||
cover?: CardCover | null
|
cover?: CardCover | null
|
||||||
@ -108,13 +98,13 @@ const boards = plugin(boardId, {
|
|||||||
class: {
|
class: {
|
||||||
Board: '' as Ref<Class<Board>>,
|
Board: '' as Ref<Class<Board>>,
|
||||||
Card: '' as Ref<Class<Card>>,
|
Card: '' as Ref<Class<Card>>,
|
||||||
CardLabel: '' as Ref<Class<CardLabel>>,
|
|
||||||
MenuPage: '' as Ref<Class<MenuPage>>,
|
MenuPage: '' as Ref<Class<MenuPage>>,
|
||||||
CommonBoardPreference: '' as Ref<Class<CommonBoardPreference>>,
|
CommonBoardPreference: '' as Ref<Class<CommonBoardPreference>>,
|
||||||
CardCover: '' as Ref<Class<Type<CardCover>>>
|
CardCover: '' as Ref<Class<Type<CardCover>>>
|
||||||
},
|
},
|
||||||
category: {
|
category: {
|
||||||
Card: '' as Ref<ActionCategory>
|
Card: '' as Ref<ActionCategory>,
|
||||||
|
Other: '' as Ref<TagCategory>
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
Completed: '' as Ref<DoneState>
|
Completed: '' as Ref<DoneState>
|
||||||
|
@ -84,6 +84,7 @@ export interface Task extends AttachedDoc, DocWithRank {
|
|||||||
dueDate: Timestamp | null
|
dueDate: Timestamp | null
|
||||||
startDate: Timestamp | null
|
startDate: Timestamp | null
|
||||||
todoItems?: number
|
todoItems?: number
|
||||||
|
labels?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -116,7 +117,6 @@ export interface Issue extends Task {
|
|||||||
|
|
||||||
comments?: number
|
comments?: number
|
||||||
attachments?: number
|
attachments?: number
|
||||||
labels?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,48 +13,5 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import type { Tx, TxCreateDoc, TxRemoveDoc } from '@anticrm/core'
|
|
||||||
import type { TriggerControl } from '@anticrm/server-core'
|
|
||||||
import type { Card, CardLabel } from '@anticrm/board'
|
|
||||||
import board from '@anticrm/board'
|
|
||||||
import core, { TxProcessor } from '@anticrm/core'
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @public
|
|
||||||
*/
|
|
||||||
export async function OnLabelDelete (tx: Tx, { findAll, hierarchy, txFactory }: TriggerControl): Promise<Tx[]> {
|
|
||||||
if (tx._class !== core.class.TxRemoveDoc) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const rmTx = tx as TxRemoveDoc<CardLabel>
|
|
||||||
if (!hierarchy.isDerived(rmTx.objectClass, board.class.CardLabel)) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const createTx = (
|
|
||||||
await findAll(
|
|
||||||
core.class.TxCreateDoc,
|
|
||||||
{
|
|
||||||
objectId: rmTx.objectId
|
|
||||||
},
|
|
||||||
{ limit: 1 }
|
|
||||||
)
|
|
||||||
)[0]
|
|
||||||
if (createTx === undefined) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
|
|
||||||
const label = TxProcessor.createDoc2Doc(createTx as TxCreateDoc<CardLabel>)
|
|
||||||
const cards = await findAll<Card>(board.class.Card, { space: label.attachedTo as any, labels: label._id })
|
|
||||||
return cards.map((card) =>
|
|
||||||
txFactory.createTxUpdateDoc<Card>(card._class, card.space, card._id, { $pull: { labels: label._id as any } })
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||||
export default async () => ({
|
export default async () => ({})
|
||||||
trigger: {
|
|
||||||
OnLabelDelete
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
@ -13,9 +13,8 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
|
|
||||||
import type { Resource, Plugin } from '@anticrm/platform'
|
import type { Plugin } from '@anticrm/platform'
|
||||||
import { plugin } from '@anticrm/platform'
|
import { plugin } from '@anticrm/platform'
|
||||||
import type { TriggerFunc } from '@anticrm/server-core'
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
@ -25,8 +24,4 @@ export const serverBoardId = 'server-board' as Plugin
|
|||||||
/**
|
/**
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export default plugin(serverBoardId, {
|
export default plugin(serverBoardId, {})
|
||||||
trigger: {
|
|
||||||
OnLabelDelete: '' as Resource<TriggerFunc>
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
Loading…
Reference in New Issue
Block a user