@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
@@ -0,0 +1,374 @@
1
+ import '@testing-library/jest-dom';
2
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
3
+ import { fireEvent, render, screen, waitFor } from '@testing-library/react';
4
+ import { beforeEach, describe, expect, it, vi } from 'vitest';
5
+ import { BoardSwitcher } from '../board-switcher';
6
+
7
+ const {
8
+ createWorkspaceTaskBoardMock,
9
+ isTaskRememberLastBoardEnabledMock,
10
+ listCurrentUserTaskBoardsMock,
11
+ pushMock,
12
+ rememberLastBoardConfig,
13
+ updateUserWorkspaceConfigMock,
14
+ useUserWorkspaceConfigMock,
15
+ } = vi.hoisted(() => ({
16
+ createWorkspaceTaskBoardMock: vi.fn(),
17
+ isTaskRememberLastBoardEnabledMock: vi.fn(
18
+ (value: string | null | undefined) => value !== 'false'
19
+ ),
20
+ listCurrentUserTaskBoardsMock: vi.fn(),
21
+ pushMock: vi.fn(),
22
+ rememberLastBoardConfig: {
23
+ value: 'true' as string | null | undefined,
24
+ },
25
+ updateUserWorkspaceConfigMock: vi.fn(),
26
+ useUserWorkspaceConfigMock: vi.fn(),
27
+ }));
28
+
29
+ let comboboxProps:
30
+ | {
31
+ createText?: string;
32
+ creatingText?: string;
33
+ onChange: (value: string) => void;
34
+ onCreate?: (value: string) => Promise<{ label: string; value: string }>;
35
+ options: Array<{
36
+ badge?: unknown;
37
+ description?: string;
38
+ group?: string;
39
+ icon?: unknown;
40
+ label: string;
41
+ value: string;
42
+ }>;
43
+ searchPlaceholder?: string;
44
+ selected?: string;
45
+ showSelectedIcon?: boolean;
46
+ }
47
+ | undefined;
48
+
49
+ vi.mock('@tuturuuu/internal-api/tasks', () => ({
50
+ createWorkspaceTaskBoard: (
51
+ ...args: Parameters<typeof createWorkspaceTaskBoardMock>
52
+ ) => createWorkspaceTaskBoardMock(...args),
53
+ listCurrentUserTaskBoards: (
54
+ ...args: Parameters<typeof listCurrentUserTaskBoardsMock>
55
+ ) => listCurrentUserTaskBoardsMock(...args),
56
+ }));
57
+
58
+ vi.mock('@tuturuuu/internal-api/users', () => ({
59
+ isTaskRememberLastBoardEnabled: (value: string | null | undefined): boolean =>
60
+ isTaskRememberLastBoardEnabledMock(value),
61
+ TASK_DEFAULT_BOARD_ID_CONFIG_ID: 'TASK_DEFAULT_BOARD_ID',
62
+ TASK_REMEMBER_LAST_BOARD_CONFIG_ID: 'TASK_REMEMBER_LAST_BOARD',
63
+ }));
64
+
65
+ vi.mock('../../../../../hooks/use-user-workspace-config', () => ({
66
+ useUpdateUserWorkspaceConfig: () => ({
67
+ isPending: false,
68
+ mutate: updateUserWorkspaceConfigMock,
69
+ }),
70
+ useUserWorkspaceConfig: (...args: unknown[]) => {
71
+ useUserWorkspaceConfigMock(...args);
72
+ return {
73
+ data: rememberLastBoardConfig.value,
74
+ isLoading: false,
75
+ };
76
+ },
77
+ }));
78
+
79
+ vi.mock('@tuturuuu/ui/custom/combobox', () => ({
80
+ Combobox: (props: any) => {
81
+ comboboxProps = props;
82
+ return (
83
+ <div>
84
+ <button
85
+ type="button"
86
+ data-testid="board-combobox"
87
+ onClick={() => props.onChange('board-2')}
88
+ >
89
+ {props.label}
90
+ </button>
91
+ <button
92
+ type="button"
93
+ data-testid="create-board"
94
+ onClick={async () => {
95
+ const result = await props.onCreate?.('Launch Board');
96
+ const boardId = typeof result === 'string' ? result : result?.value;
97
+ if (boardId) props.onChange(boardId);
98
+ }}
99
+ >
100
+ Create board
101
+ </button>
102
+ </div>
103
+ );
104
+ },
105
+ }));
106
+
107
+ vi.mock('@tuturuuu/ui/sonner', () => ({
108
+ toast: {
109
+ error: vi.fn(),
110
+ },
111
+ }));
112
+
113
+ vi.mock('next/navigation', () => ({
114
+ useRouter: () => ({
115
+ push: pushMock,
116
+ }),
117
+ }));
118
+
119
+ vi.mock('../tasks-route-context', () => ({
120
+ useTasksHref: () => (path: string) => `/tasks${path}`,
121
+ }));
122
+
123
+ function renderBoardSwitcher() {
124
+ const queryClient = new QueryClient({
125
+ defaultOptions: {
126
+ queries: {
127
+ retry: false,
128
+ },
129
+ },
130
+ });
131
+
132
+ return render(
133
+ <QueryClientProvider client={queryClient}>
134
+ <BoardSwitcher
135
+ board={{
136
+ id: 'board-1',
137
+ name: 'Tasks',
138
+ ticket_prefix: 'T',
139
+ ws_id: 'ws-1',
140
+ }}
141
+ translations={{
142
+ activeBoards: 'Active boards',
143
+ archivedBoards: 'Archived boards',
144
+ deletedBoards: 'Deleted boards',
145
+ createBoard: 'Create Board',
146
+ creatingBoard: 'Creating',
147
+ searchBoards: 'Search boards...',
148
+ tasks: 'Tasks',
149
+ }}
150
+ />
151
+ </QueryClientProvider>
152
+ );
153
+ }
154
+
155
+ describe('BoardSwitcher', () => {
156
+ beforeEach(() => {
157
+ vi.clearAllMocks();
158
+ comboboxProps = undefined;
159
+ rememberLastBoardConfig.value = 'true';
160
+ listCurrentUserTaskBoardsMock.mockResolvedValue({
161
+ boards: [
162
+ {
163
+ access_type: 'member',
164
+ archived_at: null,
165
+ created_at: '2026-06-01T00:00:00.000Z',
166
+ deleted_at: null,
167
+ icon: null,
168
+ id: 'board-2',
169
+ name: 'Roadmap',
170
+ ticket_prefix: null,
171
+ workspace: {
172
+ avatar_url: null,
173
+ id: 'ws-1',
174
+ logo_url: null,
175
+ name: 'Current Workspace',
176
+ personal: false,
177
+ },
178
+ ws_id: 'ws-1',
179
+ },
180
+ ],
181
+ });
182
+ });
183
+
184
+ it('uses the shared combobox and navigates when a board is selected', async () => {
185
+ renderBoardSwitcher();
186
+
187
+ expect(screen.getByTestId('board-combobox')).toBeInTheDocument();
188
+
189
+ await waitFor(() => {
190
+ expect(comboboxProps?.options).toEqual(
191
+ expect.arrayContaining([
192
+ expect.objectContaining({
193
+ label: 'Roadmap',
194
+ value: 'board-2',
195
+ }),
196
+ ])
197
+ );
198
+ });
199
+
200
+ expect(comboboxProps).toMatchObject({
201
+ createText: 'Create Board',
202
+ creatingText: 'Creating',
203
+ searchPlaceholder: 'Search boards...',
204
+ selected: 'board-1',
205
+ });
206
+ expect(comboboxProps?.showSelectedIcon).toBeUndefined();
207
+ expect(comboboxProps?.options.some((option) => option.icon)).toBe(true);
208
+ const activeBoardOption = comboboxProps?.options.find(
209
+ (option) => option.value === 'board-2'
210
+ );
211
+ expect(activeBoardOption?.badge).toBeUndefined();
212
+ expect(activeBoardOption?.description).toBeUndefined();
213
+ expect(activeBoardOption?.group).toBe('Current Workspace');
214
+
215
+ fireEvent.click(screen.getByTestId('board-combobox'));
216
+
217
+ expect(pushMock).toHaveBeenCalledWith('/ws-1/tasks/boards/board-2');
218
+ expect(useUserWorkspaceConfigMock).toHaveBeenCalledWith(
219
+ 'ws-1',
220
+ 'TASK_REMEMBER_LAST_BOARD',
221
+ 'true'
222
+ );
223
+ expect(isTaskRememberLastBoardEnabledMock).toHaveBeenCalledWith('true');
224
+ expect(updateUserWorkspaceConfigMock).toHaveBeenCalledWith({
225
+ configId: 'TASK_DEFAULT_BOARD_ID',
226
+ value: 'board-2',
227
+ workspaceId: 'ws-1',
228
+ });
229
+ });
230
+
231
+ it('orders current workspace boards first and switches across workspaces without updating defaults', async () => {
232
+ listCurrentUserTaskBoardsMock.mockResolvedValue({
233
+ boards: [
234
+ {
235
+ access_type: 'member',
236
+ archived_at: null,
237
+ created_at: '2026-06-01T00:00:00.000Z',
238
+ deleted_at: null,
239
+ icon: null,
240
+ id: 'board-2',
241
+ name: 'External Roadmap',
242
+ ticket_prefix: null,
243
+ workspace: {
244
+ avatar_url: null,
245
+ id: 'ws-2',
246
+ logo_url: null,
247
+ name: 'Other Workspace',
248
+ personal: false,
249
+ },
250
+ ws_id: 'ws-2',
251
+ },
252
+ {
253
+ access_type: 'member',
254
+ archived_at: null,
255
+ created_at: '2026-06-01T00:00:00.000Z',
256
+ deleted_at: null,
257
+ icon: null,
258
+ id: 'board-current-secondary',
259
+ name: 'Current Roadmap',
260
+ ticket_prefix: null,
261
+ workspace: {
262
+ avatar_url: null,
263
+ id: 'ws-1',
264
+ logo_url: null,
265
+ name: 'Current Workspace',
266
+ personal: false,
267
+ },
268
+ ws_id: 'ws-1',
269
+ },
270
+ ],
271
+ });
272
+
273
+ renderBoardSwitcher();
274
+
275
+ await waitFor(() => {
276
+ expect(comboboxProps?.options.map((option) => option.value)).toEqual([
277
+ 'board-1',
278
+ 'board-current-secondary',
279
+ 'board-2',
280
+ ]);
281
+ });
282
+ expect(comboboxProps?.options[2]).toMatchObject({
283
+ group: 'Other Workspace',
284
+ label: 'External Roadmap',
285
+ value: 'board-2',
286
+ });
287
+
288
+ fireEvent.click(screen.getByTestId('board-combobox'));
289
+
290
+ expect(pushMock).toHaveBeenCalledWith('/ws-2/tasks/boards/board-2');
291
+ expect(updateUserWorkspaceConfigMock).not.toHaveBeenCalled();
292
+ });
293
+
294
+ it('navigates without updating the default board when board memory is disabled', async () => {
295
+ rememberLastBoardConfig.value = 'false';
296
+ renderBoardSwitcher();
297
+
298
+ await waitFor(() => {
299
+ expect(comboboxProps?.options.length).toBeGreaterThan(0);
300
+ });
301
+
302
+ fireEvent.click(screen.getByTestId('board-combobox'));
303
+
304
+ expect(pushMock).toHaveBeenCalledWith('/ws-1/tasks/boards/board-2');
305
+ expect(updateUserWorkspaceConfigMock).not.toHaveBeenCalled();
306
+ });
307
+
308
+ it('hides board creation for guest-only board access', async () => {
309
+ listCurrentUserTaskBoardsMock.mockResolvedValue({
310
+ boards: [
311
+ {
312
+ access_type: 'guest',
313
+ archived_at: null,
314
+ created_at: '2026-06-01T00:00:00.000Z',
315
+ deleted_at: null,
316
+ guest_permission: 'edit',
317
+ icon: null,
318
+ id: 'board-1',
319
+ name: 'Tasks',
320
+ ticket_prefix: null,
321
+ workspace: {
322
+ avatar_url: null,
323
+ guest_products: ['tasks'],
324
+ id: 'ws-1',
325
+ logo_url: null,
326
+ name: 'Shared Workspace',
327
+ personal: false,
328
+ },
329
+ ws_id: 'ws-1',
330
+ },
331
+ ],
332
+ });
333
+
334
+ renderBoardSwitcher();
335
+
336
+ await waitFor(() => {
337
+ expect(comboboxProps?.createText).toBeUndefined();
338
+ expect(comboboxProps?.creatingText).toBeUndefined();
339
+ expect(comboboxProps?.onCreate).toBeUndefined();
340
+ });
341
+ });
342
+
343
+ it('creates a new board from the picker and opens it', async () => {
344
+ createWorkspaceTaskBoardMock.mockResolvedValue({
345
+ board: {
346
+ archived_at: null,
347
+ created_at: '2026-06-24T00:00:00.000Z',
348
+ deleted_at: null,
349
+ icon: null,
350
+ id: 'board-3',
351
+ name: 'Launch Board',
352
+ },
353
+ });
354
+
355
+ renderBoardSwitcher();
356
+
357
+ fireEvent.click(screen.getByTestId('create-board'));
358
+
359
+ await waitFor(() => {
360
+ expect(createWorkspaceTaskBoardMock).toHaveBeenCalledWith('ws-1', {
361
+ name: 'Launch Board',
362
+ });
363
+ });
364
+
365
+ await waitFor(() => {
366
+ expect(pushMock).toHaveBeenCalledWith('/ws-1/tasks/boards/board-3');
367
+ });
368
+ expect(updateUserWorkspaceConfigMock).toHaveBeenCalledWith({
369
+ configId: 'TASK_DEFAULT_BOARD_ID',
370
+ value: 'board-3',
371
+ workspaceId: 'ws-1',
372
+ });
373
+ });
374
+ });