diff --git a/models/board/src/index.ts b/models/board/src/index.ts index 46164b8056..ca95ebf9f4 100644 --- a/models/board/src/index.ts +++ b/models/board/src/index.ts @@ -206,6 +206,10 @@ export function createModel (builder: Builder): void { presenter: board.component.CardPresenter }) + builder.mixin(board.class.Board, core.class.Class, view.mixin.AttributePresenter, { + presenter: board.component.BoardPresenter + }) + builder.createDoc( task.class.KanbanTemplateSpace, core.space.Model, diff --git a/plugins/board-assets/lang/en.json b/plugins/board-assets/lang/en.json index c4de339f2d..aa362b075d 100644 --- a/plugins/board-assets/lang/en.json +++ b/plugins/board-assets/lang/en.json @@ -56,15 +56,20 @@ "AddACard": "Add a card", "AddCard": "Add card", "CardTitlePlaceholder": "Enter a title for this card...", + "MoveCard": "Move card", + "SelectDestination": "Select destination", "Create": "Create", "CreateDescription": "If you want, we can create a card for every new line ({number}). You can also create one card with a long title.", "CreateSingle": "Just one card", "CreateMultiple": "Create {number} cards", + "Cancel": "Cancel", + "List": "List", + "Position": "Position", + "Current": "{label} (current)", "StartDate": "Start date", "DueDate": "Due date", "Save": "Save", "Remove": "Remove", - "Cancel": "Cancel", "NullDate": "M/D/YYYY" } -} \ No newline at end of file +} diff --git a/plugins/board-assets/lang/ru.json b/plugins/board-assets/lang/ru.json index c9e2afe69b..e10ae8403d 100644 --- a/plugins/board-assets/lang/ru.json +++ b/plugins/board-assets/lang/ru.json @@ -60,11 +60,16 @@ "CreateDescription": "Можно создать отдельные карточки для каждой строки ({number}) или одну с длинным названием.", "CreateSingle": "Создать одну", "CreateMultiple": "Создать несколько ({number})", + "MoveCard": "Переместить карточку", + "SelectDestination": "Выберите список", + "Cancel": "Отмена", + "List": "Список", + "Position": "Позиция", + "Current": "{label} (текущий)", "StartDate": "Начало", "DueDate": "Срок", "Save": "Сохранить", "Remove": "Удалить", - "Cancel": "Закрыть", "NullDate": "М/Д/ГГГГ" } -} \ No newline at end of file +} diff --git a/plugins/board-resources/src/components/BoardPresenter.svelte b/plugins/board-resources/src/components/BoardPresenter.svelte new file mode 100644 index 0000000000..b8639830b6 --- /dev/null +++ b/plugins/board-resources/src/components/BoardPresenter.svelte @@ -0,0 +1,37 @@ + + + +{#if value} + +
+ +
+ {value.name} +
+{/if} diff --git a/plugins/board-resources/src/components/popups/MoveCard.svelte b/plugins/board-resources/src/components/popups/MoveCard.svelte new file mode 100644 index 0000000000..102c391b1f --- /dev/null +++ b/plugins/board-resources/src/components/popups/MoveCard.svelte @@ -0,0 +1,100 @@ + + +
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+ {#key selected.space} + + {/key} +
+
+ {#key selected.state} + + {/key} +
+
+ +
diff --git a/plugins/board-resources/src/components/selectors/RankSelect.svelte b/plugins/board-resources/src/components/selectors/RankSelect.svelte new file mode 100644 index 0000000000..9ba8048690 --- /dev/null +++ b/plugins/board-resources/src/components/selectors/RankSelect.svelte @@ -0,0 +1,41 @@ + + + diff --git a/plugins/board-resources/src/components/selectors/SpaceSelect.svelte b/plugins/board-resources/src/components/selectors/SpaceSelect.svelte new file mode 100644 index 0000000000..a928d36ea3 --- /dev/null +++ b/plugins/board-resources/src/components/selectors/SpaceSelect.svelte @@ -0,0 +1,26 @@ + + + diff --git a/plugins/board-resources/src/components/selectors/StateSelect.svelte b/plugins/board-resources/src/components/selectors/StateSelect.svelte new file mode 100644 index 0000000000..eed616d812 --- /dev/null +++ b/plugins/board-resources/src/components/selectors/StateSelect.svelte @@ -0,0 +1,34 @@ + + + diff --git a/plugins/board-resources/src/index.ts b/plugins/board-resources/src/index.ts index 33d0f75133..e0b2038c59 100644 --- a/plugins/board-resources/src/index.ts +++ b/plugins/board-resources/src/index.ts @@ -14,19 +14,25 @@ // limitations under the License. // -import { Resources } from '@anticrm/platform' import { showPopup } from '@anticrm/ui' import { Card } from '@anticrm/board' +import { Resources } from '@anticrm/platform' import CardPresenter from './components/CardPresenter.svelte' +import BoardPresenter from './components/BoardPresenter.svelte' import CreateBoard from './components/CreateBoard.svelte' import CreateCard from './components/CreateCard.svelte' import EditCard from './components/EditCard.svelte' import KanbanCard from './components/KanbanCard.svelte' import TemplatesIcon from './components/TemplatesIcon.svelte' import KanbanView from './components/KanbanView.svelte' +import MoveView from './components/popups/MoveCard.svelte' import DateRangePicker from './components/popups/DateRangePicker.svelte' import { addCurrentUser, canAddCurrentUser, isArchived, isUnarchived } from './utils/CardUtils' +async function showMoveCardPopup (object: Card): Promise { + showPopup(MoveView, { object }) +} + async function showDatePickerPopup (object: Card): Promise { showPopup(DateRangePicker, { object }) } @@ -38,10 +44,12 @@ export default async (): Promise => ({ KanbanCard, CardPresenter, TemplatesIcon, - KanbanView + KanbanView, + BoardPresenter }, cardActionHandler: { Join: addCurrentUser, + Move: showMoveCardPopup, Dates: showDatePickerPopup }, cardActionSupportedHandler: { diff --git a/plugins/board-resources/src/plugin.ts b/plugins/board-resources/src/plugin.ts index bae20af484..4e62546a3c 100644 --- a/plugins/board-resources/src/plugin.ts +++ b/plugins/board-resources/src/plugin.ts @@ -81,16 +81,22 @@ export default mergeIds(boardId, board, { CreateDescription: '' as IntlString, CreateSingle: '' as IntlString, CreateMultiple: '' as IntlString, + MoveCard: '' as IntlString, + Cancel: '' as IntlString, + SelectDestination: '' as IntlString, + List: '' as IntlString, + Position: '' as IntlString, + Current: '' as IntlString, StartDate: '' as IntlString, DueDate: '' as IntlString, Save: '' as IntlString, Remove: '' as IntlString, - Cancel: '' as IntlString, NullDate: '' as IntlString }, component: { CreateCustomer: '' as AnyComponent, CardsPresenter: '' as AnyComponent, + BoardPresenter: '' as AnyComponent, Boards: '' as AnyComponent, EditCard: '' as AnyComponent, Members: '' as AnyComponent, diff --git a/server/core/src/storage.ts b/server/core/src/storage.ts index e486faa4a2..d0c50f1c69 100644 --- a/server/core/src/storage.ts +++ b/server/core/src/storage.ts @@ -337,6 +337,25 @@ class TServerStorage implements ServerStorage { return result } + async processMove (ctx: MeasureContext, tx: Tx): Promise { + const actualTx = this.extractTx(tx) + if (!this.hierarchy.isDerived(actualTx._class, core.class.TxUpdateDoc)) return [] + const rtx = actualTx as TxUpdateDoc + if (rtx.operations.space === undefined || rtx.operations.space === rtx.objectSpace) return [] + const result: Tx[] = [] + const factory = new TxFactory(core.account.System) + for (const [, attribute] of this.hierarchy.getAllAttributes(rtx.objectClass)) { + if (!this.hierarchy.isDerived(attribute.type._class, core.class.Collection)) continue + const collection = attribute.type as Collection + const allAttached = await this.findAll(ctx, collection.of, { attachedTo: rtx.objectId, space: rtx.objectSpace }) + const allTx = allAttached.map(({ _class, space, _id }) => + factory.createTxUpdateDoc(_class, space, _id, { space: rtx.operations.space }) + ) + result.push(...allTx) + } + return result + } + async tx (ctx: MeasureContext, tx: Tx): Promise<[TxResult, Tx[]]> { // store tx const _class = txClass(tx) @@ -367,6 +386,7 @@ class TServerStorage implements ServerStorage { derived = [ ...(await ctx.with('process-collection', { _class }, () => this.processCollection(ctx, tx))), ...(await ctx.with('process-remove', { _class }, () => this.processRemove(ctx, tx))), + ...(await ctx.with('process-move', { _class }, () => this.processMove(ctx, tx))), ...(await ctx.with('process-triggers', {}, (ctx) => this.triggers.apply(tx.modifiedBy, tx, { fx: triggerFx.fx,