@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
@@ -72,16 +72,18 @@ const BOARD_ID = '11111111-1111-1111-1111-111111111111';
72
72
 
73
73
  type BoardClientElement = ReactElement<{
74
74
  currentUserId?: string;
75
+ rootLoading?: boolean;
75
76
  workspace: unknown;
76
77
  workspaceTier: unknown;
77
78
  }>;
78
79
 
79
- function renderServerPage() {
80
+ function renderServerPage(options?: { rootLoading?: boolean }) {
80
81
  return TaskBoardServerPage({
81
82
  params: Promise.resolve({
82
83
  boardId: BOARD_ID,
83
84
  wsId: 'ws-1',
84
85
  }),
86
+ rootLoading: options?.rootLoading,
85
87
  }) as Promise<BoardClientElement>;
86
88
  }
87
89
 
@@ -111,6 +113,28 @@ describe('TaskBoardServerPage', () => {
111
113
  expect(mocks.getWorkspace).not.toHaveBeenCalled();
112
114
  });
113
115
 
116
+ it('treats invalid board routes as not found instead of crashing the server render', async () => {
117
+ mocks.getWorkspaceTaskBoard.mockRejectedValue(
118
+ new mocks.InternalApiError('Invalid workspace or board ID', 400)
119
+ );
120
+
121
+ await expect(renderServerPage()).rejects.toThrow('NEXT_NOT_FOUND');
122
+
123
+ expect(mocks.getWorkspace).not.toHaveBeenCalled();
124
+ });
125
+
126
+ it('keeps unexpected board loader failures visible', async () => {
127
+ mocks.getWorkspaceTaskBoard.mockRejectedValue(
128
+ new mocks.InternalApiError('Failed to load task board', 500)
129
+ );
130
+
131
+ await expect(renderServerPage()).rejects.toThrow(
132
+ 'Failed to load task board'
133
+ );
134
+
135
+ expect(mocks.getWorkspace).not.toHaveBeenCalled();
136
+ });
137
+
114
138
  it('uses a minimal workspace shell for board guests', async () => {
115
139
  mocks.getWorkspaceTaskBoard.mockResolvedValue({
116
140
  board: {
@@ -134,10 +158,10 @@ describe('TaskBoardServerPage', () => {
134
158
  expect(element.props.currentUserId).toBe('user-1');
135
159
  });
136
160
 
137
- it('loads the full member workspace only after board access succeeds', async () => {
161
+ it('loads the full member workspace from the resolved board workspace', async () => {
138
162
  const workspace = {
139
163
  creator_id: 'creator-1',
140
- id: 'ws-1',
164
+ id: 'ws-board',
141
165
  joined: true,
142
166
  name: 'Member Workspace',
143
167
  personal: false,
@@ -147,18 +171,39 @@ describe('TaskBoardServerPage', () => {
147
171
  board: {
148
172
  access_type: 'member',
149
173
  id: BOARD_ID,
150
- ws_id: 'ws-1',
174
+ ws_id: 'ws-board',
151
175
  },
152
176
  });
153
177
  mocks.getWorkspace.mockResolvedValue(workspace);
154
178
 
155
179
  const element = await renderServerPage();
156
180
 
157
- expect(mocks.getWorkspace).toHaveBeenCalledWith('ws-1', {
181
+ expect(mocks.getWorkspace).toHaveBeenCalledWith('ws-board', {
158
182
  useAdmin: true,
159
183
  });
160
184
  expect(element.type).toBe(mocks.BoardClient);
161
185
  expect(element.props.workspace).toBe(workspace);
162
186
  expect(element.props.workspaceTier).toBe('FREE');
163
187
  });
188
+
189
+ it('passes full-bleed loading through to the board client when requested', async () => {
190
+ const workspace = {
191
+ id: 'ws-board',
192
+ joined: true,
193
+ personal: false,
194
+ tier: 'FREE',
195
+ };
196
+ mocks.getWorkspaceTaskBoard.mockResolvedValue({
197
+ board: {
198
+ access_type: 'member',
199
+ id: BOARD_ID,
200
+ ws_id: 'ws-board',
201
+ },
202
+ });
203
+ mocks.getWorkspace.mockResolvedValue(workspace);
204
+
205
+ const element = await renderServerPage({ rootLoading: true });
206
+
207
+ expect(element.props.rootLoading).toBe(true);
208
+ });
164
209
  });
@@ -14,6 +14,7 @@ import { getWorkspace } from '@tuturuuu/utils/workspace-helper';
14
14
  import { headers } from 'next/headers';
15
15
  import { notFound, redirect } from 'next/navigation';
16
16
  import type { ReactNode } from 'react';
17
+ import type { ViewType } from '../../shared/board-views';
17
18
 
18
19
  interface Props {
19
20
  params: Promise<{
@@ -22,7 +23,9 @@ interface Props {
22
23
  }>;
23
24
  /** Route prefix for tasks URLs. Defaults to '/tasks' (web app). Set to '' for satellite apps. */
24
25
  routePrefix?: string;
26
+ defaultView?: ViewType;
25
27
  idleBottomIsland?: ReactNode;
28
+ rootLoading?: boolean;
26
29
  }
27
30
 
28
31
  type AuthorizedWorkspace = Workspace & {
@@ -53,7 +56,10 @@ async function getAuthorizedBoard(wsId: string, boardId: string) {
53
56
  } catch (error) {
54
57
  if (
55
58
  error instanceof InternalApiError &&
56
- (error.status === 401 || error.status === 403 || error.status === 404)
59
+ (error.status === 400 ||
60
+ error.status === 401 ||
61
+ error.status === 403 ||
62
+ error.status === 404)
57
63
  ) {
58
64
  return null;
59
65
  }
@@ -68,9 +74,11 @@ async function getAuthorizedBoard(wsId: string, boardId: string) {
68
74
  * Used by both apps/web and apps/tasks.
69
75
  */
70
76
  export default async function TaskBoardServerPage({
77
+ defaultView,
71
78
  idleBottomIsland,
72
79
  params,
73
80
  routePrefix = '/tasks',
81
+ rootLoading = false,
74
82
  }: Props) {
75
83
  const { wsId: id, boardId } = await params;
76
84
 
@@ -82,7 +90,7 @@ export default async function TaskBoardServerPage({
82
90
 
83
91
  const isMemberBoardAccess = board.access_type === 'member';
84
92
  const workspace = isMemberBoardAccess
85
- ? await getWorkspace(id, { useAdmin: true })
93
+ ? await getWorkspace(board.ws_id, { useAdmin: true })
86
94
  : createBoardGuestWorkspace(board.ws_id);
87
95
  if (!workspace) notFound();
88
96
 
@@ -92,8 +100,10 @@ export default async function TaskBoardServerPage({
92
100
  workspace={workspace}
93
101
  workspaceTier={workspace.tier ?? null}
94
102
  currentUserId={user.id}
103
+ defaultView={defaultView}
95
104
  routePrefix={routePrefix}
96
105
  idleBottomIsland={idleBottomIsland}
106
+ rootLoading={rootLoading}
97
107
  />
98
108
  );
99
109
  }
@@ -1,7 +1,7 @@
1
1
  import type { Task } from '@tuturuuu/types/primitives/Task';
2
2
  import type { TaskList } from '@tuturuuu/types/primitives/TaskList';
3
3
  import { type MouseEvent, useEffect, useRef } from 'react';
4
- import { TaskCard } from './task-card';
4
+ import { TaskCard, type TaskCardAssigneeMemberSource } from './task-card';
5
5
 
6
6
  interface MeasuredTaskCardProps {
7
7
  task: Task;
@@ -13,6 +13,8 @@ interface MeasuredTaskCardProps {
13
13
  isSelected: boolean;
14
14
  isMultiSelectMode?: boolean;
15
15
  isPersonalWorkspace?: boolean;
16
+ canUseBoardAssignees?: boolean;
17
+ assigneeMemberSource?: TaskCardAssigneeMemberSource;
16
18
  onSelect?: (taskId: string, event: MouseEvent) => void;
17
19
  onClearSelection?: () => void;
18
20
  suppressSortableTransform?: boolean;
@@ -21,6 +23,7 @@ interface MeasuredTaskCardProps {
21
23
  optimisticUpdateInProgress?: Set<string>;
22
24
  selectedTasks?: Set<string>;
23
25
  bulkUpdateCustomDueDate?: (date: Date | null) => Promise<void>;
26
+ readOnly?: boolean;
24
27
  }
25
28
 
26
29
  export function MeasuredTaskCard({
@@ -33,6 +36,8 @@ export function MeasuredTaskCard({
33
36
  isSelected,
34
37
  isMultiSelectMode,
35
38
  isPersonalWorkspace,
39
+ canUseBoardAssignees,
40
+ assigneeMemberSource,
36
41
  onSelect,
37
42
  onClearSelection,
38
43
  suppressSortableTransform,
@@ -41,6 +46,7 @@ export function MeasuredTaskCard({
41
46
  optimisticUpdateInProgress,
42
47
  selectedTasks,
43
48
  bulkUpdateCustomDueDate,
49
+ readOnly = false,
44
50
  }: MeasuredTaskCardProps) {
45
51
  const ref = useRef<HTMLDivElement | null>(null);
46
52
  const onHeightRef = useRef(onHeight);
@@ -97,12 +103,15 @@ export function MeasuredTaskCard({
97
103
  isSelected={isSelected}
98
104
  isMultiSelectMode={isMultiSelectMode}
99
105
  isPersonalWorkspace={isPersonalWorkspace}
106
+ canUseBoardAssignees={canUseBoardAssignees}
107
+ assigneeMemberSource={assigneeMemberSource}
100
108
  onSelect={onSelect}
101
109
  onClearSelection={onClearSelection}
102
110
  suppressSortableTransform={suppressSortableTransform}
103
111
  optimisticUpdateInProgress={optimisticUpdateInProgress}
104
112
  selectedTasks={selectedTasks}
105
113
  bulkUpdateCustomDueDate={bulkUpdateCustomDueDate}
114
+ readOnly={readOnly}
106
115
  />
107
116
  </div>
108
117
  );
@@ -25,6 +25,7 @@ export function areTaskCardPropsEqual(
25
25
  next: TaskCardProps
26
26
  ) {
27
27
  if (prev.isOverlay !== next.isOverlay) return false;
28
+ if (prev.readOnly !== next.readOnly) return false;
28
29
  if (prev.isSelected !== next.isSelected) return false;
29
30
  if (prev.isMultiSelectMode !== next.isMultiSelectMode) return false;
30
31
  if (
@@ -36,6 +37,8 @@ export function areTaskCardPropsEqual(
36
37
  }
37
38
  if (prev.boardId !== next.boardId) return false;
38
39
  if (prev.workspaceId !== next.workspaceId) return false;
40
+ if (prev.deadlineContext !== next.deadlineContext) return false;
41
+ if (prev.deadlineNow !== next.deadlineNow) return false;
39
42
  if (prev.dragDisabled !== next.dragDisabled) return false;
40
43
  if (prev.sortableId !== next.sortableId) return false;
41
44
  if (prev.suppressSortableTransform !== next.suppressSortableTransform) {
@@ -75,6 +75,8 @@ describe('getTaskCardHydratingOpenOptions', () => {
75
75
  ],
76
76
  taskWsId: 'source-workspace',
77
77
  taskWorkspacePersonal: false,
78
+ canUseBoardAssignees: true,
79
+ assigneeMemberSource: 'workspace',
78
80
  initialSharedContext: {
79
81
  boardConfig: {
80
82
  id: 'source-board',
@@ -118,10 +120,28 @@ describe('getTaskCardHydratingOpenOptions', () => {
118
120
  availableLists: [list],
119
121
  taskWsId: 'workspace-1',
120
122
  taskWorkspacePersonal: true,
123
+ canUseBoardAssignees: false,
124
+ assigneeMemberSource: undefined,
121
125
  initialSharedContext: undefined,
122
126
  });
123
127
  });
124
128
 
129
+ it('can keep assignees enabled for shared personal board tasks', () => {
130
+ expect(
131
+ getTaskCardHydratingOpenOptions({
132
+ task,
133
+ boardId: 'board-1',
134
+ availableLists: [list],
135
+ canUseBoardAssignees: true,
136
+ effectiveWorkspaceId: 'workspace-1',
137
+ isPersonalWorkspace: true,
138
+ })
139
+ ).toMatchObject({
140
+ canUseBoardAssignees: true,
141
+ taskWorkspacePersonal: true,
142
+ });
143
+ });
144
+
125
145
  it('treats source metadata as an external task snapshot', () => {
126
146
  expect(
127
147
  isExternalTaskSnapshot({
@@ -6,6 +6,8 @@ interface TaskCardOpenOptionsInput {
6
6
  task: Task;
7
7
  boardId: string;
8
8
  availableLists?: TaskList[];
9
+ assigneeMemberSource?: 'workspace' | 'board' | 'workspace-and-board';
10
+ canUseBoardAssignees?: boolean;
9
11
  effectiveWorkspaceId?: string;
10
12
  isPersonalWorkspace: boolean;
11
13
  }
@@ -96,6 +98,8 @@ export function getTaskCardHydratingOpenOptions({
96
98
  task,
97
99
  boardId,
98
100
  availableLists,
101
+ assigneeMemberSource,
102
+ canUseBoardAssignees,
99
103
  effectiveWorkspaceId,
100
104
  isPersonalWorkspace,
101
105
  }: TaskCardOpenOptionsInput) {
@@ -110,6 +114,9 @@ export function getTaskCardHydratingOpenOptions({
110
114
  ...task,
111
115
  list_id: task.source_list_id ?? task.list_id,
112
116
  };
117
+ const sourceBoardAssigneesEnabled = sourceWorkspaceId
118
+ ? true
119
+ : !isPersonalWorkspace;
113
120
 
114
121
  return {
115
122
  initialTask,
@@ -122,6 +129,9 @@ export function getTaskCardHydratingOpenOptions({
122
129
  : availableLists,
123
130
  taskWsId: sourceWorkspaceId ?? effectiveWorkspaceId,
124
131
  taskWorkspacePersonal: sourceWorkspaceId ? false : isPersonalWorkspace,
132
+ canUseBoardAssignees: canUseBoardAssignees ?? sourceBoardAssigneesEnabled,
133
+ assigneeMemberSource:
134
+ assigneeMemberSource ?? (sourceWorkspaceId ? 'workspace' : undefined),
125
135
  initialSharedContext,
126
136
  };
127
137
  }