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<Item> | undefined
+  export let getAvailableCategories: ((doc: Doc) => Promise<CategoryType[]>) | 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<Project> | undefined = undefined
   export let baseMenuClass: Ref<Class<Doc>> | undefined = undefined
@@ -264,6 +267,39 @@
     if (enabledConfig(config, 'attachments') && (issue.attachments ?? 0) > 0) return true
     return false
   }
+
+  const getAvailableCategories = async (doc: Doc): Promise<CategoryType[]> => {
+    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<Class<Doc>>, {
+            _id: category as Ref<Doc>,
+            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
+  }
 </script>
 
 {#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) => {