@tuturuuu/ui 0.8.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/CHANGELOG.md +69 -0
  2. package/biome.json +1 -1
  3. package/package.json +74 -71
  4. package/src/components/ui/accordion.tsx +1 -1
  5. package/src/components/ui/breadcrumb.tsx +1 -1
  6. package/src/components/ui/calendar-app/calendar-page-shell.tsx +4 -0
  7. package/src/components/ui/calendar-app/components/calendar-connections-settings-content.tsx +239 -33
  8. package/src/components/ui/calendar-app/components/load-smart-scheduling-tasks.tsx +143 -0
  9. package/src/components/ui/calendar-app/components/priority-view.tsx +10 -3
  10. package/src/components/ui/calendar-app/components/tasks-sidebar.tsx +4 -116
  11. package/src/components/ui/calendar-app/components/use-calendar-connections-manager.ts +67 -2
  12. package/src/components/ui/calendar.tsx +1 -1
  13. package/src/components/ui/carousel.tsx +1 -1
  14. package/src/components/ui/chat/chat-agent-details-external-thread-panel.test.tsx +1 -1
  15. package/src/components/ui/chat/chat-agent-details-external-thread-panel.tsx +1 -1
  16. package/src/components/ui/chat/chat-agent-details-operations-panel.test.tsx +1 -1
  17. package/src/components/ui/chat/chat-agent-details-operations-panel.tsx +1 -1
  18. package/src/components/ui/chat/chat-agent-details-setup-panel.tsx +1 -1
  19. package/src/components/ui/chat/chat-agent-details-sidebar.test.tsx +1 -1
  20. package/src/components/ui/chat/chat-agent-details-sidebar.tsx +2 -2
  21. package/src/components/ui/chat/chat-agent-details-utils.test.ts +1 -1
  22. package/src/components/ui/chat/chat-agent-details-utils.tsx +1 -1
  23. package/src/components/ui/chat/chat-agent-details-zalo-personal-panel.tsx +2 -2
  24. package/src/components/ui/checkbox.tsx +1 -1
  25. package/src/components/ui/color-picker.tsx +1 -1
  26. package/src/components/ui/command.tsx +1 -1
  27. package/src/components/ui/context-menu.tsx +5 -1
  28. package/src/components/ui/custom/__tests__/settings-dialog-search.test.ts +78 -0
  29. package/src/components/ui/custom/__tests__/settings-dialog-shell-compile-graph.test.ts +76 -0
  30. package/src/components/ui/custom/__tests__/settings-dialog-shell.test.tsx +3 -0
  31. package/src/components/ui/custom/__tests__/workspace-select-helpers.test.ts +46 -1
  32. package/src/components/ui/custom/combobox.test.tsx +195 -0
  33. package/src/components/ui/custom/combobox.tsx +273 -156
  34. package/src/components/ui/custom/education/modules/youtube/delete-link-button.tsx +5 -13
  35. package/src/components/ui/custom/facebook-mockup/facebook-mockup.tsx +7 -1
  36. package/src/components/ui/custom/facebook-mockup/form.tsx +1 -1
  37. package/src/components/ui/custom/facebook-mockup/image-upload-field.tsx +1 -1
  38. package/src/components/ui/custom/facebook-mockup/preview.tsx +1 -1
  39. package/src/components/ui/custom/nav-link.test.tsx +165 -0
  40. package/src/components/ui/custom/nav-link.tsx +69 -11
  41. package/src/components/ui/custom/navigation.tsx +1 -0
  42. package/src/components/ui/custom/settings/task-settings.tsx +104 -0
  43. package/src/components/ui/custom/settings-dialog-search-loader.d.ts +5 -0
  44. package/src/components/ui/custom/settings-dialog-search-loader.js +3 -0
  45. package/src/components/ui/custom/settings-dialog-search.ts +75 -0
  46. package/src/components/ui/custom/settings-dialog-shell.tsx +65 -28
  47. package/src/components/ui/custom/theme-toggle.tsx +1 -1
  48. package/src/components/ui/custom/workspace-select-helpers.ts +23 -0
  49. package/src/components/ui/custom/workspace-select.tsx +25 -19
  50. package/src/components/ui/dialog.test.tsx +52 -0
  51. package/src/components/ui/dialog.tsx +6 -2
  52. package/src/components/ui/dropdown-menu.tsx +5 -1
  53. package/src/components/ui/finance/debts/debt-loan-form.tsx +12 -5
  54. package/src/components/ui/finance/debts/debt-loan-summary.tsx +3 -2
  55. package/src/components/ui/finance/debts/debts-page.test.tsx +54 -5
  56. package/src/components/ui/finance/debts/debts-page.tsx +15 -2
  57. package/src/components/ui/finance/invoices/components/subscription-group-selector.tsx +3 -5
  58. package/src/components/ui/finance/invoices/new-invoice-page.test.tsx +25 -5
  59. package/src/components/ui/finance/invoices/new-invoice-page.tsx +7 -2
  60. package/src/components/ui/finance/invoices/standard-invoice.tsx +4 -2
  61. package/src/components/ui/finance/invoices/subscription-invoice.tsx +4 -2
  62. package/src/components/ui/finance/invoices/utils.ts +3 -1
  63. package/src/components/ui/finance/transactions/form-content-dialog.tsx +3 -0
  64. package/src/components/ui/finance/transactions/form-types.ts +1 -0
  65. package/src/components/ui/finance/transactions/form.tsx +2 -0
  66. package/src/components/ui/finance/transactions/infinite-transactions-list.tsx +2 -0
  67. package/src/components/ui/finance/transactions/period-charts/category-breakdown-dialog.tsx +1 -1
  68. package/src/components/ui/finance/transactions/transaction-edit-dialog.tsx +1 -4
  69. package/src/components/ui/finance/transactions/transactions-create-summary.tsx +3 -0
  70. package/src/components/ui/finance/transactions/transactions-page.tsx +4 -1
  71. package/src/components/ui/finance/wallets/form.test.tsx +51 -3
  72. package/src/components/ui/finance/wallets/form.tsx +15 -4
  73. package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +4 -0
  74. package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +4 -2
  75. package/src/components/ui/finance/wallets/wallets-data-table.tsx +1 -0
  76. package/src/components/ui/finance/wallets/wallets-page.tsx +5 -2
  77. package/src/components/ui/input-otp.tsx +1 -1
  78. package/src/components/ui/legacy/calendar/all-day-event-bar.tsx +28 -39
  79. package/src/components/ui/legacy/calendar/calendar-cell.tsx +2 -0
  80. package/src/components/ui/legacy/calendar/calendar-content.tsx +10 -6
  81. package/src/components/ui/legacy/calendar/calendar-header.tsx +23 -3
  82. package/src/components/ui/legacy/calendar/calendar-loading-skeleton.tsx +135 -0
  83. package/src/components/ui/legacy/calendar/calendar-matrix.tsx +175 -237
  84. package/src/components/ui/legacy/calendar/event-card.test.tsx +177 -0
  85. package/src/components/ui/legacy/calendar/event-card.tsx +220 -131
  86. package/src/components/ui/legacy/calendar/event-modal.tsx +17 -17
  87. package/src/components/ui/legacy/calendar/event-provider-display.tsx +69 -0
  88. package/src/components/ui/legacy/calendar/smart-calendar.test.tsx +86 -4
  89. package/src/components/ui/legacy/calendar/smart-calendar.tsx +32 -2
  90. package/src/components/ui/legacy/meet/create-plan-dialog.tsx +19 -10
  91. package/src/components/ui/navigation-menu.tsx +1 -1
  92. package/src/components/ui/pagination.tsx +1 -1
  93. package/src/components/ui/radio-group.tsx +1 -1
  94. package/src/components/ui/select.tsx +5 -1
  95. package/src/components/ui/sheet.tsx +1 -1
  96. package/src/components/ui/sidebar.tsx +1 -1
  97. package/src/components/ui/storefront/cart-popover.tsx +61 -0
  98. package/src/components/ui/storefront/cart-summary-parts.tsx +290 -0
  99. package/src/components/ui/storefront/cart-summary.tsx +93 -154
  100. package/src/components/ui/storefront/checkout-overlay.tsx +4 -5
  101. package/src/components/ui/storefront/listing-card.tsx +1 -1
  102. package/src/components/ui/storefront/merch-sections.tsx +70 -0
  103. package/src/components/ui/storefront/product-detail.tsx +1 -1
  104. package/src/components/ui/storefront/storefront-surface.test.tsx +106 -11
  105. package/src/components/ui/storefront/storefront-surface.tsx +101 -166
  106. package/src/components/ui/storefront/types.ts +4 -0
  107. package/src/components/ui/storefront/utils.ts +6 -0
  108. package/src/components/ui/text-editor/__tests__/extensions.test.ts +123 -0
  109. package/src/components/ui/text-editor/background-color-extension.ts +62 -0
  110. package/src/components/ui/text-editor/color-controls.tsx +284 -0
  111. package/src/components/ui/text-editor/editor.tsx +69 -14
  112. package/src/components/ui/text-editor/extensions.ts +8 -2
  113. package/src/components/ui/text-editor/highlight-extension.ts +22 -0
  114. package/src/components/ui/text-editor/tool-bar.tsx +9 -16
  115. package/src/components/ui/toast.tsx +1 -1
  116. package/src/components/ui/tu-do/boards/__tests__/board-share-dialog.test.tsx +286 -0
  117. package/src/components/ui/tu-do/boards/__tests__/task-board-form.test.tsx +12 -0
  118. package/src/components/ui/tu-do/boards/board-public-link-section.tsx +231 -0
  119. package/src/components/ui/tu-do/boards/board-share-dialog.tsx +15 -226
  120. package/src/components/ui/tu-do/boards/board-share-settings-panel.tsx +351 -0
  121. package/src/components/ui/tu-do/boards/boardId/board-column.tsx +121 -39
  122. package/src/components/ui/tu-do/boards/boardId/enhanced-task-list.tsx +7 -0
  123. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-clear-delete.ts +2 -0
  124. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-move.ts +5 -0
  125. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-updates.ts +3 -0
  126. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operation-types.ts +3 -3
  127. package/src/components/ui/tu-do/boards/boardId/kanban/data/kanban-deadline-query.ts +50 -2
  128. package/src/components/ui/tu-do/boards/boardId/kanban/data/use-bulk-resources.ts +59 -5
  129. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/__tests__/column-reorder.test.ts +17 -0
  130. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/column-reorder.ts +4 -1
  131. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/drag-preview.tsx +20 -1
  132. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-cache.ts +38 -9
  133. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-order.ts +2 -8
  134. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-sort-key.ts +47 -0
  135. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +81 -30
  136. package/src/components/ui/tu-do/boards/boardId/kanban/planner/__tests__/kanban-planner-island.test.tsx +380 -0
  137. package/src/components/ui/tu-do/boards/boardId/kanban/planner/kanban-planner-dialog.tsx +204 -0
  138. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-digest-panel.tsx +61 -0
  139. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-item-strip.tsx +54 -0
  140. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-plan-toolbar.tsx +251 -0
  141. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-scope-badge.tsx +27 -0
  142. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-section.tsx +58 -0
  143. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-share-dialog.tsx +238 -0
  144. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-target-controls.tsx +143 -0
  145. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-utils.ts +65 -0
  146. package/src/components/ui/tu-do/boards/boardId/kanban/planner/use-kanban-planner-state.ts +234 -0
  147. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +642 -5
  148. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +224 -15
  149. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-deadline-panels.tsx +535 -53
  150. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-skeleton.tsx +101 -33
  151. package/src/components/ui/tu-do/boards/boardId/kanban.tsx +235 -113
  152. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.test.tsx +50 -5
  153. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +12 -2
  154. package/src/components/ui/tu-do/boards/boardId/task-card/measured-task-card.tsx +10 -1
  155. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.ts +3 -0
  156. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.test.ts +20 -0
  157. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-open-options.ts +10 -0
  158. package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +271 -36
  159. package/src/components/ui/tu-do/boards/boardId/task-filter.test.tsx +152 -0
  160. package/src/components/ui/tu-do/boards/boardId/task-filter.tsx +555 -545
  161. package/src/components/ui/tu-do/boards/boardId/task-list.tsx +22 -0
  162. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-grid.tsx +9 -0
  163. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-task-row.tsx +9 -0
  164. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-toolbar.tsx +9 -0
  165. package/src/components/ui/tu-do/boards/boardId/timeline-board.tsx +35 -3
  166. package/src/components/ui/tu-do/boards/form.tsx +1 -1
  167. package/src/components/ui/tu-do/boards/share-section.tsx +100 -0
  168. package/src/components/ui/tu-do/drafts/draft-convert-dialog.tsx +10 -12
  169. package/src/components/ui/tu-do/drafts/drafts-page.tsx +33 -16
  170. package/src/components/ui/tu-do/hooks/__tests__/useTaskLabelManagement.test.tsx +48 -0
  171. package/src/components/ui/tu-do/hooks/__tests__/useTaskProjectManagement.test.tsx +144 -0
  172. package/src/components/ui/tu-do/hooks/useTaskDialog.ts +7 -0
  173. package/src/components/ui/tu-do/hooks/useTaskLabelManagement.ts +115 -106
  174. package/src/components/ui/tu-do/hooks/useTaskProjectManagement.ts +115 -122
  175. package/src/components/ui/tu-do/initiatives/task-initiatives-client.tsx +56 -88
  176. package/src/components/ui/tu-do/my-tasks/my-tasks-content.tsx +26 -2
  177. package/src/components/ui/tu-do/my-tasks/use-my-tasks-state.ts +55 -8
  178. package/src/components/ui/tu-do/notes/note-edit-dialog.tsx +1 -4
  179. package/src/components/ui/tu-do/progress/task-progress-import-panel.tsx +60 -0
  180. package/src/components/ui/tu-do/progress/task-progress-leaderboards-panel.tsx +156 -0
  181. package/src/components/ui/tu-do/progress/task-progress-page.tsx +348 -0
  182. package/src/components/ui/tu-do/progress/task-progress-panels.tsx +301 -0
  183. package/src/components/ui/tu-do/providers/task-dialog-provider.tsx +26 -0
  184. package/src/components/ui/tu-do/shared/__tests__/assignee-select.test.tsx +81 -10
  185. package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +141 -1
  186. package/src/components/ui/tu-do/shared/__tests__/board-header.test.tsx +377 -36
  187. package/src/components/ui/tu-do/shared/__tests__/board-switcher.test.tsx +374 -0
  188. package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +419 -5
  189. package/src/components/ui/tu-do/shared/__tests__/task-board-loading-state.test.tsx +38 -0
  190. package/src/components/ui/tu-do/shared/__tests__/task-cache-patches.test.ts +147 -0
  191. package/src/components/ui/tu-do/shared/__tests__/task-legacy-route-recovery.test.tsx +16 -0
  192. package/src/components/ui/tu-do/shared/__tests__/use-progressive-board-loader.test.tsx +3 -0
  193. package/src/components/ui/tu-do/shared/assignee-select.tsx +77 -26
  194. package/src/components/ui/tu-do/shared/board-client.tsx +15 -10
  195. package/src/components/ui/tu-do/shared/board-config-storage.ts +7 -1
  196. package/src/components/ui/tu-do/shared/board-header.tsx +471 -975
  197. package/src/components/ui/tu-do/shared/board-layout-settings.tsx +165 -136
  198. package/src/components/ui/tu-do/shared/board-switcher.tsx +244 -220
  199. package/src/components/ui/tu-do/shared/board-user-presence-avatars.tsx +18 -12
  200. package/src/components/ui/tu-do/shared/board-views.tsx +577 -85
  201. package/src/components/ui/tu-do/shared/list-view.tsx +246 -2
  202. package/src/components/ui/tu-do/shared/recycle-bin-panel.tsx +142 -94
  203. package/src/components/ui/tu-do/shared/special-task-list-pins.ts +51 -0
  204. package/src/components/ui/tu-do/shared/task-board-loading-state.tsx +28 -0
  205. package/src/components/ui/tu-do/shared/task-cache-patches.ts +394 -0
  206. package/src/components/ui/tu-do/shared/task-dialog-manager.tsx +21 -1
  207. package/src/components/ui/tu-do/shared/task-edit-dialog/components/quick-settings-popover.tsx +5 -1
  208. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-dialog-header.tsx +25 -2
  209. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-list-selector.tsx +7 -1
  210. package/src/components/ui/tu-do/shared/task-edit-dialog/field-diff-viewer.tsx +3 -2
  211. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-data.ts +79 -10
  212. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-mutations.ts +76 -77
  213. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-relationships.test.tsx +63 -0
  214. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-relationships.ts +78 -69
  215. package/src/components/ui/tu-do/shared/task-edit-dialog/personal-overrides-section.tsx +28 -8
  216. package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/dependencies-section.tsx +14 -3
  217. package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/parent-section.tsx +6 -1
  218. package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/related-section.tsx +6 -1
  219. package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/subtasks-section.tsx +6 -1
  220. package/src/components/ui/tu-do/shared/task-edit-dialog/relationships/types/task-relationships.types.ts +8 -0
  221. package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.test.tsx +91 -0
  222. package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.tsx +123 -78
  223. package/src/components/ui/tu-do/shared/task-edit-dialog/task-activity-section.tsx +7 -1
  224. package/src/components/ui/tu-do/shared/task-edit-dialog/task-dialog-actions.tsx +8 -1
  225. package/src/components/ui/tu-do/shared/task-edit-dialog/task-properties-section.test.tsx +150 -0
  226. package/src/components/ui/tu-do/shared/task-edit-dialog/task-properties-section.tsx +61 -35
  227. package/src/components/ui/tu-do/shared/task-edit-dialog/task-relationships-properties.tsx +44 -2
  228. package/src/components/ui/tu-do/shared/task-edit-dialog/task-snapshot-dialog.tsx +8 -3
  229. package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +11 -1
  230. package/src/components/ui/tu-do/shared/task-legacy-route-recovery.tsx +2 -9
  231. package/src/components/ui/tu-do/shared/task-row-actions-menu.tsx +11 -0
  232. package/src/components/ui/tu-do/shared/use-progressive-board-loader.ts +2 -0
  233. package/src/declarations.d.ts +1 -0
  234. package/src/hooks/__tests__/use-calendar-readonly.test.tsx +322 -2
  235. package/src/hooks/__tests__/use-calendar-sync.test.tsx +446 -0
  236. package/src/hooks/__tests__/useBoardPresence.test.tsx +191 -0
  237. package/src/hooks/__tests__/useBoardRealtime.test.tsx +24 -144
  238. package/src/hooks/use-calendar-sync.tsx +247 -243
  239. package/src/hooks/use-calendar.tsx +323 -138
  240. package/src/hooks/use-task-actions.ts +24 -0
  241. package/src/hooks/use-user-workspace-config.ts +75 -0
  242. package/src/hooks/use-workspace-currency.ts +8 -3
  243. package/src/hooks/useBoardPresence.ts +364 -0
  244. package/src/hooks/useBoardRealtimeEventHandler.ts +45 -90
  245. package/src/lib/workspace-actions.ts +2 -6
@@ -11,6 +11,8 @@ import {
11
11
  Filter,
12
12
  GripVertical,
13
13
  Loader2,
14
+ Pin,
15
+ PinOff,
14
16
  RotateCcw,
15
17
  } from '@tuturuuu/icons';
16
18
  import type { ExternalTaskSortBy } from '@tuturuuu/internal-api/tasks';
@@ -49,6 +51,7 @@ import {
49
51
  } from './kanban/kanban-column-collapse';
50
52
  import { ListActions } from './list-actions';
51
53
  import { statusIcons } from './status-section';
54
+ import type { TaskCardAssigneeMemberSource } from './task-card/task-card';
52
55
  import type { TaskFilters } from './task-filter';
53
56
  import { VirtualizedTaskList } from './task-list';
54
57
 
@@ -161,6 +164,8 @@ interface BoardColumnProps {
161
164
  isMultiSelectMode?: boolean;
162
165
  setIsMultiSelectMode?: (value: boolean) => void;
163
166
  isPersonalWorkspace?: boolean;
167
+ canUseBoardAssignees?: boolean;
168
+ assigneeMemberSource?: TaskCardAssigneeMemberSource;
164
169
  onTaskSelect?: (taskId: string, event: React.MouseEvent) => void;
165
170
  onClearSelection?: () => void;
166
171
  onAddTask?: (list: TaskList) => void;
@@ -174,6 +179,10 @@ interface BoardColumnProps {
174
179
  wsId: string;
175
180
  onExternalTasksCollapsedChange?: (collapsed: boolean) => void;
176
181
  onTaskListCollapsedChange?: (listId: string, collapsed: boolean) => void;
182
+ specialPinned?: boolean;
183
+ specialStickyOffset?: string;
184
+ onSpecialPinnedChange?: (pinned: boolean) => void;
185
+ readOnly?: boolean;
177
186
  }
178
187
 
179
188
  export function BoardColumn({
@@ -189,6 +198,8 @@ export function BoardColumn({
189
198
  isMultiSelectMode,
190
199
  setIsMultiSelectMode,
191
200
  isPersonalWorkspace,
201
+ canUseBoardAssignees,
202
+ assigneeMemberSource,
192
203
  onAddTask,
193
204
  dragPreviewPosition,
194
205
  suppressTaskTransforms,
@@ -200,6 +211,10 @@ export function BoardColumn({
200
211
  wsId,
201
212
  onExternalTasksCollapsedChange,
202
213
  onTaskListCollapsedChange,
214
+ specialPinned = false,
215
+ specialStickyOffset,
216
+ onSpecialPinnedChange,
217
+ readOnly = false,
203
218
  }: BoardColumnProps) {
204
219
  const t = useTranslations('common');
205
220
  const tTasks = useTranslations('ws-tasks');
@@ -210,7 +225,9 @@ export function BoardColumn({
210
225
  const isExternalCollapsed =
211
226
  isExternalStaging && column.is_external_collapsed === true;
212
227
  const listState = pagination[column.id];
213
- const isInitialLoad = !listState || listState.isInitialLoad;
228
+ const isInitialLoad = readOnly
229
+ ? false
230
+ : !listState || listState.isInitialLoad;
214
231
  const [externalIncludeDocuments, setExternalIncludeDocuments] =
215
232
  useState(false);
216
233
  const [externalIncludeDoneClosed, setExternalIncludeDoneClosed] =
@@ -376,7 +393,7 @@ export function BoardColumn({
376
393
  isDragging,
377
394
  } = useSortable({
378
395
  id: column.id,
379
- disabled: isExternalStaging,
396
+ disabled: readOnly || isExternalStaging,
380
397
  data: {
381
398
  type: 'Column',
382
399
  column: {
@@ -400,6 +417,12 @@ export function BoardColumn({
400
417
  transform: CSS.Transform.toString(transform),
401
418
  transition,
402
419
  };
420
+ const columnStyle: React.CSSProperties = specialStickyOffset
421
+ ? {
422
+ ...style,
423
+ left: `calc(var(--kanban-snap-left-padding) + ${specialStickyOffset})`,
424
+ }
425
+ : style;
403
426
 
404
427
  const handleUpdate = () => {
405
428
  onUpdate?.();
@@ -446,6 +469,9 @@ export function BoardColumn({
446
469
  : visibleTasks.length;
447
470
  const externalFilterCount =
448
471
  (externalIncludeDocuments ? 1 : 0) + (externalIncludeDoneClosed ? 1 : 0);
472
+ const pinListLabel = specialPinned
473
+ ? tTasks('unpin_task_list', { name: translateListName(column.name) })
474
+ : tTasks('pin_task_list', { name: translateListName(column.name) });
449
475
 
450
476
  // Memoize drag handle for performance
451
477
  const DragHandle = useMemo(
@@ -500,10 +526,14 @@ export function BoardColumn({
500
526
  return (
501
527
  <Card
502
528
  ref={composedRef}
503
- style={style}
529
+ style={columnStyle}
530
+ data-kanban-pinned-special={specialStickyOffset ? 'true' : undefined}
531
+ data-kanban-column-id={column.id}
532
+ data-kanban-real-column={isExternalStaging ? undefined : 'true'}
504
533
  className={cn(
505
534
  'group flex h-full w-14 shrink-0 snap-start flex-col items-center rounded-xl border border-dashed transition-all duration-200',
506
535
  'touch-none select-none overflow-hidden hover:shadow-md',
536
+ specialStickyOffset && 'sticky z-30',
507
537
  isExternalCollapsed
508
538
  ? 'border-dynamic-cyan/45 bg-dynamic-cyan/[0.035]'
509
539
  : colorClass
@@ -549,10 +579,14 @@ export function BoardColumn({
549
579
  return (
550
580
  <Card
551
581
  ref={composedRef}
552
- style={style}
582
+ style={columnStyle}
583
+ data-kanban-pinned-special={specialStickyOffset ? 'true' : undefined}
584
+ data-kanban-column-id={column.id}
585
+ data-kanban-real-column={isExternalStaging ? undefined : 'true'}
553
586
  className={cn(
554
587
  'group flex h-full w-[var(--kanban-column-width)] shrink-0 snap-start flex-col rounded-xl transition-all duration-200 last:snap-end',
555
588
  'touch-none select-none',
589
+ specialStickyOffset && 'sticky z-30',
556
590
  colorClass,
557
591
  isDragging &&
558
592
  'rotate-1 scale-[1.02] opacity-90 shadow-xl ring-2 ring-primary/20',
@@ -566,7 +600,7 @@ export function BoardColumn({
566
600
  )}
567
601
  >
568
602
  <div className="flex items-center gap-2 rounded-t-xl border-b p-3">
569
- {!isExternalStaging && DragHandle}
603
+ {!readOnly && !isExternalStaging && DragHandle}
570
604
  <div className="flex flex-1 items-center gap-2">
571
605
  <span className="text-sm">{statusIcon}</span>
572
606
  <h3
@@ -577,9 +611,9 @@ export function BoardColumn({
577
611
  : 'cursor-pointer hover:underline'
578
612
  )}
579
613
  onClick={() => {
580
- if (!isExternalStaging) setIsEditOpen(true);
614
+ if (!readOnly && !isExternalStaging) setIsEditOpen(true);
581
615
  }}
582
- title={isExternalStaging ? undefined : t('edit_list')}
616
+ title={readOnly || isExternalStaging ? undefined : t('edit_list')}
583
617
  >
584
618
  {translateListName(column.name)}
585
619
  </h3>
@@ -705,6 +739,26 @@ export function BoardColumn({
705
739
  </DropdownMenuRadioGroup>
706
740
  </DropdownMenuContent>
707
741
  </DropdownMenu>
742
+ {onSpecialPinnedChange ? (
743
+ <Button
744
+ type="button"
745
+ variant="ghost"
746
+ size="xs"
747
+ className={cn(
748
+ 'h-7 w-7 p-0 text-dynamic-cyan hover:bg-dynamic-cyan/10',
749
+ specialPinned && 'bg-dynamic-cyan/10'
750
+ )}
751
+ title={pinListLabel}
752
+ aria-label={pinListLabel}
753
+ onClick={() => onSpecialPinnedChange(!specialPinned)}
754
+ >
755
+ {specialPinned ? (
756
+ <PinOff className="h-3.5 w-3.5" />
757
+ ) : (
758
+ <Pin className="h-3.5 w-3.5" />
759
+ )}
760
+ </Button>
761
+ ) : null}
708
762
  <Button
709
763
  type="button"
710
764
  variant="ghost"
@@ -720,38 +774,63 @@ export function BoardColumn({
720
774
  ) : (
721
775
  <>
722
776
  {isClosedCollapsed || column.status === 'closed' ? (
723
- <Button
724
- type="button"
725
- variant="ghost"
726
- size="xs"
727
- className={cn(
728
- 'h-7 w-7 p-0 hover:bg-muted/40',
729
- getListTextColorClass(column.color as SupportedColor)
730
- )}
731
- title={tTasks('collapse_task_list', {
732
- name: translateListName(column.name),
733
- })}
734
- aria-label={tTasks('collapse_task_list', {
735
- name: translateListName(column.name),
736
- })}
737
- onClick={() => onTaskListCollapsedChange?.(column.id, true)}
738
- >
739
- <ChevronLeft className="h-3.5 w-3.5" />
740
- </Button>
777
+ <>
778
+ {onSpecialPinnedChange ? (
779
+ <Button
780
+ type="button"
781
+ variant="ghost"
782
+ size="xs"
783
+ className={cn(
784
+ 'h-7 w-7 p-0 hover:bg-muted/40',
785
+ getListTextColorClass(column.color as SupportedColor),
786
+ specialPinned && 'bg-muted/40'
787
+ )}
788
+ title={pinListLabel}
789
+ aria-label={pinListLabel}
790
+ onClick={() => onSpecialPinnedChange(!specialPinned)}
791
+ >
792
+ {specialPinned ? (
793
+ <PinOff className="h-3.5 w-3.5" />
794
+ ) : (
795
+ <Pin className="h-3.5 w-3.5" />
796
+ )}
797
+ </Button>
798
+ ) : null}
799
+ <Button
800
+ type="button"
801
+ variant="ghost"
802
+ size="xs"
803
+ className={cn(
804
+ 'h-7 w-7 p-0 hover:bg-muted/40',
805
+ getListTextColorClass(column.color as SupportedColor)
806
+ )}
807
+ title={tTasks('collapse_task_list', {
808
+ name: translateListName(column.name),
809
+ })}
810
+ aria-label={tTasks('collapse_task_list', {
811
+ name: translateListName(column.name),
812
+ })}
813
+ onClick={() => onTaskListCollapsedChange?.(column.id, true)}
814
+ >
815
+ <ChevronLeft className="h-3.5 w-3.5" />
816
+ </Button>
817
+ </>
741
818
  ) : null}
742
- <ListActions
743
- listId={column.id}
744
- listName={column.name}
745
- listStatus={column.status}
746
- listColor={column.color as SupportedColor}
747
- tasks={tasks}
748
- boardId={boardId}
749
- wsId={wsId}
750
- onUpdate={handleUpdate}
751
- onSelectAll={handleSelectAll}
752
- isEditOpen={isEditOpen}
753
- onEditOpenChange={setIsEditOpen}
754
- />
819
+ {!readOnly && (
820
+ <ListActions
821
+ listId={column.id}
822
+ listName={column.name}
823
+ listStatus={column.status}
824
+ listColor={column.color as SupportedColor}
825
+ tasks={tasks}
826
+ boardId={boardId}
827
+ wsId={wsId}
828
+ onUpdate={handleUpdate}
829
+ onSelectAll={handleSelectAll}
830
+ isEditOpen={isEditOpen}
831
+ onEditOpenChange={setIsEditOpen}
832
+ />
833
+ )}
755
834
  </>
756
835
  )}
757
836
  </div>
@@ -773,6 +852,8 @@ export function BoardColumn({
773
852
  isMultiSelectMode={isMultiSelectMode}
774
853
  selectedTasks={selectedTasks}
775
854
  isPersonalWorkspace={isPersonalWorkspace}
855
+ canUseBoardAssignees={canUseBoardAssignees}
856
+ assigneeMemberSource={assigneeMemberSource}
776
857
  onTaskSelect={onTaskSelect}
777
858
  onClearSelection={onClearSelection}
778
859
  dragPreviewPosition={dragPreviewPosition}
@@ -783,10 +864,11 @@ export function BoardColumn({
783
864
  onLoadMore={handleLoadMore}
784
865
  hasMore={listState?.hasMore ?? false}
785
866
  isLoadingMore={listState?.isLoading ?? false}
867
+ readOnly={readOnly}
786
868
  />
787
869
  )}
788
870
 
789
- {!isExternalStaging && (
871
+ {!readOnly && !isExternalStaging && (
790
872
  <div className="rounded-b-xl border-t p-3 backdrop-blur-sm">
791
873
  <Button
792
874
  variant="ghost"
@@ -44,6 +44,7 @@ import { useBoardBroadcast } from '../../shared/board-broadcast-context';
44
44
  import { isTaskListNameExistsError } from '../../shared/task-board-errors';
45
45
  import { normalizeBoardText } from './board-text-utils';
46
46
  import { TaskCard } from './task';
47
+ import type { TaskCardAssigneeMemberSource } from './task-card/task-card';
47
48
 
48
49
  interface Props {
49
50
  list: TaskList;
@@ -54,6 +55,8 @@ interface Props {
54
55
  isOverlay?: boolean;
55
56
  hideTasksMode?: boolean;
56
57
  isPersonalWorkspace?: boolean;
58
+ canUseBoardAssignees?: boolean;
59
+ assigneeMemberSource?: TaskCardAssigneeMemberSource;
57
60
  onAddTask?: (list: TaskList) => void;
58
61
  }
59
62
 
@@ -102,6 +105,8 @@ export function EnhancedTaskList({
102
105
  isOverlay = false,
103
106
  hideTasksMode = false,
104
107
  isPersonalWorkspace = false,
108
+ canUseBoardAssignees,
109
+ assigneeMemberSource,
105
110
  onAddTask,
106
111
  }: Props) {
107
112
  const t = useTranslations('common');
@@ -479,6 +484,8 @@ export function EnhancedTaskList({
479
484
  taskList={list}
480
485
  boardId={boardId}
481
486
  isPersonalWorkspace={isPersonalWorkspace}
487
+ canUseBoardAssignees={canUseBoardAssignees}
488
+ assigneeMemberSource={assigneeMemberSource}
482
489
  onUpdate={onUpdate}
483
490
  />
484
491
  ))}
@@ -4,6 +4,7 @@ import { type QueryClient, useMutation } from '@tanstack/react-query';
4
4
  import { bulkWorkspaceTasks } from '@tuturuuu/internal-api/tasks';
5
5
  import { toast } from '@tuturuuu/ui/sonner';
6
6
  import type { BoardBroadcastFn } from '../../../../shared/board-broadcast-context';
7
+ import { invalidateKanbanDeadlineTasks } from '../data/kanban-deadline-query';
7
8
  import type { BulkOperationI18n } from './bulk-operation-i18n';
8
9
  import {
9
10
  type BulkTaskWorkspaceGroup,
@@ -390,6 +391,7 @@ export function useBulkDeleteTasks(
390
391
  for (const tid of succeededTaskIds) {
391
392
  broadcast?.('task:delete', { taskId: tid });
392
393
  }
394
+ void invalidateKanbanDeadlineTasks(queryClient, boardId);
393
395
 
394
396
  clearSelection();
395
397
  setBulkDeleteOpen(false);
@@ -6,6 +6,7 @@ import type { Task } from '@tuturuuu/types/primitives/Task';
6
6
  import type { TaskList } from '@tuturuuu/types/primitives/TaskList';
7
7
  import { toast } from '@tuturuuu/ui/sonner';
8
8
  import type { BoardBroadcastFn } from '../../../../shared/board-broadcast-context';
9
+ import { invalidateKanbanDeadlineTasks } from '../data/kanban-deadline-query';
9
10
  import type { BulkOperationI18n } from './bulk-operation-i18n';
10
11
  import { getInternalApiOptions } from './bulk-operation-utils';
11
12
  import {
@@ -184,6 +185,8 @@ export function useBulkMoveToBoard(
184
185
  for (const tid of movedTaskIds) {
185
186
  broadcast?.('task:delete', { taskId: tid });
186
187
  }
188
+ void invalidateKanbanDeadlineTasks(queryClient, boardId);
189
+ void invalidateKanbanDeadlineTasks(queryClient, data.targetBoardId);
187
190
 
188
191
  if (data.failures.length > 0) {
189
192
  toast.warning(
@@ -428,6 +431,7 @@ export function useBulkMoveToList(
428
431
  },
429
432
  });
430
433
  }
434
+ void invalidateKanbanDeadlineTasks(queryClient, boardId);
431
435
 
432
436
  if (data.failures.length > 0) {
433
437
  toast.warning(
@@ -631,6 +635,7 @@ export function useBulkMoveToStatus(
631
635
  },
632
636
  });
633
637
  }
638
+ void invalidateKanbanDeadlineTasks(queryClient, boardId);
634
639
 
635
640
  if (data.failures.length > 0) {
636
641
  toast.warning(
@@ -4,6 +4,7 @@ import { type QueryClient, useMutation } from '@tanstack/react-query';
4
4
  import type { Task } from '@tuturuuu/types/primitives/Task';
5
5
  import { toast } from '@tuturuuu/ui/sonner';
6
6
  import type { BoardBroadcastFn } from '../../../../shared/board-broadcast-context';
7
+ import { invalidateKanbanDeadlineTasks } from '../data/kanban-deadline-query';
7
8
  import type { BulkOperationI18n } from './bulk-operation-i18n';
8
9
  import {
9
10
  bulkWorkspaceTasksByEffectiveWorkspace,
@@ -322,6 +323,7 @@ export function useBulkUpdateDueDate(
322
323
  task: { id: tid, end_date: data.end_date },
323
324
  });
324
325
  }
326
+ void invalidateKanbanDeadlineTasks(queryClient, boardId);
325
327
 
326
328
  if (data.failures.length > 0) {
327
329
  toast.warning(
@@ -434,6 +436,7 @@ export function useBulkUpdateCustomDueDate(
434
436
  task: { id: tid, end_date: data.end_date },
435
437
  });
436
438
  }
439
+ void invalidateKanbanDeadlineTasks(queryClient, boardId);
437
440
 
438
441
  if (data.failures.length > 0) {
439
442
  toast.warning(
@@ -12,9 +12,9 @@ export interface WorkspaceProject {
12
12
  export interface WorkspaceMember {
13
13
  id: string;
14
14
  user_id?: string;
15
- display_name: string;
16
- email: string;
17
- avatar_url: string | null;
15
+ display_name?: string;
16
+ email?: string;
17
+ avatar_url?: string | null;
18
18
  }
19
19
 
20
20
  export interface BulkOperationsConfig {
@@ -1,22 +1,70 @@
1
+ import type { QueryClient } from '@tanstack/react-query';
1
2
  import { listWorkspaceTasks } from '@tuturuuu/internal-api';
3
+ import type { ListWorkspaceTasksOptions } from '@tuturuuu/internal-api/tasks';
2
4
  import type { Task } from '@tuturuuu/types/primitives/Task';
3
5
 
4
6
  const KANBAN_DEADLINE_TASK_PAGE_SIZE = 200;
7
+ export const KANBAN_DEADLINE_TASKS_QUERY_KEY = 'kanban-deadline-tasks';
8
+
9
+ export function getKanbanDeadlineTasksQueryKey(
10
+ workspaceId: string,
11
+ boardId: string | null | undefined,
12
+ taskQueryOptions?: ListWorkspaceTasksOptions
13
+ ) {
14
+ return [
15
+ KANBAN_DEADLINE_TASKS_QUERY_KEY,
16
+ workspaceId,
17
+ boardId,
18
+ taskQueryOptions,
19
+ ] as const;
20
+ }
21
+
22
+ export function invalidateKanbanDeadlineTasks(
23
+ queryClient: QueryClient,
24
+ boardId?: string | null
25
+ ) {
26
+ return queryClient.invalidateQueries({
27
+ predicate: (query) => {
28
+ const queryKey = query.queryKey;
29
+ if (!Array.isArray(queryKey)) return false;
30
+ if (queryKey[0] !== KANBAN_DEADLINE_TASKS_QUERY_KEY) return false;
31
+ return !boardId || queryKey[2] === boardId;
32
+ },
33
+ });
34
+ }
5
35
 
6
36
  interface ListKanbanDeadlineTasksOptions {
7
37
  boardId: string;
38
+ taskQueryOptions?: ListWorkspaceTasksOptions;
8
39
  workspaceId: string;
9
40
  }
10
41
 
11
42
  export async function listKanbanDeadlineTasks({
12
43
  boardId,
44
+ taskQueryOptions,
13
45
  workspaceId,
14
46
  }: ListKanbanDeadlineTasksOptions): Promise<Task[]> {
15
47
  const tasks: Task[] = [];
16
48
  let offset = 0;
49
+ const {
50
+ boardId: _boardId,
51
+ closed: _closed,
52
+ completed: _completed,
53
+ externalSortBy: _externalSortBy,
54
+ hasDueDate: _hasDueDate,
55
+ includeCount: _includeCount,
56
+ includeListCounts: _includeListCounts,
57
+ includeRelationshipSummary: _includeRelationshipSummary,
58
+ limit: _limit,
59
+ listId: _listId,
60
+ offset: _offset,
61
+ sortBy: _sortBy,
62
+ ...filterOptions
63
+ } = taskQueryOptions ?? {};
17
64
 
18
65
  while (true) {
19
66
  const response = await listWorkspaceTasks(workspaceId, {
67
+ ...filterOptions,
20
68
  boardId,
21
69
  closed: 'exclude',
22
70
  completed: 'exclude',
@@ -25,9 +73,9 @@ export async function listKanbanDeadlineTasks({
25
73
  includeCount: true,
26
74
  includeRelationshipSummary: false,
27
75
  limit: KANBAN_DEADLINE_TASK_PAGE_SIZE,
28
- listStatuses: ['not_started', 'active'],
76
+ listStatuses: filterOptions.listStatuses ?? ['not_started', 'active'],
29
77
  offset,
30
- sourceScope: 'all_visible',
78
+ sourceScope: filterOptions.sourceScope ?? 'all_visible',
31
79
  });
32
80
 
33
81
  tasks.push(...response.tasks);
@@ -5,14 +5,24 @@ import {
5
5
  listWorkspaceLabels,
6
6
  listWorkspaceTaskProjects,
7
7
  } from '@tuturuuu/internal-api';
8
+ import { listWorkspaceTaskBoardViewableMembers } from '@tuturuuu/internal-api/tasks';
8
9
  import type { Workspace } from '@tuturuuu/types';
9
- import { useWorkspaceMembers } from '@tuturuuu/ui/hooks/use-workspace-members';
10
+ import {
11
+ useWorkspaceMembers,
12
+ type WorkspaceMember,
13
+ } from '@tuturuuu/ui/hooks/use-workspace-members';
10
14
 
11
15
  export function useBulkResources({
16
+ boardId,
17
+ canUseBoardAssignees,
18
+ assigneeMemberSource,
12
19
  workspace,
13
20
  isMultiSelectMode,
14
21
  selectedCount,
15
22
  }: {
23
+ boardId?: string | null;
24
+ canUseBoardAssignees?: boolean;
25
+ assigneeMemberSource?: 'workspace' | 'board' | 'workspace-and-board';
16
26
  workspace: Workspace;
17
27
  isMultiSelectMode: boolean;
18
28
  selectedCount: number;
@@ -46,13 +56,57 @@ export function useBulkResources({
46
56
  });
47
57
 
48
58
  // Workspace members for bulk operations
49
- const { data: workspaceMembers = [] } = useWorkspaceMembers(workspace.id, {
59
+ const shouldLoadMembers =
60
+ canUseBoardAssignees !== false && isMultiSelectMode && selectedCount > 0;
61
+ const effectiveAssigneeMemberSource =
62
+ assigneeMemberSource ?? (workspace.personal ? 'board' : 'workspace');
63
+ const { data: workspaceMembersData = [] } = useWorkspaceMembers(
64
+ workspace.id,
65
+ {
66
+ enabled:
67
+ !!workspace.id &&
68
+ shouldLoadMembers &&
69
+ effectiveAssigneeMemberSource !== 'board',
70
+ }
71
+ );
72
+ const { data: boardViewableMembers = [] } = useQuery({
73
+ queryKey: ['task-board-viewable-members', workspace.id, boardId],
74
+ queryFn: async (): Promise<WorkspaceMember[]> => {
75
+ if (!workspace.id || !boardId) return [];
76
+
77
+ const payload = await listWorkspaceTaskBoardViewableMembers(
78
+ workspace.id,
79
+ boardId
80
+ );
81
+ const members = Array.isArray(payload?.members) ? payload.members : [];
82
+
83
+ return members.map((member) => ({
84
+ id: member.user_id,
85
+ user_id: member.user_id,
86
+ workspace_id: workspace.id,
87
+ display_name: member.display_name ?? member.email ?? member.user_id,
88
+ email: member.email ?? undefined,
89
+ avatar_url: member.avatar_url ?? undefined,
90
+ }));
91
+ },
50
92
  enabled:
51
93
  !!workspace.id &&
52
- !workspace.personal &&
53
- isMultiSelectMode &&
54
- selectedCount > 0,
94
+ !!boardId &&
95
+ shouldLoadMembers &&
96
+ effectiveAssigneeMemberSource !== 'workspace',
97
+ staleTime: 5 * 60 * 1000,
55
98
  });
99
+ const workspaceMembers: WorkspaceMember[] = [
100
+ ...workspaceMembersData,
101
+ ...boardViewableMembers.filter(
102
+ (boardMember) =>
103
+ !workspaceMembersData.some(
104
+ (workspaceMember) =>
105
+ (workspaceMember.user_id ?? workspaceMember.id) ===
106
+ boardMember.user_id
107
+ )
108
+ ),
109
+ ];
56
110
 
57
111
  return { workspaceLabels, workspaceProjects, workspaceMembers };
58
112
  }
@@ -43,6 +43,23 @@ describe('getColumnReorderUpdates', () => {
43
43
  expect(getColumnReorderUpdates(columns, 'todo', 'doing')).toBeNull();
44
44
  });
45
45
 
46
+ it('excludes synthetic external staging lanes from persisted position repairs', () => {
47
+ const externalLane = {
48
+ ...makeList('external', 'not_started', -1),
49
+ is_external_staging: true,
50
+ };
51
+ const columns = [
52
+ externalLane,
53
+ makeList('todo', 'not_started', 0),
54
+ makeList('backlog', 'not_started', 1),
55
+ ];
56
+
57
+ expect(getColumnReorderUpdates(columns, 'backlog', 'todo')).toEqual([
58
+ { listId: 'backlog', newPosition: 0 },
59
+ { listId: 'todo', newPosition: 1 },
60
+ ]);
61
+ });
62
+
46
63
  it('sorts columns in the same order they are rendered in Kanban', () => {
47
64
  const columns = [
48
65
  makeList('closed', 'closed', 0),
@@ -39,7 +39,10 @@ export function getColumnReorderUpdates(
39
39
  }
40
40
 
41
41
  const statusColumns = [...columns]
42
- .filter((column) => column.status === activeColumn.status)
42
+ .filter(
43
+ (column) =>
44
+ column.status === activeColumn.status && !column.is_external_staging
45
+ )
43
46
  .sort((a, b) => (a.position ?? 0) - (b.position ?? 0));
44
47
 
45
48
  const activeIndex = statusColumns.findIndex(
@@ -13,6 +13,8 @@ interface DragPreviewProps {
13
13
  columns: TaskList[];
14
14
  boardId: string;
15
15
  isPersonalWorkspace: boolean;
16
+ canUseBoardAssignees?: boolean;
17
+ assigneeMemberSource?: 'workspace' | 'board' | 'workspace-and-board';
16
18
  isMultiSelectMode: boolean;
17
19
  selectedTasks: Set<string>;
18
20
  onUpdate: () => void;
@@ -26,6 +28,8 @@ export function DragPreview({
26
28
  columns,
27
29
  boardId,
28
30
  isPersonalWorkspace,
31
+ canUseBoardAssignees,
32
+ assigneeMemberSource,
29
33
  isMultiSelectMode,
30
34
  selectedTasks,
31
35
  onUpdate,
@@ -52,6 +56,8 @@ export function DragPreview({
52
56
  isOverlay
53
57
  onUpdate={onUpdate}
54
58
  isPersonalWorkspace={isPersonalWorkspace}
59
+ canUseBoardAssignees={canUseBoardAssignees}
60
+ assigneeMemberSource={assigneeMemberSource}
55
61
  />
56
62
  {isMultiCardDrag && (
57
63
  <>
@@ -74,6 +80,8 @@ export function DragPreview({
74
80
  boardId,
75
81
  onUpdate,
76
82
  isPersonalWorkspace,
83
+ canUseBoardAssignees,
84
+ assigneeMemberSource,
77
85
  isMultiSelectMode,
78
86
  selectedTasks,
79
87
  ]);
@@ -87,11 +95,22 @@ export function DragPreview({
87
95
  tasks={tasks.filter((task) => task.list_id === activeColumn.id)}
88
96
  isOverlay
89
97
  isPersonalWorkspace={isPersonalWorkspace}
98
+ canUseBoardAssignees={canUseBoardAssignees}
99
+ assigneeMemberSource={assigneeMemberSource}
90
100
  onUpdate={onUpdate}
91
101
  wsId={wsId}
92
102
  />
93
103
  ) : null,
94
- [activeColumn, tasks, boardId, isPersonalWorkspace, onUpdate, wsId]
104
+ [
105
+ activeColumn,
106
+ tasks,
107
+ boardId,
108
+ isPersonalWorkspace,
109
+ canUseBoardAssignees,
110
+ assigneeMemberSource,
111
+ onUpdate,
112
+ wsId,
113
+ ]
95
114
  );
96
115
 
97
116
  return <>{MemoizedTaskOverlay || MemoizedColumnOverlay}</>;