diff --git a/packages/kanban/src/components/Kanban.svelte b/packages/kanban/src/components/Kanban.svelte index a79a6b2b46..03b2506dc1 100644 --- a/packages/kanban/src/components/Kanban.svelte +++ b/packages/kanban/src/components/Kanban.svelte @@ -36,6 +36,7 @@ export let dontUpdateRank: boolean = false export let getUpdateProps: (doc: Doc, state: CategoryType) => DocumentUpdate | undefined + export let getAvailableCategories: ((doc: Doc) => Promise) | undefined = undefined const dispatch = createEventDispatcher() @@ -43,6 +44,13 @@ if (dragCard === undefined) { return } + + const canDrop = !dragCardAvailableCategories || dragCardAvailableCategories.includes(state) + + if (!canDrop) { + return + } + let updates = getUpdateProps(dragCard, state) if (updates === undefined) { @@ -61,6 +69,7 @@ await client.diffUpdate(dragCard, updates) } dragCard = undefined + dragCardAvailableCategories = undefined } const client = getClient() @@ -70,6 +79,7 @@ let dragCardInitialState: CategoryType let dragCardInitialPosition: number | undefined let dragCardState: CategoryType | undefined + let dragCardAvailableCategories: CategoryType[] | undefined let isDragging = false @@ -84,6 +94,12 @@ function panelDragOver (event: Event | undefined, state: CategoryType): void { event?.preventDefault() if (dragCard !== undefined && dragCardState !== state) { + const canDrop = !dragCardAvailableCategories || dragCardAvailableCategories.includes(state) + + if (!canDrop) { + return + } + const updates = getUpdateProps(dragCard, state) if (updates === undefined) { return @@ -169,7 +185,7 @@ } isDragging = false } - function onDragStart (object: Item, state: CategoryType): void { + async function onDragStart (object: Item, state: CategoryType) { dragCardInitialState = state dragCardState = state dragCardInitialRank = object.rank @@ -177,6 +193,7 @@ dragCardInitialPosition = items.findIndex((p) => p._id === object._id) dragCard = object isDragging = true + dragCardAvailableCategories = await getAvailableCategories?.(object) dispatch('obj-focus', object) } // eslint-disable-next-line diff --git a/plugins/tracker-resources/src/components/issues/KanbanView.svelte b/plugins/tracker-resources/src/components/issues/KanbanView.svelte index 02a8155425..e3b7c42dc4 100644 --- a/plugins/tracker-resources/src/components/issues/KanbanView.svelte +++ b/plugins/tracker-resources/src/components/issues/KanbanView.svelte @@ -66,7 +66,8 @@ noCategory, openDoc, SelectDirection, - setGroupByValues + setGroupByValues, + statusStore } from '@hcengineering/view-resources' import view from '@hcengineering/view-resources/src/plugin' import { onMount } from 'svelte' @@ -82,6 +83,8 @@ import PriorityEditor from './PriorityEditor.svelte' import StatusEditor from './StatusEditor.svelte' import EstimationEditor from './timereport/EstimationEditor.svelte' + import { getStates } from '@hcengineering/task' + import { typeStore } from '@hcengineering/task-resources' export let space: Ref | undefined = undefined export let baseMenuClass: Ref> | undefined = undefined @@ -264,6 +267,39 @@ if (enabledConfig(config, 'attachments') && (issue.attachments ?? 0) > 0) return true return false } + + const getAvailableCategories = async (doc: Doc): Promise => { + const issue = toIssue(doc) + + if ([IssuesGrouping.Component, IssuesGrouping.Milestone].includes(groupByKey)) { + const availableCategories = [] + const clazz = hierarchy.getAttribute(tracker.class.Issue, groupByKey) + + for (const category of categories) { + if (!category || (issue as any)[groupByKey] === category) { + availableCategories.push(category) + } else if (clazz !== undefined && 'to' in clazz.type) { + const categoryDoc = await client.findOne(clazz.type.to as Ref>, { + _id: category as Ref, + space: issue.space + }) + + if (categoryDoc) { + availableCategories.push(category) + } + } + } + + return availableCategories + } + + if (groupByKey === IssuesGrouping.Status) { + const space = await client.findOne(tracker.class.Project, { _id: issue.space }) + return getStates(space, $typeStore, $statusStore.byId).map(({ _id }) => _id) + } + + return categories + } {#if categories.length === 0} @@ -288,6 +324,7 @@ on:obj-focus={(evt) => { listProvider.updateFocus(evt.detail) }} + {getAvailableCategories} selection={listProvider.current($focusStore)} checked={$selection ?? []} on:check={(evt) => {