@tuturuuu/ui 0.2.0 → 0.3.2

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 (129) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/package.json +79 -67
  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-operations-panel.test.tsx +396 -2
  12. package/src/components/ui/chat/chat-agent-details-operations-panel.tsx +36 -8
  13. package/src/components/ui/chat/chat-agent-details-setup-panel.tsx +14 -0
  14. package/src/components/ui/chat/chat-agent-details-sidebar.test.tsx +5 -0
  15. package/src/components/ui/chat/chat-agent-details-sidebar.tsx +21 -7
  16. package/src/components/ui/chat/chat-agent-details-utils.test.ts +73 -0
  17. package/src/components/ui/chat/chat-agent-details-utils.tsx +100 -26
  18. package/src/components/ui/chat/chat-agent-details-zalo-personal-panel.tsx +517 -0
  19. package/src/components/ui/chat/chat-workspace.tsx +31 -1
  20. package/src/components/ui/chat/hooks-messages.test.tsx +45 -1
  21. package/src/components/ui/chat/hooks-messages.ts +1 -1
  22. package/src/components/ui/chat/hooks-realtime.ts +13 -16
  23. package/src/components/ui/custom/__tests__/settings-dialog-shell.test.tsx +24 -1
  24. package/src/components/ui/custom/__tests__/tuturuuu-logo.test.ts +12 -3
  25. package/src/components/ui/custom/__tests__/workspace-select-helpers.test.ts +39 -0
  26. package/src/components/ui/custom/common-footer.tsx +16 -1
  27. package/src/components/ui/custom/production-indicator.tsx +1 -1
  28. package/src/components/ui/custom/settings/sidebar-settings.tsx +1 -1
  29. package/src/components/ui/custom/settings/task-settings.tsx +18 -0
  30. package/src/components/ui/custom/settings-dialog-shell.tsx +38 -23
  31. package/src/components/ui/custom/sidebar-context-compile-graph.test.ts +60 -0
  32. package/src/components/ui/custom/sidebar-context.tsx +61 -61
  33. package/src/components/ui/custom/sidebar-remote-behavior-bridge.tsx +123 -0
  34. package/src/components/ui/custom/tuturuuu-logo-urls.ts +6 -0
  35. package/src/components/ui/custom/tuturuuu-logo.tsx +25 -7
  36. package/src/components/ui/custom/workspace-select-helpers.ts +20 -0
  37. package/src/components/ui/custom/workspace-select.tsx +33 -12
  38. package/src/components/ui/finance/invoices/components/invoice-checkout-summary.tsx +7 -1
  39. package/src/components/ui/finance/invoices/components/invoice-payment-settings.tsx +3 -0
  40. package/src/components/ui/finance/invoices/components/invoice-products-permission-warning.tsx +58 -0
  41. package/src/components/ui/finance/invoices/components/subscription-group-selector.tsx +12 -20
  42. package/src/components/ui/finance/invoices/hooks/use-subscription-auto-selection.ts +10 -9
  43. package/src/components/ui/finance/invoices/hooks/use-subscription-invoice-content.ts +10 -5
  44. package/src/components/ui/finance/invoices/hooks.ts +75 -20
  45. package/src/components/ui/finance/invoices/new-invoice-page.test.tsx +137 -0
  46. package/src/components/ui/finance/invoices/new-invoice-page.tsx +86 -37
  47. package/src/components/ui/finance/invoices/product-selection.test.tsx +8 -26
  48. package/src/components/ui/finance/invoices/product-selection.tsx +2 -10
  49. package/src/components/ui/finance/invoices/standard-invoice.tsx +88 -26
  50. package/src/components/ui/finance/invoices/subscription-invoice.tsx +154 -46
  51. package/src/components/ui/finance/invoices/utils.test.ts +50 -0
  52. package/src/components/ui/finance/invoices/utils.ts +75 -17
  53. package/src/components/ui/finance/shared/finance-display-amount.tsx +3 -1
  54. package/src/components/ui/finance/shared/finance-permission-warning-dialog.test.tsx +34 -0
  55. package/src/components/ui/finance/shared/finance-permission-warning-dialog.tsx +157 -0
  56. package/src/components/ui/finance/transactions/form-basic-tab.tsx +8 -0
  57. package/src/components/ui/finance/transactions/form-more-tab.tsx +8 -0
  58. package/src/components/ui/finance/transactions/form-types.ts +2 -0
  59. package/src/components/ui/finance/transactions/form.test.tsx +43 -0
  60. package/src/components/ui/finance/transactions/form.tsx +60 -0
  61. package/src/components/ui/finance/transactions/infinite-transactions-list.tsx +27 -0
  62. package/src/components/ui/finance/transactions/transactions-create-summary.tsx +13 -1
  63. package/src/components/ui/finance/transactions/transactions-infinite-page.tsx +4 -0
  64. package/src/components/ui/finance/transactions/transactions-page.tsx +23 -1
  65. package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +4 -0
  66. package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +5 -0
  67. package/src/components/ui/legacy/calendar/calendar-content.tsx +9 -1
  68. package/src/components/ui/legacy/calendar/event-modal.tsx +146 -2
  69. package/src/components/ui/legacy/calendar/event-preview-popover.tsx +200 -0
  70. package/src/components/ui/legacy/calendar/smart-calendar.test.tsx +76 -0
  71. package/src/components/ui/legacy/calendar/smart-calendar.tsx +13 -1
  72. package/src/components/ui/legacy/meet/page.test.ts +180 -0
  73. package/src/components/ui/legacy/meet/page.tsx +87 -39
  74. package/src/components/ui/tu-do/boards/boardId/board-column.tsx +79 -25
  75. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/__tests__/bulk-mutations-external-workspaces.test.tsx +392 -0
  76. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-actions-island.test.tsx +57 -0
  77. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-actions-island.tsx +106 -0
  78. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-clear-delete.ts +106 -161
  79. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations-assignees.ts +96 -150
  80. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations-labels.ts +63 -79
  81. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations-projects.ts +64 -83
  82. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-updates.ts +115 -155
  83. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operation-utils.ts +319 -2
  84. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operations.ts +8 -1
  85. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +63 -37
  86. package/src/components/ui/tu-do/boards/boardId/kanban/kanban-column-collapse.ts +16 -0
  87. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +46 -0
  88. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +5 -3
  89. package/src/components/ui/tu-do/boards/boardId/kanban.tsx +19 -7
  90. package/src/components/ui/tu-do/boards/boardId/menus/__tests__/task-menus.test.tsx +181 -2
  91. package/src/components/ui/tu-do/boards/boardId/menus/index.ts +1 -0
  92. package/src/components/ui/tu-do/boards/boardId/menus/task-scheduling-menu.tsx +463 -0
  93. package/src/components/ui/tu-do/boards/boardId/menus/task-scheduling-utils.ts +109 -0
  94. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +4 -0
  95. package/src/components/ui/tu-do/boards/boardId/task-card/TaskCardCheckbox.tsx +6 -3
  96. package/src/components/ui/tu-do/boards/boardId/task-card/TaskCardDates.tsx +26 -9
  97. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-checkbox-style.ts +39 -0
  98. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.test.ts +43 -0
  99. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.ts +33 -0
  100. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-completion-checkbox-visibility.test.ts +31 -0
  101. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-completion-checkbox-visibility.ts +9 -0
  102. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-identifier-row.test.tsx +124 -0
  103. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-identifier-row.tsx +88 -0
  104. package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +151 -76
  105. package/src/components/ui/tu-do/boards/boardId/task-card/task-scheduling-badge.tsx +174 -0
  106. package/src/components/ui/tu-do/providers/task-dialog-provider.tsx +34 -13
  107. package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +54 -1
  108. package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +158 -0
  109. package/src/components/ui/tu-do/shared/__tests__/task-dialog-manager.test.tsx +5 -2
  110. package/src/components/ui/tu-do/shared/board-client.tsx +12 -2
  111. package/src/components/ui/tu-do/shared/board-views.tsx +195 -328
  112. package/src/components/ui/tu-do/shared/list-view.tsx +18 -8
  113. package/src/components/ui/tu-do/shared/task-due-date-visibility.test.ts +72 -0
  114. package/src/components/ui/tu-do/shared/task-due-date-visibility.ts +38 -0
  115. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-mutations.ts +6 -3
  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-semantic-task-search.ts +10 -33
  125. package/src/hooks/use-task-actions.ts +43 -117
  126. package/src/hooks/use-user-config.ts +1 -1
  127. package/src/hooks/use-workspace-config.ts +6 -2
  128. package/src/hooks/use-workspace-presence.ts +1 -1
  129. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-actions-bar.tsx +0 -94
@@ -1,16 +1,10 @@
1
1
  import { useQuery } from '@tanstack/react-query';
2
+ import {
3
+ searchWorkspaceTasks,
4
+ type WorkspaceTaskSearchResult,
5
+ } from '@tuturuuu/internal-api/tasks';
2
6
 
3
- interface SemanticSearchResult {
4
- id: string;
5
- name: string;
6
- description: string | null;
7
- list_id: string;
8
- start_date: string | null;
9
- end_date: string | null;
10
- completed: boolean;
11
- archived: boolean;
12
- similarity: number;
13
- }
7
+ type SemanticSearchResult = WorkspaceTaskSearchResult;
14
8
 
15
9
  interface UseSemanticTaskSearchOptions {
16
10
  wsId: string;
@@ -41,28 +35,11 @@ export function useSemanticTaskSearch({
41
35
  }
42
36
 
43
37
  try {
44
- // Call the API endpoint to perform semantic search
45
- const response = await fetch(
46
- `/api/v1/workspaces/${wsId}/tasks/search`,
47
- {
48
- method: 'POST',
49
- headers: {
50
- 'Content-Type': 'application/json',
51
- },
52
- body: JSON.stringify({
53
- query: query.trim(),
54
- matchThreshold,
55
- matchCount,
56
- }),
57
- }
58
- );
59
-
60
- if (!response.ok) {
61
- console.error('Semantic search failed:', await response.text());
62
- return [];
63
- }
64
-
65
- const data = await response.json();
38
+ const data = await searchWorkspaceTasks(wsId, {
39
+ query: query.trim(),
40
+ matchThreshold,
41
+ matchCount,
42
+ });
66
43
  return (data.tasks || []) as SemanticSearchResult[];
67
44
  } catch (error) {
68
45
  console.error('Error performing semantic search:', error);
@@ -94,6 +94,24 @@ export function useTaskActions({
94
94
  return resolveWorkspaceIdForTask(task);
95
95
  }, [resolveWorkspaceIdForTask, task]);
96
96
 
97
+ const getEffectiveTaskIds = useCallback(
98
+ (taskRecord?: Task) => {
99
+ if (!taskRecord) return [];
100
+
101
+ if (
102
+ isMultiSelectMode &&
103
+ selectedTasks &&
104
+ selectedTasks.size > 1 &&
105
+ selectedTasks.has(taskRecord.id)
106
+ ) {
107
+ return Array.from(selectedTasks);
108
+ }
109
+
110
+ return [taskRecord.id];
111
+ },
112
+ [isMultiSelectMode, selectedTasks]
113
+ );
114
+
97
115
  const getExternalMoveOptions = useCallback((targetList: TaskList) => {
98
116
  const options: {
99
117
  sourceStatus?: TaskBoardStatus;
@@ -389,13 +407,8 @@ export function useTaskActions({
389
407
 
390
408
  setIsLoading(true);
391
409
 
392
- // Check if we're in multi-select mode and have multiple tasks selected
393
- const shouldBulkMove =
394
- isMultiSelectMode &&
395
- selectedTasks &&
396
- selectedTasks.size > 1 &&
397
- selectedTasks.has(task.id);
398
- const tasksToMove = shouldBulkMove ? Array.from(selectedTasks) : [task.id];
410
+ const tasksToMove = getEffectiveTaskIds(task);
411
+ const shouldBulkMove = tasksToMove.length > 1;
399
412
 
400
413
  // Store previous state for rollback
401
414
  const previousTasks = queryClient.getQueryData<Task[]>(['tasks', boardId]);
@@ -515,8 +528,7 @@ export function useTaskActions({
515
528
  task?.id,
516
529
  setIsLoading,
517
530
  setMenuOpen,
518
- isMultiSelectMode,
519
- selectedTasks,
531
+ getEffectiveTaskIds,
520
532
  queryClient,
521
533
  boardId,
522
534
  task,
@@ -531,13 +543,8 @@ export function useTaskActions({
531
543
 
532
544
  setIsLoading(true);
533
545
 
534
- // Check if we're in multi-select mode and have multiple tasks selected
535
- const shouldBulkMove =
536
- isMultiSelectMode &&
537
- selectedTasks &&
538
- selectedTasks.size > 1 &&
539
- selectedTasks.has(task.id);
540
- const tasksToMove = shouldBulkMove ? Array.from(selectedTasks) : [task.id];
546
+ const tasksToMove = getEffectiveTaskIds(task);
547
+ const shouldBulkMove = tasksToMove.length > 1;
541
548
 
542
549
  // Store previous state for rollback
543
550
  const previousTasks = queryClient.getQueryData<Task[]>(['tasks', boardId]);
@@ -649,8 +656,7 @@ export function useTaskActions({
649
656
  task?.id,
650
657
  setIsLoading,
651
658
  setMenuOpen,
652
- isMultiSelectMode,
653
- selectedTasks,
659
+ getEffectiveTaskIds,
654
660
  queryClient,
655
661
  boardId,
656
662
  task,
@@ -664,15 +670,7 @@ export function useTaskActions({
664
670
  if (!task) return;
665
671
  setIsLoading(true);
666
672
 
667
- // Check if we're in multi-select mode and have multiple tasks selected
668
- const shouldBulkDelete =
669
- isMultiSelectMode &&
670
- selectedTasks &&
671
- selectedTasks.size > 1 &&
672
- selectedTasks.has(task.id);
673
- const tasksToDelete = shouldBulkDelete
674
- ? Array.from(selectedTasks)
675
- : [task.id];
673
+ const tasksToDelete = getEffectiveTaskIds(task);
676
674
 
677
675
  const now = new Date().toISOString();
678
676
 
@@ -777,8 +775,7 @@ export function useTaskActions({
777
775
  task?.id,
778
776
  setIsLoading,
779
777
  setDeleteDialogOpen,
780
- isMultiSelectMode,
781
- selectedTasks,
778
+ getEffectiveTaskIds,
782
779
  queryClient,
783
780
  boardId,
784
781
  task,
@@ -923,15 +920,8 @@ export function useTaskActions({
923
920
 
924
921
  setIsLoading(true);
925
922
 
926
- // Check if we're in multi-select mode and have multiple tasks selected
927
- const shouldBulkMove =
928
- isMultiSelectMode &&
929
- selectedTasks &&
930
- selectedTasks.size > 1 &&
931
- selectedTasks.has(task.id);
932
- const tasksToMove = shouldBulkMove
933
- ? Array.from(selectedTasks)
934
- : [task.id];
923
+ const tasksToMove = getEffectiveTaskIds(task);
924
+ const shouldBulkMove = tasksToMove.length > 1;
935
925
 
936
926
  // Store previous state for rollback
937
927
  const previousTasks = queryClient.getQueryData<Task[]>([
@@ -1127,8 +1117,7 @@ export function useTaskActions({
1127
1117
  availableLists,
1128
1118
  setIsLoading,
1129
1119
  setMenuOpen,
1130
- isMultiSelectMode,
1131
- selectedTasks,
1120
+ getEffectiveTaskIds,
1132
1121
  queryClient,
1133
1122
  boardId,
1134
1123
  task,
@@ -1150,15 +1139,7 @@ export function useTaskActions({
1150
1139
  newDate = target.toISOString();
1151
1140
  }
1152
1141
 
1153
- // Check if we're in multi-select mode and have multiple tasks selected
1154
- const shouldBulkUpdate =
1155
- isMultiSelectMode &&
1156
- selectedTasks &&
1157
- selectedTasks.size > 1 &&
1158
- selectedTasks.has(task.id);
1159
- const tasksToUpdate = shouldBulkUpdate
1160
- ? Array.from(selectedTasks)
1161
- : [task.id];
1142
+ const tasksToUpdate = getEffectiveTaskIds(task);
1162
1143
 
1163
1144
  setIsLoading(true);
1164
1145
 
@@ -1260,8 +1241,7 @@ export function useTaskActions({
1260
1241
  task?.id,
1261
1242
  setIsLoading,
1262
1243
  getWorkspaceId,
1263
- isMultiSelectMode,
1264
- selectedTasks,
1244
+ getEffectiveTaskIds,
1265
1245
  queryClient,
1266
1246
  boardId,
1267
1247
  task,
@@ -1274,26 +1254,7 @@ export function useTaskActions({
1274
1254
  if (!task) return;
1275
1255
  if (newPriority === task.priority && !isMultiSelectMode) return;
1276
1256
 
1277
- // Check if we're in multi-select mode and have multiple tasks selected
1278
- const shouldBulkUpdate =
1279
- isMultiSelectMode &&
1280
- selectedTasks &&
1281
- selectedTasks.size > 1 &&
1282
- selectedTasks.has(task.id);
1283
- const tasksToUpdate = shouldBulkUpdate
1284
- ? Array.from(selectedTasks)
1285
- : [task.id];
1286
-
1287
- console.log('🎯 handlePriorityChange called:', {
1288
- taskId: task.id,
1289
- newPriority,
1290
- isMultiSelectMode,
1291
- selectedTasksSize: selectedTasks?.size,
1292
- selectedTasksArray: Array.from(selectedTasks || []),
1293
- shouldBulkUpdate,
1294
- tasksToUpdate,
1295
- tasksToUpdateCount: tasksToUpdate.length,
1296
- });
1257
+ const tasksToUpdate = getEffectiveTaskIds(task);
1297
1258
 
1298
1259
  setIsLoading(true);
1299
1260
 
@@ -1318,12 +1279,6 @@ export function useTaskActions({
1318
1279
  const succeededTaskIds: string[] = [];
1319
1280
  const workspaceId = await getWorkspaceId();
1320
1281
 
1321
- console.log('🔄 Executing sequential API updates:', {
1322
- tasksToUpdate,
1323
- count: tasksToUpdate.length,
1324
- priority: newPriority,
1325
- });
1326
-
1327
1282
  for (const taskId of tasksToUpdate) {
1328
1283
  try {
1329
1284
  await updateWorkspaceTask(workspaceId, taskId, {
@@ -1338,11 +1293,6 @@ export function useTaskActions({
1338
1293
  }
1339
1294
  }
1340
1295
 
1341
- console.log('✅ Sequential update result:', {
1342
- successCount: succeededTaskIds.length,
1343
- totalTasks: tasksToUpdate.length,
1344
- });
1345
-
1346
1296
  if (succeededTaskIds.length === 0) {
1347
1297
  throw new Error('Failed to update any tasks');
1348
1298
  }
@@ -1392,7 +1342,7 @@ export function useTaskActions({
1392
1342
 
1393
1343
  // Don't auto-clear selection - let user manually clear with "Clear" button
1394
1344
  } catch (error) {
1395
- console.error('Failed to update priority:', error);
1345
+ console.error('Failed to update priority:', error);
1396
1346
  // Rollback on error
1397
1347
  if (previousTasks) {
1398
1348
  queryClient.setQueryData(['tasks', boardId], previousTasks);
@@ -1409,12 +1359,12 @@ export function useTaskActions({
1409
1359
  task?.priority,
1410
1360
  setIsLoading,
1411
1361
  getWorkspaceId,
1412
- isMultiSelectMode,
1413
- selectedTasks,
1362
+ getEffectiveTaskIds,
1414
1363
  queryClient,
1415
1364
  boardId,
1416
1365
  task,
1417
1366
  broadcast,
1367
+ isMultiSelectMode,
1418
1368
  ]
1419
1369
  );
1420
1370
 
@@ -1423,20 +1373,11 @@ export function useTaskActions({
1423
1373
  if (!task) return;
1424
1374
  if (points === task.estimation_points && !isMultiSelectMode) return;
1425
1375
 
1426
- // Check if we're in multi-select mode and have multiple tasks selected
1427
- const shouldBulkUpdate =
1428
- isMultiSelectMode &&
1429
- selectedTasks &&
1430
- selectedTasks.size > 1 &&
1431
- selectedTasks.has(task.id);
1432
-
1433
1376
  // Get current tasks from cache to filter out ones that already have the target value
1434
1377
  const currentTasks = queryClient.getQueryData<Task[]>(['tasks', boardId]);
1435
1378
 
1436
1379
  // Filter tasks that actually need updating (don't already have the target estimation)
1437
- const candidateTasks = shouldBulkUpdate
1438
- ? Array.from(selectedTasks)
1439
- : [task.id];
1380
+ const candidateTasks = getEffectiveTaskIds(task);
1440
1381
 
1441
1382
  const tasksToUpdate = currentTasks
1442
1383
  ? candidateTasks.filter((taskId) => {
@@ -1556,7 +1497,7 @@ export function useTaskActions({
1556
1497
  setEstimationSaving,
1557
1498
  getWorkspaceId,
1558
1499
  isMultiSelectMode,
1559
- selectedTasks,
1500
+ getEffectiveTaskIds,
1560
1501
  queryClient,
1561
1502
  boardId,
1562
1503
  task,
@@ -1587,12 +1528,7 @@ export function useTaskActions({
1587
1528
  setIsLoading(true);
1588
1529
  setCustomDateDialogOpen?.(false); // Close dialog immediately when date is selected
1589
1530
 
1590
- // Check if we're in multi-select mode with multiple tasks selected
1591
- const shouldBulkUpdate =
1592
- isMultiSelectMode &&
1593
- selectedTasks &&
1594
- selectedTasks.size > 1 &&
1595
- selectedTasks.has(task.id);
1531
+ const shouldBulkUpdate = getEffectiveTaskIds(task).length > 1;
1596
1532
 
1597
1533
  if (shouldBulkUpdate && bulkUpdateCustomDueDate) {
1598
1534
  // Use the centralized bulk update function from useBulkOperations
@@ -1655,8 +1591,7 @@ export function useTaskActions({
1655
1591
  getWorkspaceId,
1656
1592
  setIsLoading,
1657
1593
  setCustomDateDialogOpen,
1658
- isMultiSelectMode,
1659
- selectedTasks,
1594
+ getEffectiveTaskIds,
1660
1595
  bulkUpdateCustomDueDate,
1661
1596
  task,
1662
1597
  queryClient,
@@ -1676,16 +1611,8 @@ export function useTaskActions({
1676
1611
  task)
1677
1612
  : task;
1678
1613
 
1679
- // Check if we're in multi-select mode with multiple tasks selected
1680
- const shouldBulkUpdate =
1681
- isMultiSelectMode &&
1682
- selectedTasks &&
1683
- selectedTasks.size > 1 &&
1684
- selectedTasks.has(currentTask.id);
1685
-
1686
- const tasksToUpdate = shouldBulkUpdate
1687
- ? Array.from(selectedTasks)
1688
- : [currentTask.id];
1614
+ const tasksToUpdate = getEffectiveTaskIds(currentTask);
1615
+ const shouldBulkUpdate = tasksToUpdate.length > 1;
1689
1616
 
1690
1617
  setIsLoading(true);
1691
1618
 
@@ -1703,7 +1630,7 @@ export function useTaskActions({
1703
1630
 
1704
1631
  if (shouldBulkUpdate && previousTasks) {
1705
1632
  const selectedTasksData = previousTasks.filter((t) =>
1706
- selectedTasks?.has(t.id)
1633
+ tasksToUpdate.includes(t.id)
1707
1634
  );
1708
1635
  // Only mark as active (to remove) if ALL selected tasks have the assignee
1709
1636
  active = selectedTasksData.every((t) =>
@@ -1891,8 +1818,7 @@ export function useTaskActions({
1891
1818
  boardId,
1892
1819
  queryClient,
1893
1820
  setIsLoading,
1894
- isMultiSelectMode,
1895
- selectedTasks,
1821
+ getEffectiveTaskIds,
1896
1822
  broadcast,
1897
1823
  getWorkspaceId,
1898
1824
  ]
@@ -89,7 +89,7 @@ export function useUserBooleanConfig(
89
89
  );
90
90
  const updateConfig = useUpdateUserConfig();
91
91
 
92
- const value = rawValue === 'true';
92
+ const value = rawValue === undefined ? defaultValue : rawValue === 'true';
93
93
 
94
94
  const setValue = (newValue: boolean) => {
95
95
  updateConfig.mutate({ configId, value: newValue ? 'true' : 'false' });
@@ -28,7 +28,10 @@ export const useWorkspaceConfig = <T>(
28
28
  });
29
29
  };
30
30
 
31
- export const useWorkspaceConfigs = (wsId: string, configIds: string[]) => {
31
+ export const useWorkspaceConfigs = (
32
+ wsId: string,
33
+ configIds: readonly string[]
34
+ ) => {
32
35
  return useQuery({
33
36
  queryKey: ['workspace-configs', wsId, configIds],
34
37
  queryFn: async () => {
@@ -43,7 +46,8 @@ export const useWorkspaceConfigs = (wsId: string, configIds: string[]) => {
43
46
  return data ?? {};
44
47
  },
45
48
  enabled: !!wsId && configIds.length > 0,
46
- staleTime: 30_000,
49
+ staleTime: 5 * 60_000,
47
50
  placeholderData: (previousData) => previousData,
51
+ refetchOnWindowFocus: false,
48
52
  });
49
53
  };
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { getCurrentUserProfile } from '@tuturuuu/internal-api';
3
+ import { getCurrentUserProfile } from '@tuturuuu/internal-api/users';
4
4
  import { createClient } from '@tuturuuu/supabase/next/client';
5
5
  import type { RealtimePresenceState } from '@tuturuuu/supabase/next/realtime';
6
6
  import type { User } from '@tuturuuu/types/primitives/User';
@@ -1,94 +0,0 @@
1
- 'use client';
2
-
3
- import { ArrowRightLeft, Check, MoreHorizontal } from '@tuturuuu/icons';
4
- import { Badge } from '@tuturuuu/ui/badge';
5
- import { Button } from '@tuturuuu/ui/button';
6
- import { DropdownMenu, DropdownMenuTrigger } from '@tuturuuu/ui/dropdown-menu';
7
- import { useTranslations } from 'next-intl';
8
- import { BulkActionsMenu } from './bulk-actions-menu';
9
-
10
- interface BulkActionsBarProps {
11
- selectedCount: number;
12
- isMultiSelectMode: boolean;
13
- bulkWorking: boolean;
14
- modKey: string;
15
- onClearSelection: () => void;
16
- onOpenBoardSelector: () => void;
17
- menuProps: React.ComponentProps<typeof BulkActionsMenu>;
18
- }
19
-
20
- export function BulkActionsBar({
21
- selectedCount,
22
- isMultiSelectMode,
23
- bulkWorking,
24
- modKey,
25
- onClearSelection,
26
- onOpenBoardSelector,
27
- menuProps,
28
- }: BulkActionsBarProps) {
29
- const t = useTranslations();
30
- const tc = useTranslations('common');
31
-
32
- if (!isMultiSelectMode) return null;
33
-
34
- return (
35
- <div className="flex flex-wrap items-center justify-between gap-3 border-b bg-linear-to-r from-primary/5 via-primary/3 to-transparent px-4 py-3 shadow-sm">
36
- <div className="flex flex-wrap items-center gap-3 text-xs md:text-sm">
37
- <div className="flex items-center gap-2 rounded-full bg-primary/10 px-3 py-1 ring-1 ring-primary/20">
38
- <Check className="h-3.5 w-3.5 text-primary" />
39
- <span className="font-semibold text-primary">
40
- {selectedCount}{' '}
41
- {selectedCount === 1 ? tc('task') : tc('tasks_plural')}
42
- </span>
43
- </div>
44
- <span className="hidden text-muted-foreground text-xs sm:inline">
45
- {tc('selection_instruction', { modKey })}
46
- </span>
47
- {bulkWorking && (
48
- <Badge
49
- variant="outline"
50
- className="animate-pulse border-dynamic-blue/40 text-[10px]"
51
- >
52
- {t('common.working')}
53
- </Badge>
54
- )}
55
- </div>
56
- <div className="flex flex-wrap items-center gap-2">
57
- <DropdownMenu>
58
- <DropdownMenuTrigger asChild>
59
- <Button
60
- variant="outline"
61
- size="sm"
62
- className="h-6 px-2 text-xs"
63
- disabled={bulkWorking}
64
- >
65
- <MoreHorizontal className="mr-1 h-3 w-3" />{' '}
66
- {t('common.bulk_actions')}
67
- </Button>
68
- </DropdownMenuTrigger>
69
- <BulkActionsMenu {...menuProps} />
70
- </DropdownMenu>
71
-
72
- <Button
73
- variant="outline"
74
- size="sm"
75
- onClick={onOpenBoardSelector}
76
- className="h-6 px-2 text-xs"
77
- disabled={selectedCount === 0 || bulkWorking}
78
- >
79
- <ArrowRightLeft className="mr-1 h-3 w-3" />
80
- {t('common.move')}
81
- </Button>
82
- <Button
83
- variant="ghost"
84
- size="sm"
85
- onClick={onClearSelection}
86
- className="h-6 px-2 text-xs"
87
- disabled={bulkWorking}
88
- >
89
- {t('common.clear')}
90
- </Button>
91
- </div>
92
- </div>
93
- );
94
- }