@tuturuuu/ui 0.7.0 → 0.9.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 (226) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/biome.json +1 -1
  3. package/package.json +75 -73
  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/currency-input.test.tsx +43 -0
  29. package/src/components/ui/currency-input.tsx +1 -1
  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 +19 -0
  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/settings-dialog-shell.tsx +2 -1
  40. package/src/components/ui/custom/theme-toggle.tsx +1 -1
  41. package/src/components/ui/custom/workspace-access/workspace-access-default-role-card.tsx +60 -35
  42. package/src/components/ui/custom/workspace-access/workspace-access-member-row.tsx +176 -167
  43. package/src/components/ui/custom/workspace-access/workspace-access-members.tsx +16 -10
  44. package/src/components/ui/custom/workspace-access/workspace-access-page-header.tsx +75 -36
  45. package/src/components/ui/custom/workspace-access/workspace-access-page.tsx +39 -42
  46. package/src/components/ui/custom/workspace-access/workspace-access-people-filters.tsx +1 -1
  47. package/src/components/ui/custom/workspace-access/workspace-access-roles.tsx +113 -91
  48. package/src/components/ui/custom/workspace-access/workspace-access-tabs-toolbar.tsx +73 -32
  49. package/src/components/ui/custom/workspace-select.tsx +8 -3
  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 +3 -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-card.tsx +21 -9
  69. package/src/components/ui/finance/transactions/transaction-edit-dialog.tsx +1 -4
  70. package/src/components/ui/finance/transactions/transactions-create-summary.tsx +3 -0
  71. package/src/components/ui/finance/transactions/transactions-page.tsx +4 -1
  72. package/src/components/ui/finance/wallets/form.test.tsx +51 -3
  73. package/src/components/ui/finance/wallets/form.tsx +15 -4
  74. package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +4 -0
  75. package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +4 -2
  76. package/src/components/ui/finance/wallets/wallets-data-table.tsx +1 -0
  77. package/src/components/ui/finance/wallets/wallets-page.tsx +5 -2
  78. package/src/components/ui/input-otp.tsx +1 -1
  79. package/src/components/ui/legacy/calendar/all-day-event-bar.tsx +28 -39
  80. package/src/components/ui/legacy/calendar/calendar-cell.tsx +2 -0
  81. package/src/components/ui/legacy/calendar/calendar-content.tsx +10 -6
  82. package/src/components/ui/legacy/calendar/calendar-header.tsx +23 -3
  83. package/src/components/ui/legacy/calendar/calendar-loading-skeleton.tsx +135 -0
  84. package/src/components/ui/legacy/calendar/calendar-matrix.tsx +175 -237
  85. package/src/components/ui/legacy/calendar/event-card.test.tsx +177 -0
  86. package/src/components/ui/legacy/calendar/event-card.tsx +220 -131
  87. package/src/components/ui/legacy/calendar/event-modal.tsx +17 -17
  88. package/src/components/ui/legacy/calendar/event-provider-display.tsx +69 -0
  89. package/src/components/ui/legacy/calendar/smart-calendar.test.tsx +86 -4
  90. package/src/components/ui/legacy/calendar/smart-calendar.tsx +32 -2
  91. package/src/components/ui/legacy/meet/create-plan-dialog.tsx +19 -10
  92. package/src/components/ui/money-input.test.tsx +64 -0
  93. package/src/components/ui/money-input.tsx +63 -0
  94. package/src/components/ui/navigation-menu.tsx +1 -1
  95. package/src/components/ui/pagination.tsx +1 -1
  96. package/src/components/ui/radio-group.tsx +1 -1
  97. package/src/components/ui/select.tsx +5 -1
  98. package/src/components/ui/sheet.tsx +1 -1
  99. package/src/components/ui/sidebar.tsx +1 -1
  100. package/src/components/ui/storefront/cart-popover.tsx +61 -0
  101. package/src/components/ui/storefront/cart-summary-parts.tsx +290 -0
  102. package/src/components/ui/storefront/cart-summary.tsx +104 -80
  103. package/src/components/ui/storefront/checkout-overlay.tsx +26 -0
  104. package/src/components/ui/storefront/hero-panel.tsx +2 -8
  105. package/src/components/ui/storefront/image-panel.tsx +6 -0
  106. package/src/components/ui/storefront/index.ts +11 -0
  107. package/src/components/ui/storefront/listing-card.tsx +84 -22
  108. package/src/components/ui/storefront/merch-sections.tsx +70 -0
  109. package/src/components/ui/storefront/product-detail.tsx +289 -0
  110. package/src/components/ui/storefront/product-dialog.tsx +72 -0
  111. package/src/components/ui/storefront/storefront-surface.test.tsx +221 -3
  112. package/src/components/ui/storefront/storefront-surface.tsx +288 -153
  113. package/src/components/ui/storefront/types.ts +27 -1
  114. package/src/components/ui/storefront/utils.ts +117 -27
  115. package/src/components/ui/text-editor/__tests__/content-migration.test.ts +32 -0
  116. package/src/components/ui/text-editor/__tests__/extensions.test.ts +123 -0
  117. package/src/components/ui/text-editor/__tests__/image-extension.test.ts +69 -1
  118. package/src/components/ui/text-editor/__tests__/video-extension.test.ts +47 -0
  119. package/src/components/ui/text-editor/background-color-extension.ts +62 -0
  120. package/src/components/ui/text-editor/color-controls.tsx +284 -0
  121. package/src/components/ui/text-editor/content-migration.ts +41 -18
  122. package/src/components/ui/text-editor/editor.tsx +69 -14
  123. package/src/components/ui/text-editor/extensions.ts +9 -3
  124. package/src/components/ui/text-editor/highlight-extension.ts +22 -0
  125. package/src/components/ui/text-editor/image-extension.ts +40 -18
  126. package/src/components/ui/text-editor/tool-bar.tsx +9 -16
  127. package/src/components/ui/text-editor/video-extension.ts +11 -2
  128. package/src/components/ui/toast.tsx +1 -1
  129. package/src/components/ui/tu-do/boards/__tests__/board-share-dialog.test.tsx +270 -0
  130. package/src/components/ui/tu-do/boards/__tests__/workspace-projects-client-page.test.tsx +70 -1
  131. package/src/components/ui/tu-do/boards/board-public-link-section.tsx +231 -0
  132. package/src/components/ui/tu-do/boards/board-share-dialog.tsx +222 -109
  133. package/src/components/ui/tu-do/boards/boardId/board-column-external-retry.test.tsx +127 -0
  134. package/src/components/ui/tu-do/boards/boardId/board-column.tsx +113 -46
  135. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-clear-delete.ts +2 -0
  136. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-move.ts +5 -0
  137. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-updates.ts +3 -0
  138. package/src/components/ui/tu-do/boards/boardId/kanban/data/kanban-deadline-query.ts +50 -2
  139. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/__tests__/column-reorder.test.ts +17 -0
  140. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/column-reorder.ts +4 -1
  141. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-cache.ts +51 -9
  142. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-drag-order.ts +2 -8
  143. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/task-sort-key.ts +47 -0
  144. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.test.ts +63 -0
  145. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +127 -38
  146. package/src/components/ui/tu-do/boards/boardId/kanban/planner/__tests__/kanban-planner-island.test.tsx +380 -0
  147. package/src/components/ui/tu-do/boards/boardId/kanban/planner/kanban-planner-dialog.tsx +204 -0
  148. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-digest-panel.tsx +61 -0
  149. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-item-strip.tsx +54 -0
  150. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-plan-toolbar.tsx +251 -0
  151. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-scope-badge.tsx +27 -0
  152. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-section.tsx +58 -0
  153. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-share-dialog.tsx +238 -0
  154. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-target-controls.tsx +143 -0
  155. package/src/components/ui/tu-do/boards/boardId/kanban/planner/planner-utils.ts +65 -0
  156. package/src/components/ui/tu-do/boards/boardId/kanban/planner/use-kanban-planner-state.ts +234 -0
  157. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +410 -4
  158. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +106 -14
  159. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-deadline-panels.tsx +443 -19
  160. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-skeleton.tsx +94 -32
  161. package/src/components/ui/tu-do/boards/boardId/kanban.tsx +213 -106
  162. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.test.tsx +186 -0
  163. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +59 -2
  164. package/src/components/ui/tu-do/boards/boardId/task-card/measured-task-card.tsx +3 -0
  165. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.ts +3 -0
  166. package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +191 -28
  167. package/src/components/ui/tu-do/boards/boardId/task-filter.test.tsx +152 -0
  168. package/src/components/ui/tu-do/boards/boardId/task-filter.tsx +555 -545
  169. package/src/components/ui/tu-do/boards/boardId/task-list.tsx +7 -0
  170. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-display.ts +9 -0
  171. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-grid.tsx +8 -16
  172. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-task-row.tsx +5 -25
  173. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-utils.test.ts +36 -1
  174. package/src/components/ui/tu-do/boards/boardId/timeline/timeline-utils.ts +51 -2
  175. package/src/components/ui/tu-do/boards/share-section.tsx +100 -0
  176. package/src/components/ui/tu-do/boards/workspace-projects-client-page.tsx +13 -3
  177. package/src/components/ui/tu-do/drafts/draft-convert-dialog.tsx +10 -12
  178. package/src/components/ui/tu-do/drafts/drafts-page.tsx +33 -16
  179. package/src/components/ui/tu-do/initiatives/task-initiatives-client.tsx +56 -88
  180. package/src/components/ui/tu-do/my-tasks/my-tasks-content.tsx +26 -2
  181. package/src/components/ui/tu-do/my-tasks/use-my-tasks-state.ts +55 -8
  182. package/src/components/ui/tu-do/notes/note-edit-dialog.tsx +1 -4
  183. package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +25 -0
  184. package/src/components/ui/tu-do/shared/__tests__/board-header.test.tsx +341 -38
  185. package/src/components/ui/tu-do/shared/__tests__/board-switcher.test.tsx +253 -0
  186. package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +237 -3
  187. package/src/components/ui/tu-do/shared/__tests__/task-board-loading-state.test.tsx +17 -0
  188. package/src/components/ui/tu-do/shared/__tests__/task-legacy-route-recovery.test.tsx +16 -0
  189. package/src/components/ui/tu-do/shared/board-client.tsx +2 -7
  190. package/src/components/ui/tu-do/shared/board-config-storage.ts +7 -1
  191. package/src/components/ui/tu-do/shared/board-header.tsx +465 -937
  192. package/src/components/ui/tu-do/shared/board-layout-settings.tsx +165 -136
  193. package/src/components/ui/tu-do/shared/board-switcher.tsx +209 -217
  194. package/src/components/ui/tu-do/shared/board-views.tsx +596 -82
  195. package/src/components/ui/tu-do/shared/cursor-overlay-multi-wrapper.tsx +53 -12
  196. package/src/components/ui/tu-do/shared/list-view.tsx +227 -1
  197. package/src/components/ui/tu-do/shared/recycle-bin-panel.tsx +142 -94
  198. package/src/components/ui/tu-do/shared/special-task-list-pins.ts +51 -0
  199. package/src/components/ui/tu-do/shared/task-board-loading-state.tsx +28 -0
  200. package/src/components/ui/tu-do/shared/task-dialog-presentation.test.ts +53 -0
  201. package/src/components/ui/tu-do/shared/task-dialog-presentation.ts +19 -0
  202. package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.test.tsx +57 -0
  203. package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.tsx +136 -111
  204. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-description-editor.tsx +3 -1
  205. package/src/components/ui/tu-do/shared/task-edit-dialog/field-diff-viewer.tsx +3 -2
  206. package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.test.tsx +91 -0
  207. package/src/components/ui/tu-do/shared/task-edit-dialog/selective-revert-panel.tsx +123 -78
  208. package/src/components/ui/tu-do/shared/task-edit-dialog/task-activity-section.tsx +7 -1
  209. package/src/components/ui/tu-do/shared/task-edit-dialog/task-snapshot-dialog.tsx +8 -3
  210. package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +44 -15
  211. package/src/components/ui/tu-do/shared/task-legacy-route-recovery.tsx +2 -9
  212. package/src/declarations.d.ts +1 -0
  213. package/src/hooks/__tests__/use-calendar-readonly.test.tsx +322 -2
  214. package/src/hooks/__tests__/use-calendar-sync.test.tsx +446 -0
  215. package/src/hooks/__tests__/useBoardRealtime.test.tsx +2 -2
  216. package/src/hooks/__tests__/useCursorTracking.test.tsx +212 -0
  217. package/src/hooks/use-calendar-sync.tsx +247 -243
  218. package/src/hooks/use-calendar.tsx +323 -138
  219. package/src/hooks/use-task-actions.ts +24 -0
  220. package/src/hooks/use-user-workspace-config.ts +75 -0
  221. package/src/hooks/use-workspace-currency.ts +8 -3
  222. package/src/hooks/useBoardRealtime.ts +6 -3
  223. package/src/hooks/useBoardRealtime.types.ts +11 -0
  224. package/src/hooks/useBoardRealtimeEventHandler.ts +11 -0
  225. package/src/hooks/useCursorTracking.ts +91 -27
  226. package/src/hooks/useTaskUserRealtime.ts +5 -3
@@ -13,6 +13,15 @@ import {
13
13
  Trash2,
14
14
  User,
15
15
  } from '@tuturuuu/icons';
16
+ import {
17
+ createWorkspaceTaskInitiative,
18
+ deleteWorkspaceTaskInitiative,
19
+ linkWorkspaceTaskInitiativeProject,
20
+ listWorkspaceTaskInitiatives,
21
+ listWorkspaceTaskProjects,
22
+ unlinkWorkspaceTaskInitiativeProject,
23
+ updateWorkspaceTaskInitiative,
24
+ } from '@tuturuuu/internal-api/tasks';
16
25
  import { Badge } from '@tuturuuu/ui/badge';
17
26
  import { Button } from '@tuturuuu/ui/button';
18
27
  import {
@@ -98,6 +107,12 @@ const STATUS_BADGE_CLASS: Record<InitiativeStatus, string> = {
98
107
  cancelled: 'bg-dynamic-red/15 text-dynamic-red border-transparent',
99
108
  };
100
109
 
110
+ function getBrowserInternalApiOptions() {
111
+ return typeof window !== 'undefined'
112
+ ? { baseUrl: window.location.origin }
113
+ : undefined;
114
+ }
115
+
101
116
  export function TaskInitiativesClient({
102
117
  wsId,
103
118
  initialInitiatives,
@@ -136,16 +151,8 @@ export function TaskInitiativesClient({
136
151
  refetch: refetchInitiatives,
137
152
  } = useQuery<TaskInitiative[]>({
138
153
  queryKey: ['workspace', wsId, 'task-initiatives'],
139
- queryFn: async () => {
140
- const response = await fetch(
141
- `/api/v1/workspaces/${wsId}/task-initiatives`,
142
- { cache: 'no-store' }
143
- );
144
- if (!response.ok) {
145
- throw new Error(t('errors.fetch_initiatives'));
146
- }
147
- return response.json();
148
- },
154
+ queryFn: () =>
155
+ listWorkspaceTaskInitiatives(wsId, getBrowserInternalApiOptions()),
149
156
  initialData: initialInitiatives,
150
157
  staleTime: 30_000,
151
158
  });
@@ -157,20 +164,12 @@ export function TaskInitiativesClient({
157
164
  } = useQuery<TaskProjectOption[]>({
158
165
  queryKey: ['workspace', wsId, 'task-projects-for-initiatives'],
159
166
  queryFn: async () => {
160
- const response = await fetch(`/api/v1/workspaces/${wsId}/task-projects`, {
161
- cache: 'no-store',
162
- });
163
- if (!response.ok) {
164
- throw new Error(t('errors.fetch_projects'));
165
- }
166
- const rawProjects = await response.json();
167
- return (
168
- rawProjects as Array<{
169
- id: string;
170
- name: string;
171
- status?: string | null;
172
- }>
173
- ).map((project) => ({
167
+ const rawProjects = await listWorkspaceTaskProjects(
168
+ wsId,
169
+ getBrowserInternalApiOptions()
170
+ );
171
+
172
+ return rawProjects.map((project) => ({
174
173
  id: project.id,
175
174
  name: project.name,
176
175
  status: project.status ?? null,
@@ -218,19 +217,13 @@ export function TaskInitiativesClient({
218
217
  description?: string;
219
218
  status: InitiativeStatus;
220
219
  }) => {
221
- const response = await fetch(
222
- `/api/v1/workspaces/${wsId}/task-initiatives`,
223
- {
224
- method: 'POST',
225
- headers: { 'Content-Type': 'application/json' },
226
- body: JSON.stringify({ name, description, status }),
227
- }
220
+ const payload = { name, description: description ?? '', status };
221
+
222
+ return createWorkspaceTaskInitiative(
223
+ wsId,
224
+ payload,
225
+ getBrowserInternalApiOptions()
228
226
  );
229
- if (!response.ok) {
230
- const errorData = await response.json().catch(() => ({}));
231
- throw new Error(errorData.error || t('errors.create_initiative'));
232
- }
233
- return response.json();
234
227
  },
235
228
  onSuccess: () => {
236
229
  toast.success(t('success.initiative_created'));
@@ -257,19 +250,14 @@ export function TaskInitiativesClient({
257
250
  description?: string;
258
251
  status: InitiativeStatus;
259
252
  }) => {
260
- const response = await fetch(
261
- `/api/v1/workspaces/${wsId}/task-initiatives/${initiativeId}`,
262
- {
263
- method: 'PUT',
264
- headers: { 'Content-Type': 'application/json' },
265
- body: JSON.stringify({ name, description, status }),
266
- }
253
+ const payload = { name, description: description ?? '', status };
254
+
255
+ return updateWorkspaceTaskInitiative(
256
+ wsId,
257
+ initiativeId,
258
+ payload,
259
+ getBrowserInternalApiOptions()
267
260
  );
268
- if (!response.ok) {
269
- const errorData = await response.json().catch(() => ({}));
270
- throw new Error(errorData.error || t('errors.update_initiative'));
271
- }
272
- return response.json();
273
261
  },
274
262
  onSuccess: () => {
275
263
  toast.success(t('success.initiative_updated'));
@@ -286,18 +274,12 @@ export function TaskInitiativesClient({
286
274
  });
287
275
 
288
276
  const deleteInitiativeMutation = useMutation({
289
- mutationFn: async (initiativeId: string) => {
290
- const response = await fetch(
291
- `/api/v1/workspaces/${wsId}/task-initiatives/${initiativeId}`,
292
- {
293
- method: 'DELETE',
294
- }
295
- );
296
- if (!response.ok) {
297
- const errorData = await response.json().catch(() => ({}));
298
- throw new Error(errorData.error || t('errors.delete_initiative'));
299
- }
300
- },
277
+ mutationFn: (initiativeId: string) =>
278
+ deleteWorkspaceTaskInitiative(
279
+ wsId,
280
+ initiativeId,
281
+ getBrowserInternalApiOptions()
282
+ ),
301
283
  onSuccess: () => {
302
284
  toast.success(t('success.initiative_deleted'));
303
285
  refetchInitiatives();
@@ -314,21 +296,13 @@ export function TaskInitiativesClient({
314
296
  }: {
315
297
  initiativeId: string;
316
298
  projectId: string;
317
- }) => {
318
- const response = await fetch(
319
- `/api/v1/workspaces/${wsId}/task-initiatives/${initiativeId}/projects`,
320
- {
321
- method: 'POST',
322
- headers: { 'Content-Type': 'application/json' },
323
- body: JSON.stringify({ projectId }),
324
- }
325
- );
326
- if (!response.ok) {
327
- const errorData = await response.json().catch(() => ({}));
328
- throw new Error(errorData.error || t('errors.link_project'));
329
- }
330
- return response.json();
331
- },
299
+ }) =>
300
+ linkWorkspaceTaskInitiativeProject(
301
+ wsId,
302
+ initiativeId,
303
+ projectId,
304
+ getBrowserInternalApiOptions()
305
+ ),
332
306
  onSuccess: (_data, variables) => {
333
307
  toast.success(t('success.project_linked'));
334
308
  setProjectToLink('');
@@ -362,19 +336,13 @@ export function TaskInitiativesClient({
362
336
  }: {
363
337
  initiativeId: string;
364
338
  projectId: string;
365
- }) => {
366
- const response = await fetch(
367
- `/api/v1/workspaces/${wsId}/task-initiatives/${initiativeId}/projects/${projectId}`,
368
- {
369
- method: 'DELETE',
370
- }
371
- );
372
- if (!response.ok) {
373
- const errorData = await response.json().catch(() => ({}));
374
- throw new Error(errorData.error || t('errors.unlink_project'));
375
- }
376
- return response.json();
377
- },
339
+ }) =>
340
+ unlinkWorkspaceTaskInitiativeProject(
341
+ wsId,
342
+ initiativeId,
343
+ projectId,
344
+ getBrowserInternalApiOptions()
345
+ ),
378
346
  onSuccess: (_data, variables) => {
379
347
  toast.success(t('success.project_unlinked'));
380
348
  if (variables) {
@@ -20,12 +20,30 @@ import { TaskPreviewDialog } from './task-preview-dialog';
20
20
  import { useMyTasksState } from './use-my-tasks-state';
21
21
 
22
22
  interface MyTasksContentProps {
23
+ disableAutoCreateBoard?: boolean;
24
+ embedded?: boolean;
25
+ initialBoard?: {
26
+ id: string;
27
+ name: string | null;
28
+ };
29
+ initialListId?: string;
30
+ initialLists?: Array<{
31
+ deleted?: boolean | null;
32
+ id: string;
33
+ name: string | null;
34
+ position?: number | null;
35
+ }>;
23
36
  wsId: string;
24
37
  userId: string;
25
38
  isPersonal: boolean;
26
39
  }
27
40
 
28
41
  export default function MyTasksContent({
42
+ disableAutoCreateBoard = false,
43
+ embedded = false,
44
+ initialBoard,
45
+ initialListId,
46
+ initialLists,
29
47
  wsId,
30
48
  userId,
31
49
  isPersonal,
@@ -40,13 +58,17 @@ export default function MyTasksContent({
40
58
  'enter'
41
59
  );
42
60
  const state = useMyTasksState({
61
+ ...(disableAutoCreateBoard ? { disableAutoCreateBoard } : {}),
62
+ ...(initialBoard ? { initialBoard } : {}),
63
+ ...(initialListId ? { initialListId } : {}),
64
+ ...(initialLists ? { initialLists } : {}),
43
65
  wsId,
44
66
  userId,
45
67
  isPersonal,
46
68
  });
47
69
 
48
70
  return (
49
- <div className="space-y-4 md:space-y-6">
71
+ <div className={embedded ? 'space-y-4' : 'space-y-4 md:space-y-6'}>
50
72
  {/* Header with greeting + summary cards */}
51
73
  <MyTasksHeader
52
74
  overdueCount={state.filteredTasks.overdueTasks?.length ?? 0}
@@ -55,7 +77,9 @@ export default function MyTasksContent({
55
77
  />
56
78
 
57
79
  {/* Command Bar */}
58
- <div className="mx-auto mb-32 max-w-5xl">
80
+ <div
81
+ className={embedded ? 'mx-auto max-w-5xl' : 'mx-auto mb-32 max-w-5xl'}
82
+ >
59
83
  <CommandBar
60
84
  value={state.commandBarInput}
61
85
  onValueChange={state.setCommandBarInput}
@@ -39,12 +39,28 @@ dayjs.extend(utc);
39
39
  dayjs.extend(timezone);
40
40
 
41
41
  interface UseMyTasksStateProps {
42
+ disableAutoCreateBoard?: boolean;
43
+ initialBoard?: {
44
+ id: string;
45
+ name: string | null;
46
+ };
47
+ initialListId?: string;
48
+ initialLists?: Array<{
49
+ deleted?: boolean | null;
50
+ id: string;
51
+ name: string | null;
52
+ position?: number | null;
53
+ }>;
42
54
  wsId: string;
43
55
  userId: string;
44
56
  isPersonal: boolean;
45
57
  }
46
58
 
47
59
  export function useMyTasksState({
60
+ disableAutoCreateBoard = false,
61
+ initialBoard,
62
+ initialListId,
63
+ initialLists,
48
64
  wsId,
49
65
  userId,
50
66
  isPersonal,
@@ -96,8 +112,16 @@ export function useMyTasksState({
96
112
  // Board selector state
97
113
  const [boardSelectorOpen, setBoardSelectorOpen] = useState(false);
98
114
  const [selectedWorkspaceId, setSelectedWorkspaceId] = useState<string>(wsId);
99
- const [selectedBoardId, setSelectedBoardId] = useState<string>('');
100
- const [selectedListId, setSelectedListId] = useState<string>('');
115
+ const [selectedBoardId, setSelectedBoardId] = useState<string>(
116
+ initialBoard?.id ?? ''
117
+ );
118
+ const [selectedListId, setSelectedListId] = useState<string>(
119
+ initialListId ??
120
+ initialLists
121
+ ?.filter((list) => !list.deleted)
122
+ .sort((a, b) => (a.position ?? 0) - (b.position ?? 0))[0]?.id ??
123
+ ''
124
+ );
101
125
  const [newBoardDialogOpen, setNewBoardDialogOpen] = useState(false);
102
126
  const [newBoardName, setNewBoardName] = useState<string>('');
103
127
  const [newListDialogOpen, setNewListDialogOpen] = useState(false);
@@ -276,6 +300,7 @@ export function useMyTasksState({
276
300
  });
277
301
  return payload.count ?? 0;
278
302
  },
303
+ enabled: !disableAutoCreateBoard,
279
304
  });
280
305
 
281
306
  // Auto-create a board if the workspace has none
@@ -294,7 +319,13 @@ export function useMyTasksState({
294
319
  );
295
320
 
296
321
  useEffect(() => {
297
- if (wsBoardCountLoading || wsBoardCount === undefined) return;
322
+ if (
323
+ disableAutoCreateBoard ||
324
+ wsBoardCountLoading ||
325
+ wsBoardCount === undefined
326
+ ) {
327
+ return;
328
+ }
298
329
  if (wsBoardCount > 0) return;
299
330
  if (autoCreateAttemptedRef.current) return;
300
331
  autoCreateAttemptedRef.current = true;
@@ -344,6 +375,7 @@ export function useMyTasksState({
344
375
  queryClient,
345
376
  defaultBoardName,
346
377
  defaultListNames,
378
+ disableAutoCreateBoard,
347
379
  ]);
348
380
 
349
381
  // Fetch boards with lists for selected workspace
@@ -422,11 +454,16 @@ export function useMyTasksState({
422
454
  const availableLists = useMemo(() => {
423
455
  if (!selectedBoardId) return [];
424
456
  const board = boardsData.find((b: any) => b.id === selectedBoardId);
425
- if (!board?.task_lists) return [];
457
+ if (!board?.task_lists) {
458
+ if (selectedBoardId !== initialBoard?.id) return [];
459
+ return (initialLists ?? [])
460
+ .filter((list) => !list.deleted)
461
+ .sort((a, b) => (a.position ?? 0) - (b.position ?? 0));
462
+ }
426
463
  return (board.task_lists as any[])
427
464
  .filter((l: any) => !l.deleted)
428
465
  .sort((a: any, b: any) => (a.position || 0) - (b.position || 0));
429
- }, [selectedBoardId, boardsData]);
466
+ }, [selectedBoardId, boardsData, initialBoard?.id, initialLists]);
430
467
 
431
468
  const hasValidSelectedList = useMemo(
432
469
  () => availableLists.some((list: any) => list.id === selectedListId),
@@ -794,13 +831,23 @@ export function useMyTasksState({
794
831
  const selectedDestination = useMemo(() => {
795
832
  if (!selectedBoardId || !selectedListId) return null;
796
833
  const board = boardsData.find((b: any) => b.id === selectedBoardId);
797
- const lists = (board?.task_lists as any[]) || [];
834
+ const isInitialBoard = selectedBoardId === initialBoard?.id;
835
+ const lists =
836
+ (board?.task_lists as any[]) ||
837
+ (isInitialBoard ? (initialLists ?? []) : []);
798
838
  const list = lists.find((l: any) => l.id === selectedListId);
799
839
  return {
800
- boardName: board?.name || 'Unknown Board',
840
+ boardName: board?.name || initialBoard?.name || 'Unknown Board',
801
841
  listName: list?.name || 'Unknown List',
802
842
  };
803
- }, [selectedBoardId, selectedListId, boardsData]);
843
+ }, [
844
+ selectedBoardId,
845
+ selectedListId,
846
+ boardsData,
847
+ initialBoard?.id,
848
+ initialBoard?.name,
849
+ initialLists,
850
+ ]);
804
851
 
805
852
  const handleClearDestination = () => {
806
853
  setSelectedBoardId('');
@@ -41,10 +41,7 @@ export function NoteEditDialog({
41
41
 
42
42
  return (
43
43
  <Dialog open={isOpen} onOpenChange={onOpenChange} modal={true}>
44
- <DialogContent
45
- showCloseButton={false}
46
- className="inset-0! top-0! left-0! flex h-screen max-h-screen w-screen max-w-none! translate-x-0! translate-y-0! gap-0 rounded-none! border-0 p-0"
47
- >
44
+ <DialogContent showCloseButton={false} presentation="fullscreen">
48
45
  {/* Main content area - Note title and description */}
49
46
  <div className="flex min-w-0 flex-1 flex-col bg-background transition-all duration-300">
50
47
  {/* Enhanced Header with gradient */}
@@ -115,6 +115,31 @@ describe('BoardClient', () => {
115
115
  );
116
116
  });
117
117
 
118
+ it('uses the shared task board loading state while the board query resolves', () => {
119
+ getWorkspaceTaskBoardMock.mockReturnValue(new Promise(() => {}));
120
+ const queryClient = new QueryClient({
121
+ defaultOptions: {
122
+ queries: {
123
+ retry: false,
124
+ },
125
+ },
126
+ });
127
+
128
+ render(
129
+ <QueryClientProvider client={queryClient}>
130
+ <BoardClient
131
+ boardId="board-1"
132
+ workspace={{ id: 'workspace-uuid', personal: false } as any}
133
+ currentUserId="user-1"
134
+ />
135
+ </QueryClientProvider>
136
+ );
137
+
138
+ expect(screen.getByTestId('task-board-loading-state')).toBeInTheDocument();
139
+ expect(screen.getByTestId('kanban-skeleton')).toBeInTheDocument();
140
+ expect(screen.queryByText('Loading board...')).not.toBeInTheDocument();
141
+ });
142
+
118
143
  it('can revalidate loaded board lists without invalidating visible task caches', async () => {
119
144
  const queryClient = new QueryClient({
120
145
  defaultOptions: {