@tuturuuu/ui 0.1.0 → 0.3.1

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 (128) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/package.json +82 -70
  3. package/src/components/ui/__tests__/avatar.test.tsx +8 -5
  4. package/src/components/ui/calendar-app/components/calendar-connections-compact.tsx +414 -0
  5. package/src/components/ui/calendar-app/components/calendar-connections-manager.tsx +5 -1
  6. package/src/components/ui/calendar-app/components/calendar-connections-settings-content.tsx +529 -0
  7. package/src/components/ui/calendar-app/components/calendar-connections-unified.tsx +26 -1429
  8. package/src/components/ui/calendar-app/components/use-calendar-connections-manager.ts +711 -0
  9. package/src/components/ui/chart.test.tsx +29 -0
  10. package/src/components/ui/chart.tsx +12 -3
  11. package/src/components/ui/chat/chat-agent-details-external-thread-panel.test.tsx +43 -13
  12. package/src/components/ui/chat/chat-agent-details-external-thread-panel.tsx +138 -74
  13. package/src/components/ui/chat/chat-agent-details-operations-panel.test.tsx +70 -0
  14. package/src/components/ui/chat/chat-agent-details-operations-panel.tsx +60 -1
  15. package/src/components/ui/chat/chat-agent-details-sidebar.tsx +13 -5
  16. package/src/components/ui/chat/chat-sidebar-panel.test.tsx +110 -0
  17. package/src/components/ui/chat/chat-sidebar-panel.tsx +13 -3
  18. package/src/components/ui/custom/__tests__/settings-dialog-shell.test.tsx +24 -1
  19. package/src/components/ui/custom/__tests__/tuturuuu-logo.test.ts +12 -3
  20. package/src/components/ui/custom/__tests__/workspace-select-helpers.test.ts +39 -0
  21. package/src/components/ui/custom/common-footer.tsx +16 -1
  22. package/src/components/ui/custom/production-indicator.tsx +1 -1
  23. package/src/components/ui/custom/settings/sidebar-settings.tsx +1 -1
  24. package/src/components/ui/custom/settings/task-settings.tsx +18 -0
  25. package/src/components/ui/custom/settings-dialog-shell.tsx +38 -23
  26. package/src/components/ui/custom/sidebar-context-compile-graph.test.ts +60 -0
  27. package/src/components/ui/custom/sidebar-context.tsx +61 -61
  28. package/src/components/ui/custom/sidebar-remote-behavior-bridge.tsx +123 -0
  29. package/src/components/ui/custom/tuturuuu-logo-urls.ts +6 -0
  30. package/src/components/ui/custom/tuturuuu-logo.tsx +25 -7
  31. package/src/components/ui/custom/workspace-select-helpers.ts +20 -0
  32. package/src/components/ui/custom/workspace-select.tsx +33 -12
  33. package/src/components/ui/finance/invoices/components/invoice-checkout-summary.tsx +7 -1
  34. package/src/components/ui/finance/invoices/components/invoice-payment-settings.tsx +3 -0
  35. package/src/components/ui/finance/invoices/components/invoice-products-permission-warning.tsx +58 -0
  36. package/src/components/ui/finance/invoices/components/subscription-group-selector.tsx +12 -20
  37. package/src/components/ui/finance/invoices/hooks/use-subscription-auto-selection.ts +10 -9
  38. package/src/components/ui/finance/invoices/hooks/use-subscription-invoice-content.ts +10 -5
  39. package/src/components/ui/finance/invoices/hooks.ts +75 -20
  40. package/src/components/ui/finance/invoices/new-invoice-page.test.tsx +137 -0
  41. package/src/components/ui/finance/invoices/new-invoice-page.tsx +86 -37
  42. package/src/components/ui/finance/invoices/product-selection.test.tsx +8 -26
  43. package/src/components/ui/finance/invoices/product-selection.tsx +2 -10
  44. package/src/components/ui/finance/invoices/standard-invoice.tsx +88 -26
  45. package/src/components/ui/finance/invoices/subscription-invoice.tsx +154 -46
  46. package/src/components/ui/finance/invoices/utils.test.ts +50 -0
  47. package/src/components/ui/finance/invoices/utils.ts +75 -17
  48. package/src/components/ui/finance/shared/finance-display-amount.tsx +3 -1
  49. package/src/components/ui/finance/shared/finance-permission-warning-dialog.test.tsx +34 -0
  50. package/src/components/ui/finance/shared/finance-permission-warning-dialog.tsx +157 -0
  51. package/src/components/ui/finance/transactions/form-basic-tab.tsx +8 -0
  52. package/src/components/ui/finance/transactions/form-more-tab.tsx +8 -0
  53. package/src/components/ui/finance/transactions/form-types.ts +2 -0
  54. package/src/components/ui/finance/transactions/form.test.tsx +43 -0
  55. package/src/components/ui/finance/transactions/form.tsx +60 -0
  56. package/src/components/ui/finance/transactions/infinite-transactions-list.tsx +27 -0
  57. package/src/components/ui/finance/transactions/transactions-create-summary.tsx +13 -1
  58. package/src/components/ui/finance/transactions/transactions-infinite-page.tsx +4 -0
  59. package/src/components/ui/finance/transactions/transactions-page.tsx +23 -1
  60. package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +4 -0
  61. package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +5 -0
  62. package/src/components/ui/legacy/calendar/calendar-content.tsx +9 -1
  63. package/src/components/ui/legacy/calendar/event-modal.tsx +146 -2
  64. package/src/components/ui/legacy/calendar/event-preview-popover.tsx +200 -0
  65. package/src/components/ui/legacy/calendar/smart-calendar.test.tsx +76 -0
  66. package/src/components/ui/legacy/calendar/smart-calendar.tsx +13 -1
  67. package/src/components/ui/legacy/meet/page.test.ts +180 -0
  68. package/src/components/ui/legacy/meet/page.tsx +87 -39
  69. package/src/components/ui/legacy/meet/planId/page.tsx +10 -4
  70. package/src/components/ui/text-editor/__tests__/task-mention-chip.test.tsx +203 -6
  71. package/src/components/ui/text-editor/task-mention-chip.tsx +29 -7
  72. package/src/components/ui/tu-do/boards/boardId/board-column.tsx +79 -25
  73. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/__tests__/bulk-mutations-external-workspaces.test.tsx +392 -0
  74. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-actions-island.test.tsx +57 -0
  75. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-actions-island.tsx +106 -0
  76. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-clear-delete.ts +106 -161
  77. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations-assignees.ts +96 -150
  78. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations-labels.ts +63 -79
  79. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations-projects.ts +64 -83
  80. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-updates.ts +115 -155
  81. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operation-utils.ts +319 -2
  82. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operations.ts +8 -1
  83. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +63 -37
  84. package/src/components/ui/tu-do/boards/boardId/kanban/kanban-column-collapse.ts +16 -0
  85. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +46 -0
  86. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +5 -3
  87. package/src/components/ui/tu-do/boards/boardId/kanban.tsx +19 -7
  88. package/src/components/ui/tu-do/boards/boardId/menus/__tests__/task-menus.test.tsx +181 -2
  89. package/src/components/ui/tu-do/boards/boardId/menus/index.ts +1 -0
  90. package/src/components/ui/tu-do/boards/boardId/menus/task-scheduling-menu.tsx +463 -0
  91. package/src/components/ui/tu-do/boards/boardId/menus/task-scheduling-utils.ts +109 -0
  92. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +4 -0
  93. package/src/components/ui/tu-do/boards/boardId/task-card/TaskCardCheckbox.tsx +6 -3
  94. package/src/components/ui/tu-do/boards/boardId/task-card/TaskCardDates.tsx +26 -9
  95. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-checkbox-style.ts +39 -0
  96. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.test.ts +43 -0
  97. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.ts +33 -0
  98. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-completion-checkbox-visibility.test.ts +31 -0
  99. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-completion-checkbox-visibility.ts +9 -0
  100. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-identifier-row.test.tsx +124 -0
  101. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-identifier-row.tsx +88 -0
  102. package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +151 -76
  103. package/src/components/ui/tu-do/boards/boardId/task-card/task-scheduling-badge.tsx +174 -0
  104. package/src/components/ui/tu-do/providers/task-dialog-provider.tsx +34 -13
  105. package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +54 -1
  106. package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +158 -0
  107. package/src/components/ui/tu-do/shared/__tests__/task-dialog-manager.test.tsx +5 -2
  108. package/src/components/ui/tu-do/shared/board-client.tsx +12 -2
  109. package/src/components/ui/tu-do/shared/board-views.tsx +195 -328
  110. package/src/components/ui/tu-do/shared/list-view.tsx +18 -8
  111. package/src/components/ui/tu-do/shared/task-due-date-visibility.test.ts +72 -0
  112. package/src/components/ui/tu-do/shared/task-due-date-visibility.ts +38 -0
  113. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/__tests__/use-task-realtime-sync.test.tsx +37 -9
  114. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-mutations.ts +6 -3
  115. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-realtime-sync.ts +89 -70
  116. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-save.ts +2 -2
  117. package/src/components/ui/tu-do/shared/task-row-actions-menu.tsx +33 -0
  118. package/src/hooks/__tests__/use-calendar-readonly.test.tsx +74 -3
  119. package/src/hooks/__tests__/use-task-actions.test.tsx +118 -0
  120. package/src/hooks/__tests__/use-user-config.test.tsx +65 -0
  121. package/src/hooks/__tests__/use-workspace-presence.test.tsx +1 -1
  122. package/src/hooks/use-calendar-sync.tsx +22 -277
  123. package/src/hooks/use-calendar.tsx +95 -525
  124. package/src/hooks/use-task-actions.ts +43 -117
  125. package/src/hooks/use-user-config.ts +1 -1
  126. package/src/hooks/use-workspace-config.ts +6 -2
  127. package/src/hooks/use-workspace-presence.ts +1 -1
  128. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-actions-bar.tsx +0 -94
@@ -2,11 +2,19 @@
2
2
 
3
3
  import { type QueryClient, useMutation } from '@tanstack/react-query';
4
4
  import { bulkWorkspaceTasks } from '@tuturuuu/internal-api/tasks';
5
- import type { Task } from '@tuturuuu/types/primitives/Task';
6
5
  import { toast } from '@tuturuuu/ui/sonner';
7
6
  import type { BoardBroadcastFn } from '../../../../shared/board-broadcast-context';
8
7
  import type { BulkOperationI18n } from './bulk-operation-i18n';
9
- import { getInternalApiOptions } from './bulk-operation-utils';
8
+ import {
9
+ type BulkTaskWorkspaceGroup,
10
+ bulkWorkspaceTasksByEffectiveWorkspace,
11
+ getInternalApiOptions,
12
+ restoreBoardTaskCaches,
13
+ restoreDeletedBoardTasks,
14
+ restoreFailedBoardTasks,
15
+ snapshotBoardTaskCaches,
16
+ updateBoardTaskCaches,
17
+ } from './bulk-operation-utils';
10
18
 
11
19
  export function useBulkClearLabels(
12
20
  queryClient: QueryClient,
@@ -38,24 +46,22 @@ export function useBulkClearLabels(
38
46
  },
39
47
  onMutate: async ({ taskIds }) => {
40
48
  await queryClient.cancelQueries({ queryKey: ['tasks', boardId] });
41
- const previousTasks = queryClient.getQueryData(['tasks', boardId]);
49
+ await queryClient.cancelQueries({ queryKey: ['tasks-full', boardId] });
50
+ const cacheSnapshot = snapshotBoardTaskCaches(queryClient, boardId);
42
51
  const taskIdSet = new Set(taskIds);
43
52
 
44
- queryClient.setQueryData(
45
- ['tasks', boardId],
46
- (old: Task[] | undefined) => {
47
- if (!old) return old;
48
- return old.map((task) =>
49
- taskIdSet.has(task.id) ? { ...task, labels: [] } : task
50
- );
51
- }
52
- );
53
+ updateBoardTaskCaches(queryClient, boardId, (old) => {
54
+ if (!old) return old;
55
+ return old.map((task) =>
56
+ taskIdSet.has(task.id) ? { ...task, labels: [] } : task
57
+ );
58
+ });
53
59
 
54
- return { previousTasks };
60
+ return cacheSnapshot;
55
61
  },
56
62
  onError: (error, _, context) => {
57
- if (context?.previousTasks) {
58
- queryClient.setQueryData(['tasks', boardId], context.previousTasks);
63
+ if (context) {
64
+ restoreBoardTaskCaches(queryClient, boardId, context);
59
65
  }
60
66
  console.error('Bulk clear labels failed', error);
61
67
  toast.error(
@@ -68,22 +74,13 @@ export function useBulkClearLabels(
68
74
  data.failures.map((failure) => failure.taskId)
69
75
  );
70
76
 
71
- if (failedTaskIds.size > 0 && Array.isArray(context?.previousTasks)) {
72
- const previousTaskMap = new Map(
73
- (context.previousTasks as Task[]).map((task) => [task.id, task])
74
- );
75
-
76
- queryClient.setQueryData(
77
- ['tasks', boardId],
78
- (old: Task[] | undefined) => {
79
- if (!old) return old;
80
- return old.map((task) => {
81
- if (!failedTaskIds.has(task.id)) return task;
82
- return previousTaskMap.get(task.id) ?? task;
83
- });
84
- }
85
- );
86
- }
77
+ restoreFailedBoardTasks({
78
+ queryClient,
79
+ boardId,
80
+ previousTasks: context?.previousTasks,
81
+ previousFullTasks: context?.previousFullTasks,
82
+ failedTaskIds,
83
+ });
87
84
 
88
85
  const succeededTaskIds = data.taskIds.filter(
89
86
  (taskId) => !failedTaskIds.has(taskId)
@@ -147,24 +144,22 @@ export function useBulkClearProjects(
147
144
  },
148
145
  onMutate: async ({ taskIds }) => {
149
146
  await queryClient.cancelQueries({ queryKey: ['tasks', boardId] });
150
- const previousTasks = queryClient.getQueryData(['tasks', boardId]);
147
+ await queryClient.cancelQueries({ queryKey: ['tasks-full', boardId] });
148
+ const cacheSnapshot = snapshotBoardTaskCaches(queryClient, boardId);
151
149
  const taskIdSet = new Set(taskIds);
152
150
 
153
- queryClient.setQueryData(
154
- ['tasks', boardId],
155
- (old: Task[] | undefined) => {
156
- if (!old) return old;
157
- return old.map((task) =>
158
- taskIdSet.has(task.id) ? { ...task, projects: [] } : task
159
- );
160
- }
161
- );
151
+ updateBoardTaskCaches(queryClient, boardId, (old) => {
152
+ if (!old) return old;
153
+ return old.map((task) =>
154
+ taskIdSet.has(task.id) ? { ...task, projects: [] } : task
155
+ );
156
+ });
162
157
 
163
- return { previousTasks };
158
+ return cacheSnapshot;
164
159
  },
165
160
  onError: (error, _, context) => {
166
- if (context?.previousTasks) {
167
- queryClient.setQueryData(['tasks', boardId], context.previousTasks);
161
+ if (context) {
162
+ restoreBoardTaskCaches(queryClient, boardId, context);
168
163
  }
169
164
  console.error('Bulk clear projects failed', error);
170
165
  toast.error(
@@ -177,22 +172,13 @@ export function useBulkClearProjects(
177
172
  data.failures.map((failure) => failure.taskId)
178
173
  );
179
174
 
180
- if (failedTaskIds.size > 0 && Array.isArray(context?.previousTasks)) {
181
- const previousTaskMap = new Map(
182
- (context.previousTasks as Task[]).map((task) => [task.id, task])
183
- );
184
-
185
- queryClient.setQueryData(
186
- ['tasks', boardId],
187
- (old: Task[] | undefined) => {
188
- if (!old) return old;
189
- return old.map((task) => {
190
- if (!failedTaskIds.has(task.id)) return task;
191
- return previousTaskMap.get(task.id) ?? task;
192
- });
193
- }
194
- );
195
- }
175
+ restoreFailedBoardTasks({
176
+ queryClient,
177
+ boardId,
178
+ previousTasks: context?.previousTasks,
179
+ previousFullTasks: context?.previousFullTasks,
180
+ failedTaskIds,
181
+ });
196
182
 
197
183
  const succeededTaskIds = data.taskIds.filter(
198
184
  (taskId) => !failedTaskIds.has(taskId)
@@ -237,14 +223,14 @@ export function useBulkClearAssignees(
237
223
  mutationFn: async ({ taskIds }: { taskIds: string[] }) => {
238
224
  const apiOptions = getInternalApiOptions();
239
225
 
240
- const result = await bulkWorkspaceTasks(
241
- wsId,
242
- {
243
- taskIds,
244
- operation: { type: 'clear_assignees' },
245
- },
246
- apiOptions
247
- );
226
+ const result = await bulkWorkspaceTasksByEffectiveWorkspace({
227
+ queryClient,
228
+ boardId,
229
+ defaultWorkspaceId: wsId,
230
+ taskIds,
231
+ operation: { type: 'clear_assignees' },
232
+ options: apiOptions,
233
+ });
248
234
 
249
235
  if (result.successCount === 0 && taskIds.length > 0) {
250
236
  throw new Error(
@@ -256,24 +242,22 @@ export function useBulkClearAssignees(
256
242
  },
257
243
  onMutate: async ({ taskIds }) => {
258
244
  await queryClient.cancelQueries({ queryKey: ['tasks', boardId] });
259
- const previousTasks = queryClient.getQueryData(['tasks', boardId]);
245
+ await queryClient.cancelQueries({ queryKey: ['tasks-full', boardId] });
246
+ const cacheSnapshot = snapshotBoardTaskCaches(queryClient, boardId);
260
247
  const taskIdSet = new Set(taskIds);
261
248
 
262
- queryClient.setQueryData(
263
- ['tasks', boardId],
264
- (old: Task[] | undefined) => {
265
- if (!old) return old;
266
- return old.map((task) =>
267
- taskIdSet.has(task.id) ? { ...task, assignees: [] } : task
268
- );
269
- }
270
- );
249
+ updateBoardTaskCaches(queryClient, boardId, (old) => {
250
+ if (!old) return old;
251
+ return old.map((task) =>
252
+ taskIdSet.has(task.id) ? { ...task, assignees: [] } : task
253
+ );
254
+ });
271
255
 
272
- return { previousTasks };
256
+ return cacheSnapshot;
273
257
  },
274
258
  onError: (error, _, context) => {
275
- if (context?.previousTasks) {
276
- queryClient.setQueryData(['tasks', boardId], context.previousTasks);
259
+ if (context) {
260
+ restoreBoardTaskCaches(queryClient, boardId, context);
277
261
  }
278
262
  console.error('Bulk clear assignees failed', error);
279
263
  toast.error(
@@ -286,22 +270,13 @@ export function useBulkClearAssignees(
286
270
  data.failures.map((failure) => failure.taskId)
287
271
  );
288
272
 
289
- if (failedTaskIds.size > 0 && Array.isArray(context?.previousTasks)) {
290
- const previousTaskMap = new Map(
291
- (context.previousTasks as Task[]).map((task) => [task.id, task])
292
- );
293
-
294
- queryClient.setQueryData(
295
- ['tasks', boardId],
296
- (old: Task[] | undefined) => {
297
- if (!old) return old;
298
- return old.map((task) => {
299
- if (!failedTaskIds.has(task.id)) return task;
300
- return previousTaskMap.get(task.id) ?? task;
301
- });
302
- }
303
- );
304
- }
273
+ restoreFailedBoardTasks({
274
+ queryClient,
275
+ boardId,
276
+ previousTasks: context?.previousTasks,
277
+ previousFullTasks: context?.previousFullTasks,
278
+ failedTaskIds,
279
+ });
305
280
 
306
281
  const succeededTaskIds = data.taskIds.filter(
307
282
  (taskId) => !failedTaskIds.has(taskId)
@@ -345,20 +320,27 @@ export function useBulkDeleteTasks(
345
320
  i18n?: BulkOperationI18n
346
321
  ) {
347
322
  return useMutation({
348
- mutationFn: async ({ taskIds }: { taskIds: string[] }) => {
323
+ mutationFn: async ({
324
+ taskIds,
325
+ workspaceGroups,
326
+ }: {
327
+ taskIds: string[];
328
+ workspaceGroups?: BulkTaskWorkspaceGroup[];
329
+ }) => {
349
330
  const apiOptions = getInternalApiOptions();
350
331
 
351
- const result = await bulkWorkspaceTasks(
352
- wsId,
353
- {
354
- taskIds,
355
- operation: {
356
- type: 'update_fields',
357
- updates: { deleted: true },
358
- },
332
+ const result = await bulkWorkspaceTasksByEffectiveWorkspace({
333
+ queryClient,
334
+ boardId,
335
+ defaultWorkspaceId: wsId,
336
+ taskIds,
337
+ operation: {
338
+ type: 'update_fields',
339
+ updates: { deleted: true },
359
340
  },
360
- apiOptions
361
- );
341
+ options: apiOptions,
342
+ workspaceGroups,
343
+ });
362
344
 
363
345
  if (result.successCount === 0 && taskIds.length > 0) {
364
346
  throw new Error(`Failed to delete all ${taskIds.length} tasks`);
@@ -368,22 +350,20 @@ export function useBulkDeleteTasks(
368
350
  },
369
351
  onMutate: async ({ taskIds }) => {
370
352
  await queryClient.cancelQueries({ queryKey: ['tasks', boardId] });
371
- const previousTasks = queryClient.getQueryData(['tasks', boardId]);
353
+ await queryClient.cancelQueries({ queryKey: ['tasks-full', boardId] });
354
+ const cacheSnapshot = snapshotBoardTaskCaches(queryClient, boardId);
372
355
  const taskIdSet = new Set(taskIds);
373
356
 
374
- queryClient.setQueryData(
375
- ['tasks', boardId],
376
- (old: Task[] | undefined) => {
377
- if (!old) return old;
378
- return old.filter((task) => !taskIdSet.has(task.id));
379
- }
380
- );
357
+ updateBoardTaskCaches(queryClient, boardId, (old) => {
358
+ if (!old) return old;
359
+ return old.filter((task) => !taskIdSet.has(task.id));
360
+ });
381
361
 
382
- return { previousTasks };
362
+ return cacheSnapshot;
383
363
  },
384
364
  onError: (error, _, context) => {
385
- if (context?.previousTasks) {
386
- queryClient.setQueryData(['tasks', boardId], context.previousTasks);
365
+ if (context) {
366
+ restoreBoardTaskCaches(queryClient, boardId, context);
387
367
  }
388
368
  console.error('Bulk delete failed', error);
389
369
  toast.error(
@@ -395,48 +375,13 @@ export function useBulkDeleteTasks(
395
375
  data.failures.map((failure) => failure.taskId)
396
376
  );
397
377
 
398
- if (failedTaskIds.size > 0 && Array.isArray(context?.previousTasks)) {
399
- queryClient.setQueryData(
400
- ['tasks', boardId],
401
- (old: Task[] | undefined) => {
402
- const existing = old ?? [];
403
- const existingById = new Map(
404
- existing.map((task) => [task.id, task])
405
- );
406
- const previousTasks = context.previousTasks as Task[];
407
- const previousOrder = new Map(
408
- previousTasks.map((task, index) => [task.id, index])
409
- );
410
-
411
- for (const previousTask of previousTasks) {
412
- if (!failedTaskIds.has(previousTask.id)) {
413
- continue;
414
- }
415
-
416
- existingById.set(previousTask.id, previousTask);
417
- }
418
-
419
- return Array.from(existingById.values()).sort((a, b) => {
420
- const aIndex = previousOrder.get(a.id);
421
- const bIndex = previousOrder.get(b.id);
422
-
423
- if (typeof aIndex === 'number' && typeof bIndex === 'number') {
424
- return aIndex - bIndex;
425
- }
426
-
427
- if (typeof aIndex === 'number') {
428
- return -1;
429
- }
430
-
431
- if (typeof bIndex === 'number') {
432
- return 1;
433
- }
434
-
435
- return 0;
436
- });
437
- }
438
- );
439
- }
378
+ restoreDeletedBoardTasks({
379
+ queryClient,
380
+ boardId,
381
+ previousTasks: context?.previousTasks,
382
+ previousFullTasks: context?.previousFullTasks,
383
+ failedTaskIds,
384
+ });
440
385
 
441
386
  const succeededTaskIds = data.taskIds.filter(
442
387
  (taskId) => !failedTaskIds.has(taskId)