From 0e110854073331dfd7b2b9318bf7e3feb24c3941 Mon Sep 17 00:00:00 2001 From: Alex <41288429+Dvinyanin@users.noreply.github.com> Date: Sat, 21 May 2022 18:20:34 +0700 Subject: [PATCH] Board: Add convert checklist to card action (#1805) --- models/board/src/index.ts | 18 +++++++ models/board/src/plugin.ts | 11 +++- plugins/board-assets/lang/en.json | 3 +- plugins/board-assets/lang/ru.json | 3 +- .../components/editor/CardChecklist.svelte | 2 +- .../presenters/ChecklistsPresenter.svelte | 3 +- plugins/board-resources/src/index.ts | 23 ++++++++ .../board-resources/src/utils/CardUtils.ts | 52 ++++++++++++++++++- 8 files changed, 107 insertions(+), 8 deletions(-) diff --git a/models/board/src/index.ts b/models/board/src/index.ts index 469285deae..691e266da4 100644 --- a/models/board/src/index.ts +++ b/models/board/src/index.ts @@ -420,6 +420,24 @@ export function createModel (builder: Builder): void { builder.mixin(board.class.Card, core.class.Class, view.mixin.IgnoreActions, { actions: [view.action.Delete, task.action.Move] }) + + // TODO: update query when nested query is available + createAction( + builder, + { + action: board.actionImpl.ConvertToCard, + label: board.string.ConvertToCard, + icon: board.icon.Card, + category: board.category.Card, + query: { + attachedToClass: task.class.TodoItem + }, + input: 'any', + target: task.class.TodoItem, + context: { mode: ['context', 'browser'] } + }, + board.action.ConvertToCard + ) } export { boardOperation } from './migration' diff --git a/models/board/src/plugin.ts b/models/board/src/plugin.ts index b2e1d43d8b..b43e9c55bc 100644 --- a/models/board/src/plugin.ts +++ b/models/board/src/plugin.ts @@ -20,7 +20,7 @@ import type { Ref, Space } from '@anticrm/core' import { IntlString, mergeIds } from '@anticrm/platform' import { KanbanTemplate, Sequence } from '@anticrm/task' import type { AnyComponent } from '@anticrm/ui' -import { ViewletDescriptor } from '@anticrm/view' +import { Action, ViewAction, ViewletDescriptor } from '@anticrm/view' export default mergeIds(boardId, board, { component: { @@ -55,6 +55,13 @@ export default mergeIds(boardId, board, { Table: '' as Ref }, string: { - CommonBoardPreference: '' as IntlString + CommonBoardPreference: '' as IntlString, + ConvertToCard: '' as IntlString + }, + action: { + ConvertToCard: '' as Ref + }, + actionImpl: { + ConvertToCard: '' as ViewAction } }) diff --git a/plugins/board-assets/lang/en.json b/plugins/board-assets/lang/en.json index a5deca3637..cc13fb0ae9 100644 --- a/plugins/board-assets/lang/en.json +++ b/plugins/board-assets/lang/en.json @@ -106,6 +106,7 @@ "Size": "Size", "RemoveCover": "Remove cover", "DeleteChecklist": "Delete checklist", - "DeleteChecklistConfirm": "Deleting a checklist is permanent and there is no way to get it back." + "DeleteChecklistConfirm": "Deleting a checklist is permanent and there is no way to get it back.", + "ConvertToCard": "Convert to card" } } diff --git a/plugins/board-assets/lang/ru.json b/plugins/board-assets/lang/ru.json index ae8148b410..9e696c9aea 100644 --- a/plugins/board-assets/lang/ru.json +++ b/plugins/board-assets/lang/ru.json @@ -106,6 +106,7 @@ "Size": "Размер", "RemoveCover": "Удалить обложку", "DeleteChecklist": "Удалить список задач", - "DeleteChecklistConfirm": "Удаление списка задач необратимо, и не будет возможности его вернуть." + "DeleteChecklistConfirm": "Удаление списка задач необратимо, и не будет возможности его вернуть.", + "ConvertToCard": "Конвертировать в карточку" } } diff --git a/plugins/board-resources/src/components/editor/CardChecklist.svelte b/plugins/board-resources/src/components/editor/CardChecklist.svelte index 44e456d0c4..541371f621 100644 --- a/plugins/board-resources/src/components/editor/CardChecklist.svelte +++ b/plugins/board-resources/src/components/editor/CardChecklist.svelte @@ -117,7 +117,7 @@ } function showItemMenu (item: TodoItem, e?: Event) { - showPopup(ContextMenu, { object: item, baseMenuClass: board.class.Card }, getPopupAlignment(e)) + showPopup(ContextMenu, { object: item }, getPopupAlignment(e)) } $: checklistItemsQuery.query(task.class.TodoItem, { space: value.space, attachedTo: value._id }, (result) => { diff --git a/plugins/board-resources/src/components/presenters/ChecklistsPresenter.svelte b/plugins/board-resources/src/components/presenters/ChecklistsPresenter.svelte index 19de62bbe6..218d367acc 100644 --- a/plugins/board-resources/src/components/presenters/ChecklistsPresenter.svelte +++ b/plugins/board-resources/src/components/presenters/ChecklistsPresenter.svelte @@ -10,7 +10,7 @@ export let size: 'small' | 'medium' | 'large' = 'small' const todoListQuery = createQuery() - let todoLists: Ref[] + let todoLists: Ref[] = [] $: todoListQuery.query(task.class.TodoItem, { space: value.space, attachedTo: value._id }, (result) => { todoLists = result.map(({ _id }) => _id) }) @@ -19,6 +19,7 @@ $: query.query(task.class.TodoItem, { space: value.space, attachedTo: { $in: todoLists } }, (result) => { total = result.total done = result.filter((t) => t.done).length + if (!total) return item = result.reduce((min, cur) => cur.dueTo === null ? min : min.dueTo === null || cur.dueTo < min.dueTo ? cur : min ) diff --git a/plugins/board-resources/src/index.ts b/plugins/board-resources/src/index.ts index b75bdd633a..cc7564f71e 100644 --- a/plugins/board-resources/src/index.ts +++ b/plugins/board-resources/src/index.ts @@ -14,6 +14,9 @@ // limitations under the License. // import { Resources } from '@anticrm/platform' +import { TodoItem } from '@anticrm/task' +import { getClient } from '@anticrm/presentation' +import board from '@anticrm/board' import BoardPresenter from './components/BoardPresenter.svelte' import CardPresenter from './components/CardPresenter.svelte' @@ -37,6 +40,23 @@ import TableView from './components/TableView.svelte' import UserBoxList from './components/UserBoxList.svelte' import CardLabels from './components/editor/CardLabels.svelte' import CardCoverEditor from './components/popups/CardCoverEditor.svelte' +import { createCard, getCardFromTodoItem } from './utils/CardUtils' + +async function ConvertToCard (object: TodoItem): Promise { + const client = getClient() + const todoItemCard = await getCardFromTodoItem(client, object) + if (todoItemCard === undefined) return + const date = + object.dueTo === null + ? {} + : { date: { _class: board.class.CardDate, dueDate: object.dueTo, isChecked: object.done } } + await createCard(client, todoItemCard.space, todoItemCard.state, { + title: object.name, + assignee: object.assignee, + ...date + }) + await client.remove(object) +} export default async (): Promise => ({ component: { @@ -63,5 +83,8 @@ export default async (): Promise => ({ CoverActionPopup: CardCoverEditor, MoveActionPopup: MoveCard, CopyActionPopup: CopyCard + }, + actionImpl: { + ConvertToCard } }) diff --git a/plugins/board-resources/src/utils/CardUtils.ts b/plugins/board-resources/src/utils/CardUtils.ts index 775f962cd5..80f546a570 100644 --- a/plugins/board-resources/src/utils/CardUtils.ts +++ b/plugins/board-resources/src/utils/CardUtils.ts @@ -1,10 +1,58 @@ import { Card } from '@anticrm/board' import { Employee, EmployeeAccount } from '@anticrm/contact' -import { TxOperations as Client, TxResult, getCurrentAccount, Ref } from '@anticrm/core' +import { + TxOperations as Client, + TxResult, + getCurrentAccount, + Ref, + Space, + AttachedData, + SortingOrder +} from '@anticrm/core' import { showPanel } from '@anticrm/ui' - +import task, { calcRank, State, TodoItem } from '@anticrm/task' import board from '../plugin' +export async function createCard ( + client: Client, + space: Ref, + state: Ref, + attribues: Partial> +): Promise> { + const sequence = await client.findOne(task.class.Sequence, { attachedTo: board.class.Card }) + if (sequence === undefined) { + throw new Error('sequence object not found') + } + + const lastOne = await client.findOne(board.class.Card, { state }, { sort: { rank: SortingOrder.Descending } }) + const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true) + + const value: AttachedData = { + title: '', + state, + doneState: null, + number: (incResult as any).object.sequence, + rank: calcRank(lastOne, undefined), + assignee: null, + description: '', + labels: [], + ...attribues + } + + return await client.addCollection(board.class.Card, space, space, board.class.Board, 'cards', value) +} + +export async function getCardFromTodoItem (client: Client, todoItem: TodoItem | undefined): Promise { + if (todoItem === undefined) return + if (todoItem.attachedToClass === todoItem._class) { + return await getCardFromTodoItem( + client, + await client.findOne(todoItem._class, { _id: todoItem.attachedTo as Ref }) + ) + } + return await client.findOne(board.class.Card, { _id: todoItem.attachedTo as Ref }) +} + export function updateCard (client: Client, card: Card, field: string, value: any): Promise | undefined { if (card === undefined) { return