@tuturuuu/ui 0.4.0 → 0.5.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 (49) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/package.json +6 -6
  3. package/src/components/ui/chat/chat-agent-details-operations-panel.test.tsx +62 -0
  4. package/src/components/ui/chat/chat-agent-details-zalo-personal-panel.tsx +8 -2
  5. package/src/components/ui/chat/chat-sidebar-conversation-groups.tsx +332 -0
  6. package/src/components/ui/chat/chat-sidebar-groups.test.ts +44 -0
  7. package/src/components/ui/chat/chat-sidebar-panel.test.tsx +2 -0
  8. package/src/components/ui/chat/chat-sidebar-panel.tsx +6 -0
  9. package/src/components/ui/chat/chat-sidebar-sections.ts +199 -0
  10. package/src/components/ui/chat/chat-sidebar.tsx +11 -258
  11. package/src/components/ui/chat/chat-workspace.tsx +5 -0
  12. package/src/components/ui/chat/utils.ts +7 -0
  13. package/src/components/ui/custom/settings/task-settings.tsx +76 -0
  14. package/src/components/ui/custom/settings/task-sound-settings.test.tsx +126 -0
  15. package/src/components/ui/finance/transactions/infinite-transactions-list.tsx +29 -18
  16. package/src/components/ui/finance/transactions/transaction-card.tsx +75 -43
  17. package/src/components/ui/finance/transactions/transfer-merge.test.ts +90 -0
  18. package/src/components/ui/finance/transactions/transfer-merge.ts +52 -0
  19. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-adjustment-dialog.tsx +172 -0
  20. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-amount.tsx +32 -0
  21. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-delete-dialog.tsx +50 -0
  22. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-dialog.tsx +138 -0
  23. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-panel.tsx +196 -0
  24. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-sections.tsx +201 -0
  25. package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoints.test.tsx +277 -0
  26. package/src/components/ui/finance/wallets/checkpoints/wallet-total-check-dialog.tsx +189 -0
  27. package/src/components/ui/finance/wallets/query-invalidation.ts +2 -0
  28. package/src/components/ui/finance/wallets/walletId/wallet-details-page.test.tsx +7 -3
  29. package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +10 -0
  30. package/src/components/ui/finance/wallets/wallets-page.test.tsx +7 -0
  31. package/src/components/ui/finance/wallets/wallets-page.tsx +21 -5
  32. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/__tests__/bulk-mutations-move.test.tsx +14 -0
  33. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operations.ts +29 -0
  34. package/src/components/ui/tu-do/my-tasks/__tests__/use-task-context-actions.test.ts +11 -0
  35. package/src/components/ui/tu-do/my-tasks/use-task-context-actions.ts +10 -0
  36. package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.test.tsx +141 -0
  37. package/src/components/ui/tu-do/shared/task-edit-dialog/components/compact-task-create-popover.tsx +208 -0
  38. package/src/components/ui/tu-do/shared/task-edit-dialog/components/quick-settings-popover.tsx +26 -0
  39. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-list-selector.tsx +36 -20
  40. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-name-input.test.tsx +24 -0
  41. package/src/components/ui/tu-do/shared/task-edit-dialog/components/task-name-input.tsx +18 -3
  42. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-save.test.ts +84 -1
  43. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-save.ts +5 -1
  44. package/src/components/ui/tu-do/shared/task-edit-dialog/task-properties-section.tsx +300 -172
  45. package/src/components/ui/tu-do/shared/task-edit-dialog.tsx +411 -323
  46. package/src/components/ui/tu-do/shared/task-sound-effects.test.ts +189 -0
  47. package/src/components/ui/tu-do/shared/task-sound-effects.tsx +468 -0
  48. package/src/hooks/__tests__/use-task-actions.test.tsx +61 -0
  49. package/src/hooks/use-task-actions.ts +45 -0
@@ -10,11 +10,19 @@ import type { ReactNode } from 'react';
10
10
  import { beforeEach, describe, expect, it, vi } from 'vitest';
11
11
  import { useTaskActions } from '../use-task-actions';
12
12
 
13
+ const { mockDispatchTaskSoundCue } = vi.hoisted(() => ({
14
+ mockDispatchTaskSoundCue: vi.fn(),
15
+ }));
16
+
13
17
  // Mock dependencies
14
18
  vi.mock('@tuturuuu/supabase/next/client', () => ({
15
19
  createClient: vi.fn(),
16
20
  }));
17
21
 
22
+ vi.mock('../../components/ui/tu-do/shared/task-sound-effects', () => ({
23
+ dispatchTaskSoundCue: mockDispatchTaskSoundCue,
24
+ }));
25
+
18
26
  vi.mock('@tuturuuu/ui/sonner', () => ({
19
27
  toast: {
20
28
  success: vi.fn(),
@@ -268,6 +276,12 @@ describe('useTaskActions', () => {
268
276
  expect(mockToast.success).toHaveBeenCalledWith('Task completed', {
269
277
  description: 'Task marked as done and moved to Done',
270
278
  });
279
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledTimes(1);
280
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledWith({
281
+ count: 1,
282
+ cue: 'complete',
283
+ intensity: 1,
284
+ });
271
285
  });
272
286
 
273
287
  it('moves an external task to the personal and source done lists when checked', async () => {
@@ -421,6 +435,11 @@ describe('useTaskActions', () => {
421
435
  expect(mockUpdateWorkspaceTask).toHaveBeenCalledWith('ws-1', 'task-1', {
422
436
  closed_at: expect.any(String),
423
437
  });
438
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledWith({
439
+ count: 1,
440
+ cue: 'complete',
441
+ intensity: 1,
442
+ });
424
443
  });
425
444
 
426
445
  it('prefers task.ws_id over a fallback workspace prop when archiving', async () => {
@@ -667,6 +686,12 @@ describe('useTaskActions', () => {
667
686
  expect(mockToast.success).toHaveBeenCalledWith('2 tasks completed', {
668
687
  description: 'Tasks marked as done',
669
688
  });
689
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledTimes(1);
690
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledWith({
691
+ count: 2,
692
+ cue: 'complete',
693
+ intensity: 1.15,
694
+ });
670
695
  });
671
696
  });
672
697
 
@@ -849,6 +874,12 @@ describe('useTaskActions', () => {
849
874
  expect(mockToast.success).toHaveBeenCalledWith('Success', {
850
875
  description: 'Task deleted successfully',
851
876
  });
877
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledTimes(1);
878
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledWith({
879
+ count: 1,
880
+ cue: 'delete',
881
+ intensity: 1,
882
+ });
852
883
  expect(setDeleteDialogOpen).toHaveBeenCalledWith(false);
853
884
 
854
885
  const deletedTasks = queryClient.getQueryData<Task[]>([
@@ -939,6 +970,12 @@ describe('useTaskActions', () => {
939
970
  expect(mockToast.success).toHaveBeenCalledWith('Due date updated', {
940
971
  description: 'Due date set successfully',
941
972
  });
973
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledTimes(1);
974
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledWith({
975
+ count: 1,
976
+ cue: 'update',
977
+ intensity: 1,
978
+ });
942
979
  });
943
980
 
944
981
  it('should handle bulk due date update', async () => {
@@ -1041,6 +1078,12 @@ describe('useTaskActions', () => {
1041
1078
  expect(mockToast.success).toHaveBeenCalledWith('Priority updated', {
1042
1079
  description: 'Priority changed',
1043
1080
  });
1081
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledTimes(1);
1082
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledWith({
1083
+ count: 1,
1084
+ cue: 'update',
1085
+ intensity: 1,
1086
+ });
1044
1087
  });
1045
1088
 
1046
1089
  it('should handle bulk priority update', async () => {
@@ -1154,6 +1197,12 @@ describe('useTaskActions', () => {
1154
1197
  expect(mockToast.success).toHaveBeenCalledWith('Estimation updated', {
1155
1198
  description: 'Estimation points updated successfully',
1156
1199
  });
1200
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledTimes(1);
1201
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledWith({
1202
+ count: 1,
1203
+ cue: 'update',
1204
+ intensity: 1,
1205
+ });
1157
1206
  });
1158
1207
 
1159
1208
  it('should skip update if estimation points already match', async () => {
@@ -1314,6 +1363,12 @@ describe('useTaskActions', () => {
1314
1363
  expect(mockUpdateWorkspaceTask).toHaveBeenCalledWith('ws-1', 'task-1', {
1315
1364
  end_date: expect.any(String),
1316
1365
  });
1366
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledTimes(1);
1367
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledWith({
1368
+ count: 1,
1369
+ cue: 'update',
1370
+ intensity: 1,
1371
+ });
1317
1372
  });
1318
1373
 
1319
1374
  it('delegates selected-card custom dates to the bulk updater', async () => {
@@ -1625,6 +1680,12 @@ describe('useTaskActions', () => {
1625
1680
  expect(mockUpdateWorkspaceTask).toHaveBeenCalledWith('ws-1', 'task-1', {
1626
1681
  list_id: 'list-2',
1627
1682
  });
1683
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledTimes(1);
1684
+ expect(mockDispatchTaskSoundCue).toHaveBeenCalledWith({
1685
+ count: 1,
1686
+ cue: 'move',
1687
+ intensity: 1,
1688
+ });
1628
1689
  expect(setMenuOpen).toHaveBeenCalledWith(false);
1629
1690
  });
1630
1691
 
@@ -16,6 +16,10 @@ import {
16
16
  import { addDays } from 'date-fns';
17
17
  import { useCallback } from 'react';
18
18
  import { useBoardBroadcast } from '../components/ui/tu-do/shared/board-broadcast-context';
19
+ import {
20
+ dispatchTaskSoundCue,
21
+ type TaskSoundCue,
22
+ } from '../components/ui/tu-do/shared/task-sound-effects';
19
23
  import {
20
24
  isPersonalExternalTask,
21
25
  moveExternalTaskToPersonalList,
@@ -42,6 +46,21 @@ interface UseTaskActionsProps {
42
46
  bulkUpdateCustomDueDate?: (date: Date | null) => Promise<void>;
43
47
  }
44
48
 
49
+ function dispatchTaskActionSound(cue: TaskSoundCue, count = 1) {
50
+ dispatchTaskSoundCue({
51
+ cue,
52
+ count,
53
+ intensity: count > 1 ? 1.15 : 1,
54
+ });
55
+ }
56
+
57
+ function getMoveSoundCue(targetList?: TaskList | null): TaskSoundCue {
58
+ return isTaskBoardCompletedStatus(targetList?.status) ||
59
+ isTaskBoardTerminalStatus(targetList?.status)
60
+ ? 'complete'
61
+ : 'move';
62
+ }
63
+
45
64
  export function useTaskActions({
46
65
  task,
47
66
  boardId,
@@ -252,6 +271,7 @@ export function useTaskActions({
252
271
  toast.success('Task completed', {
253
272
  description: `Task marked as ${targetCompletionList.status === 'done' ? 'done' : 'closed'} and moved to ${targetCompletionList.name}`,
254
273
  });
274
+ dispatchTaskActionSound('complete');
255
275
  } catch (error) {
256
276
  console.error('Failed to complete external task:', error);
257
277
  toast.error('Error', {
@@ -321,6 +341,7 @@ export function useTaskActions({
321
341
  toast.success('Task completed', {
322
342
  description: `Task marked as done and moved to ${targetCompletionList.name}`,
323
343
  });
344
+ dispatchTaskActionSound('complete');
324
345
  } catch (error) {
325
346
  // Rollback on error
326
347
  if (previousTasks) {
@@ -373,6 +394,7 @@ export function useTaskActions({
373
394
  completed_at: updatedTask.completed_at,
374
395
  },
375
396
  });
397
+ dispatchTaskActionSound(newClosedState ? 'complete' : 'update');
376
398
  } catch (error) {
377
399
  // Rollback on error
378
400
  if (previousTasks) {
@@ -429,6 +451,7 @@ export function useTaskActions({
429
451
  toast.success('Task completed', {
430
452
  description: `Task marked as ${targetCompletionList.status === 'done' ? 'done' : 'closed'} and moved to ${targetCompletionList.name}`,
431
453
  });
454
+ dispatchTaskActionSound('complete');
432
455
  return;
433
456
  }
434
457
 
@@ -496,6 +519,7 @@ export function useTaskActions({
496
519
  toast.warning('Partial completion update', {
497
520
  description: `${successCount}/${tasksToMove.length} tasks updated`,
498
521
  });
522
+ dispatchTaskActionSound('complete', successCount);
499
523
  return;
500
524
  }
501
525
 
@@ -509,6 +533,7 @@ export function useTaskActions({
509
533
  : `Task marked as ${targetCompletionList.status === 'done' ? 'done' : 'closed'} and moved to ${targetCompletionList.name}`,
510
534
  }
511
535
  );
536
+ dispatchTaskActionSound('complete', taskCount);
512
537
  } catch (error) {
513
538
  // Rollback on error
514
539
  if (previousTasks) {
@@ -564,6 +589,7 @@ export function useTaskActions({
564
589
  toast.success('Success', {
565
590
  description: 'Task marked as closed',
566
591
  });
592
+ dispatchTaskActionSound('complete');
567
593
  return;
568
594
  }
569
595
 
@@ -627,6 +653,7 @@ export function useTaskActions({
627
653
  toast.warning('Partial close update', {
628
654
  description: `${successCount}/${tasksToMove.length} tasks updated`,
629
655
  });
656
+ dispatchTaskActionSound('complete', successCount);
630
657
  return;
631
658
  }
632
659
 
@@ -637,6 +664,7 @@ export function useTaskActions({
637
664
  ? `${taskCount} tasks marked as closed`
638
665
  : 'Task marked as closed',
639
666
  });
667
+ dispatchTaskActionSound('complete', taskCount);
640
668
  } catch (error) {
641
669
  // Rollback on error
642
670
  if (previousTasks) {
@@ -741,6 +769,7 @@ export function useTaskActions({
741
769
  toast.warning('Partial delete update', {
742
770
  description: `${successCount}/${tasksToDelete.length} tasks deleted`,
743
771
  });
772
+ dispatchTaskActionSound('delete', successCount);
744
773
  return;
745
774
  }
746
775
 
@@ -751,6 +780,7 @@ export function useTaskActions({
751
780
  ? `${taskCount} tasks deleted`
752
781
  : 'Task deleted successfully',
753
782
  });
783
+ dispatchTaskActionSound('delete', taskCount);
754
784
 
755
785
  setDeleteDialogOpen?.(false);
756
786
  } catch (error) {
@@ -817,6 +847,7 @@ export function useTaskActions({
817
847
  toast.success('Success', {
818
848
  description: 'All assignees removed from task',
819
849
  });
850
+ dispatchTaskActionSound('update');
820
851
  } catch (error) {
821
852
  queryClient.setQueryData(['tasks', boardId], previousTasks);
822
853
  console.error('Failed to remove all assignees:', error);
@@ -888,6 +919,7 @@ export function useTaskActions({
888
919
  description: `${assignee?.display_name || assignee?.email || 'Assignee'} removed from task`,
889
920
  });
890
921
  broadcast?.('task:relations-changed', { taskId: task.id });
922
+ dispatchTaskActionSound('update');
891
923
  } catch (error) {
892
924
  queryClient.setQueryData(['tasks', boardId], previousTasks);
893
925
  console.error('Failed to remove assignee:', error);
@@ -977,6 +1009,7 @@ export function useTaskActions({
977
1009
  toast.success('Success', {
978
1010
  description: `Task moved to ${targetList.name || 'selected list'}`,
979
1011
  });
1012
+ dispatchTaskActionSound(getMoveSoundCue(targetList));
980
1013
  } catch (error) {
981
1014
  console.error('Failed to move external task:', error);
982
1015
  toast.error('Error', {
@@ -1087,6 +1120,7 @@ export function useTaskActions({
1087
1120
  toast.warning('Partial move update', {
1088
1121
  description: `${successCount}/${tasksToMove.length} tasks updated`,
1089
1122
  });
1123
+ dispatchTaskActionSound(getMoveSoundCue(targetList), successCount);
1090
1124
  return;
1091
1125
  }
1092
1126
 
@@ -1097,6 +1131,7 @@ export function useTaskActions({
1097
1131
  ? `${taskCount} tasks moved to ${targetList?.name || 'selected list'}`
1098
1132
  : `Task moved to ${targetList?.name || 'selected list'}`,
1099
1133
  });
1134
+ dispatchTaskActionSound(getMoveSoundCue(targetList), taskCount);
1100
1135
  } catch (error) {
1101
1136
  // Rollback on error
1102
1137
  if (previousTasks) {
@@ -1213,6 +1248,7 @@ export function useTaskActions({
1213
1248
  toast.warning('Partial due date update', {
1214
1249
  description: `${succeededTaskIds.length}/${tasksToUpdate.length} tasks updated`,
1215
1250
  });
1251
+ dispatchTaskActionSound('update', succeededTaskIds.length);
1216
1252
  } else {
1217
1253
  const taskCount = tasksToUpdate.length;
1218
1254
  toast.success('Due date updated', {
@@ -1223,6 +1259,7 @@ export function useTaskActions({
1223
1259
  ? 'Due date set successfully'
1224
1260
  : 'Due date removed',
1225
1261
  });
1262
+ dispatchTaskActionSound('update', taskCount);
1226
1263
  }
1227
1264
  } catch (error) {
1228
1265
  console.error('Failed to update due date:', error);
@@ -1328,6 +1365,7 @@ export function useTaskActions({
1328
1365
  toast.warning('Partial priority update', {
1329
1366
  description: `${succeededTaskIds.length}/${tasksToUpdate.length} tasks updated`,
1330
1367
  });
1368
+ dispatchTaskActionSound('update', succeededTaskIds.length);
1331
1369
  } else {
1332
1370
  const taskCount = tasksToUpdate.length;
1333
1371
  toast.success('Priority updated', {
@@ -1338,6 +1376,7 @@ export function useTaskActions({
1338
1376
  ? 'Priority changed'
1339
1377
  : 'Priority cleared',
1340
1378
  });
1379
+ dispatchTaskActionSound('update', taskCount);
1341
1380
  }
1342
1381
 
1343
1382
  // Don't auto-clear selection - let user manually clear with "Clear" button
@@ -1459,6 +1498,7 @@ export function useTaskActions({
1459
1498
  toast.warning('Partial estimation update', {
1460
1499
  description: `${succeededIds.length}/${tasksToUpdate.length} tasks updated`,
1461
1500
  });
1501
+ dispatchTaskActionSound('update', succeededIds.length);
1462
1502
 
1463
1503
  return;
1464
1504
  }
@@ -1476,6 +1516,7 @@ export function useTaskActions({
1476
1516
  ? `${taskCount} tasks updated`
1477
1517
  : 'Estimation points updated successfully',
1478
1518
  });
1519
+ dispatchTaskActionSound('update', taskCount);
1479
1520
 
1480
1521
  return;
1481
1522
  } catch (e: any) {
@@ -1575,6 +1616,7 @@ export function useTaskActions({
1575
1616
  ? 'Custom due date set successfully'
1576
1617
  : 'Due date removed',
1577
1618
  });
1619
+ dispatchTaskActionSound('update');
1578
1620
  } catch (error) {
1579
1621
  if (previousTasks) {
1580
1622
  queryClient.setQueryData(['tasks', boardId], previousTasks);
@@ -1797,6 +1839,9 @@ export function useTaskActions({
1797
1839
  ? `${succeededTaskIds.length} tasks updated`
1798
1840
  : `${assigneeName} ${active ? 'removed' : 'added'} on task`,
1799
1841
  });
1842
+ if (succeededTaskIds.length > 0) {
1843
+ dispatchTaskActionSound('update', succeededTaskIds.length);
1844
+ }
1800
1845
 
1801
1846
  // Don't auto-clear selection - let user manually clear with "Clear" button
1802
1847
  } catch (e: any) {