@tuturuuu/ui 0.2.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 (116) hide show
  1. package/CHANGELOG.md +53 -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/custom/__tests__/settings-dialog-shell.test.tsx +24 -1
  12. package/src/components/ui/custom/__tests__/tuturuuu-logo.test.ts +12 -3
  13. package/src/components/ui/custom/__tests__/workspace-select-helpers.test.ts +39 -0
  14. package/src/components/ui/custom/common-footer.tsx +16 -1
  15. package/src/components/ui/custom/production-indicator.tsx +1 -1
  16. package/src/components/ui/custom/settings/sidebar-settings.tsx +1 -1
  17. package/src/components/ui/custom/settings/task-settings.tsx +18 -0
  18. package/src/components/ui/custom/settings-dialog-shell.tsx +38 -23
  19. package/src/components/ui/custom/sidebar-context-compile-graph.test.ts +60 -0
  20. package/src/components/ui/custom/sidebar-context.tsx +61 -61
  21. package/src/components/ui/custom/sidebar-remote-behavior-bridge.tsx +123 -0
  22. package/src/components/ui/custom/tuturuuu-logo-urls.ts +6 -0
  23. package/src/components/ui/custom/tuturuuu-logo.tsx +25 -7
  24. package/src/components/ui/custom/workspace-select-helpers.ts +20 -0
  25. package/src/components/ui/custom/workspace-select.tsx +33 -12
  26. package/src/components/ui/finance/invoices/components/invoice-checkout-summary.tsx +7 -1
  27. package/src/components/ui/finance/invoices/components/invoice-payment-settings.tsx +3 -0
  28. package/src/components/ui/finance/invoices/components/invoice-products-permission-warning.tsx +58 -0
  29. package/src/components/ui/finance/invoices/components/subscription-group-selector.tsx +12 -20
  30. package/src/components/ui/finance/invoices/hooks/use-subscription-auto-selection.ts +10 -9
  31. package/src/components/ui/finance/invoices/hooks/use-subscription-invoice-content.ts +10 -5
  32. package/src/components/ui/finance/invoices/hooks.ts +75 -20
  33. package/src/components/ui/finance/invoices/new-invoice-page.test.tsx +137 -0
  34. package/src/components/ui/finance/invoices/new-invoice-page.tsx +86 -37
  35. package/src/components/ui/finance/invoices/product-selection.test.tsx +8 -26
  36. package/src/components/ui/finance/invoices/product-selection.tsx +2 -10
  37. package/src/components/ui/finance/invoices/standard-invoice.tsx +88 -26
  38. package/src/components/ui/finance/invoices/subscription-invoice.tsx +154 -46
  39. package/src/components/ui/finance/invoices/utils.test.ts +50 -0
  40. package/src/components/ui/finance/invoices/utils.ts +75 -17
  41. package/src/components/ui/finance/shared/finance-display-amount.tsx +3 -1
  42. package/src/components/ui/finance/shared/finance-permission-warning-dialog.test.tsx +34 -0
  43. package/src/components/ui/finance/shared/finance-permission-warning-dialog.tsx +157 -0
  44. package/src/components/ui/finance/transactions/form-basic-tab.tsx +8 -0
  45. package/src/components/ui/finance/transactions/form-more-tab.tsx +8 -0
  46. package/src/components/ui/finance/transactions/form-types.ts +2 -0
  47. package/src/components/ui/finance/transactions/form.test.tsx +43 -0
  48. package/src/components/ui/finance/transactions/form.tsx +60 -0
  49. package/src/components/ui/finance/transactions/infinite-transactions-list.tsx +27 -0
  50. package/src/components/ui/finance/transactions/transactions-create-summary.tsx +13 -1
  51. package/src/components/ui/finance/transactions/transactions-infinite-page.tsx +4 -0
  52. package/src/components/ui/finance/transactions/transactions-page.tsx +23 -1
  53. package/src/components/ui/finance/wallets/walletId/wallet-details-actions.tsx +4 -0
  54. package/src/components/ui/finance/wallets/walletId/wallet-details-page.tsx +5 -0
  55. package/src/components/ui/legacy/calendar/calendar-content.tsx +9 -1
  56. package/src/components/ui/legacy/calendar/event-modal.tsx +146 -2
  57. package/src/components/ui/legacy/calendar/event-preview-popover.tsx +200 -0
  58. package/src/components/ui/legacy/calendar/smart-calendar.test.tsx +76 -0
  59. package/src/components/ui/legacy/calendar/smart-calendar.tsx +13 -1
  60. package/src/components/ui/legacy/meet/page.test.ts +180 -0
  61. package/src/components/ui/legacy/meet/page.tsx +87 -39
  62. package/src/components/ui/tu-do/boards/boardId/board-column.tsx +79 -25
  63. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/__tests__/bulk-mutations-external-workspaces.test.tsx +392 -0
  64. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-actions-island.test.tsx +57 -0
  65. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-actions-island.tsx +106 -0
  66. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-clear-delete.ts +106 -161
  67. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations-assignees.ts +96 -150
  68. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations-labels.ts +63 -79
  69. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-relations-projects.ts +64 -83
  70. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-mutations-updates.ts +115 -155
  71. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operation-utils.ts +319 -2
  72. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-operations.ts +8 -1
  73. package/src/components/ui/tu-do/boards/boardId/kanban/dnd/use-kanban-dnd.ts +63 -37
  74. package/src/components/ui/tu-do/boards/boardId/kanban/kanban-column-collapse.ts +16 -0
  75. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.test.tsx +46 -0
  76. package/src/components/ui/tu-do/boards/boardId/kanban/rendering/kanban-columns.tsx +5 -3
  77. package/src/components/ui/tu-do/boards/boardId/kanban.tsx +19 -7
  78. package/src/components/ui/tu-do/boards/boardId/menus/__tests__/task-menus.test.tsx +181 -2
  79. package/src/components/ui/tu-do/boards/boardId/menus/index.ts +1 -0
  80. package/src/components/ui/tu-do/boards/boardId/menus/task-scheduling-menu.tsx +463 -0
  81. package/src/components/ui/tu-do/boards/boardId/menus/task-scheduling-utils.ts +109 -0
  82. package/src/components/ui/tu-do/boards/boardId/task-board-server-page.tsx +4 -0
  83. package/src/components/ui/tu-do/boards/boardId/task-card/TaskCardCheckbox.tsx +6 -3
  84. package/src/components/ui/tu-do/boards/boardId/task-card/TaskCardDates.tsx +26 -9
  85. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-checkbox-style.ts +39 -0
  86. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.test.ts +43 -0
  87. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-comparator.ts +33 -0
  88. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-completion-checkbox-visibility.test.ts +31 -0
  89. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-completion-checkbox-visibility.ts +9 -0
  90. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-identifier-row.test.tsx +124 -0
  91. package/src/components/ui/tu-do/boards/boardId/task-card/task-card-identifier-row.tsx +88 -0
  92. package/src/components/ui/tu-do/boards/boardId/task-card/task-card.tsx +151 -76
  93. package/src/components/ui/tu-do/boards/boardId/task-card/task-scheduling-badge.tsx +174 -0
  94. package/src/components/ui/tu-do/providers/task-dialog-provider.tsx +34 -13
  95. package/src/components/ui/tu-do/shared/__tests__/board-client.test.tsx +54 -1
  96. package/src/components/ui/tu-do/shared/__tests__/board-views.test.tsx +158 -0
  97. package/src/components/ui/tu-do/shared/__tests__/task-dialog-manager.test.tsx +5 -2
  98. package/src/components/ui/tu-do/shared/board-client.tsx +12 -2
  99. package/src/components/ui/tu-do/shared/board-views.tsx +195 -328
  100. package/src/components/ui/tu-do/shared/list-view.tsx +18 -8
  101. package/src/components/ui/tu-do/shared/task-due-date-visibility.test.ts +72 -0
  102. package/src/components/ui/tu-do/shared/task-due-date-visibility.ts +38 -0
  103. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-mutations.ts +6 -3
  104. package/src/components/ui/tu-do/shared/task-edit-dialog/hooks/use-task-save.ts +2 -2
  105. package/src/components/ui/tu-do/shared/task-row-actions-menu.tsx +33 -0
  106. package/src/hooks/__tests__/use-calendar-readonly.test.tsx +74 -3
  107. package/src/hooks/__tests__/use-task-actions.test.tsx +118 -0
  108. package/src/hooks/__tests__/use-user-config.test.tsx +65 -0
  109. package/src/hooks/__tests__/use-workspace-presence.test.tsx +1 -1
  110. package/src/hooks/use-calendar-sync.tsx +22 -277
  111. package/src/hooks/use-calendar.tsx +95 -525
  112. package/src/hooks/use-task-actions.ts +43 -117
  113. package/src/hooks/use-user-config.ts +1 -1
  114. package/src/hooks/use-workspace-config.ts +6 -2
  115. package/src/hooks/use-workspace-presence.ts +1 -1
  116. package/src/components/ui/tu-do/boards/boardId/kanban/bulk/bulk-actions-bar.tsx +0 -94
@@ -28,6 +28,10 @@ type CalendarConnection = {
28
28
  calendar_name: string;
29
29
  is_enabled: boolean;
30
30
  color: string | null;
31
+ provider?: 'google' | 'microsoft' | string;
32
+ auth_token_id?: string | null;
33
+ workspace_calendar_id?: string | null;
34
+ access_role?: string | null;
31
35
  created_at: string;
32
36
  updated_at: string;
33
37
  };
@@ -161,7 +165,7 @@ type CacheUpdate = {
161
165
  export const CalendarSyncProvider = ({
162
166
  children,
163
167
  wsId,
164
- experimentalGoogleToken,
168
+ experimentalGoogleToken: _experimentalGoogleToken,
165
169
  initialCalendarConnections = [],
166
170
  }: {
167
171
  children: React.ReactNode;
@@ -170,9 +174,7 @@ export const CalendarSyncProvider = ({
170
174
  initialCalendarConnections?: CalendarConnection[];
171
175
  }) => {
172
176
  const [data, setData] = useState<WorkspaceCalendarEvent[] | null>(null);
173
- const [googleData, setGoogleData] = useState<WorkspaceCalendarEvent[] | null>(
174
- null
175
- );
177
+ const [googleData] = useState<WorkspaceCalendarEvent[] | null>(null);
176
178
  const [events, setEvents] = useState<CalendarEventWithHabitInfo[]>([]);
177
179
 
178
180
  const [error, setError] = useState<Error | null>(null);
@@ -184,11 +186,9 @@ export const CalendarSyncProvider = ({
184
186
  const [calendarCache, setCalendarCache] = useState<CalendarCache>({});
185
187
  const [isSyncing, setIsSyncing] = useState(false);
186
188
  const [syncStatus, setSyncStatus] = useState<SyncStatus>({ state: 'idle' });
187
- const prevGoogleDataRef = useRef<string>('');
188
189
  const prevDatesRef = useRef<string>('');
189
190
  const isForcedRef = useRef<boolean>(false);
190
191
  const lastSyncTimeRef = useRef<number>(0);
191
- const syncDebounceTimerRef = useRef<NodeJS.Timeout | null>(null);
192
192
  const queryClient = useQueryClient();
193
193
 
194
194
  // Calendar connections state
@@ -449,74 +449,14 @@ export const CalendarSyncProvider = ({
449
449
  refetchInterval: 60000, // Reduced from 30s to 60s to lower load
450
450
  });
451
451
 
452
- // Fetch google events with caching
453
- const { data: fetchedGoogleData, isLoading: isGoogleLoading } = useQuery({
452
+ // Legacy direct Google fetch/reconcile is disabled. Provider inbound sync is
453
+ // owned by the workspace sync route so account/calendar identity stays scoped.
454
+ const { isLoading: isGoogleLoading } = useQuery({
454
455
  queryKey: ['googleCalendarEvents', wsId, getCacheKey(dates)],
455
- enabled:
456
- !!wsId && experimentalGoogleToken?.ws_id === wsId && dates.length > 0,
456
+ enabled: false,
457
457
  staleTime: 30000, // Consider data fresh for 30 seconds
458
458
  gcTime: 5 * 60 * 1000, // Keep in cache for 5 minutes
459
- queryFn: async () => {
460
- const cacheKey = getCacheKey(dates);
461
- if (!cacheKey) return null;
462
-
463
- const cachedData = calendarCache[cacheKey];
464
-
465
- // If we have cached data and it's not stale, return it immediately
466
- if (
467
- cachedData?.googleEvents &&
468
- cachedData.googleEvents.length > 0 &&
469
- !isCacheStaleEnhanced(cachedData.googleLastUpdated, dates)
470
- ) {
471
- setGoogleData(cachedData.googleEvents);
472
- return cachedData.googleEvents;
473
- }
474
-
475
- // Otherwise fetch fresh data
476
- const startDate = dayjs(dates[0]).startOf('day');
477
- const endDate = dayjs(dates[dates.length - 1]).endOf('day');
478
-
479
- const response = await fetch(
480
- `/api/v1/calendar/auth/fetch?wsId=${wsId}&startDate=${startDate.toISOString()}&endDate=${endDate.toISOString()}`,
481
- { cache: 'no-store' }
482
- );
483
- const googleResponse = await response.json();
484
-
485
- if (!response.ok) {
486
- const errorMessage =
487
- googleResponse.error +
488
- '. ' +
489
- googleResponse.googleError +
490
- ': ' +
491
- googleResponse.details?.reason;
492
- setError(new Error(errorMessage));
493
-
494
- // Notify user of Google Calendar fetch failure
495
- toast.error('Failed to fetch Google Calendar', {
496
- description:
497
- errorMessage || 'Could not retrieve events from Google Calendar',
498
- duration: 5000,
499
- });
500
-
501
- setSyncStatus({
502
- state: 'error',
503
- message: 'failed_to_fetch_google', // Translation key
504
- lastSyncTime: new Date(),
505
- });
506
-
507
- return null;
508
- }
509
-
510
- // Update cache with new data
511
- updateCache(cacheKey, {
512
- googleEvents: googleResponse.events,
513
- googleLastUpdated: Date.now(),
514
- });
515
-
516
- setGoogleData(googleResponse.events);
517
- setError(null);
518
- return googleResponse.events;
519
- },
459
+ queryFn: async () => null,
520
460
  refetchInterval: 60000, // Reduced from 30s to 60s to lower load
521
461
  });
522
462
 
@@ -708,59 +648,6 @@ export const CalendarSyncProvider = ({
708
648
  [wsId, isActiveSyncOn, refresh]
709
649
  );
710
650
 
711
- // Sync to Tuturuuu database when google data changes for current view (debounced)
712
- useEffect(() => {
713
- // If have not connected to google, don't sync
714
- if (experimentalGoogleToken?.ws_id !== wsId) {
715
- return;
716
- }
717
-
718
- // Convert current data to strings for comparison
719
- const currentGoogleDataStr = JSON.stringify(fetchedGoogleData);
720
-
721
- // Only sync if the data has actually changed
722
- const hasDataChanged = currentGoogleDataStr !== prevGoogleDataRef.current;
723
-
724
- if (hasDataChanged) {
725
- // Clear any pending sync
726
- if (syncDebounceTimerRef.current) {
727
- clearTimeout(syncDebounceTimerRef.current);
728
- }
729
-
730
- // Debounce sync by 2 seconds to prevent rapid consecutive syncs
731
- syncDebounceTimerRef.current = setTimeout(() => {
732
- syncToTuturuuu();
733
- prevGoogleDataRef.current = currentGoogleDataStr;
734
- }, 2000);
735
- }
736
-
737
- // Cleanup on unmount
738
- return () => {
739
- if (syncDebounceTimerRef.current) {
740
- clearTimeout(syncDebounceTimerRef.current);
741
- }
742
- };
743
- }, [fetchedGoogleData, syncToTuturuuu, wsId, experimentalGoogleToken?.ws_id]);
744
-
745
- // Trigger sync when isActiveSyncOn becomes true
746
- useEffect(() => {
747
- // If have not connected to google, don't sync
748
- if (experimentalGoogleToken?.ws_id !== wsId) {
749
- return;
750
- }
751
-
752
- // Only sync when isActiveSyncOn becomes true and we have Google data
753
- if (isActiveSyncOn && fetchedGoogleData && fetchedGoogleData.length > 0) {
754
- syncToTuturuuu();
755
- }
756
- }, [
757
- isActiveSyncOn,
758
- fetchedGoogleData,
759
- syncToTuturuuu,
760
- wsId,
761
- experimentalGoogleToken?.ws_id,
762
- ]);
763
-
764
651
  // Trigger refetch from DB when changing views (optimized to reduce load)
765
652
  useEffect(() => {
766
653
  // Skip if dates haven't actually changed
@@ -899,15 +786,15 @@ export const CalendarSyncProvider = ({
899
786
  visibleDatabaseEvents as CalendarEvent[]
900
787
  );
901
788
 
902
- // Filter events by enabled calendars
903
- // If no calendar connections exist (not using Google Calendar), show all events
904
- // If connections exist, only show events from enabled calendars or events without a google_calendar_id
789
+ // Filter external events by enabled provider calendars. Local Tuturuuu
790
+ // events are always shown here; native calendar visibility is handled
791
+ // by the workspace calendar endpoints.
905
792
  const filteredEvents =
906
793
  calendarConnections.length > 0
907
794
  ? result.filter((event) => {
908
- const eventCalendarId = (event as any).google_calendar_id;
909
- // Show events without google_calendar_id (manually created events)
910
- // Or events from enabled calendars
795
+ const eventCalendarId =
796
+ (event as any).external_calendar_id ||
797
+ (event as any).google_calendar_id;
911
798
  return (
912
799
  !eventCalendarId || enabledCalendarIds.has(eventCalendarId)
913
800
  );
@@ -978,156 +865,14 @@ export const CalendarSyncProvider = ({
978
865
  }, []);
979
866
 
980
867
  const syncToGoogle = useCallback(async () => {
981
- // Helper to dispatch debug logs
982
- const logDebug = (
983
- type: 'info' | 'success' | 'warning' | 'error',
984
- message: string,
985
- details?: any
986
- ) => {
987
- if (typeof window !== 'undefined') {
988
- window.dispatchEvent(
989
- new CustomEvent('calendar-debug-log', {
990
- detail: {
991
- id: `${Date.now()}-${Math.random()}`,
992
- timestamp: new Date(),
993
- type,
994
- message,
995
- details,
996
- },
997
- })
998
- );
999
- }
1000
- console.log(
1001
- `[SYNC TO GOOGLE ${type.toUpperCase()}]`,
1002
- message,
1003
- details || ''
1004
- );
1005
- };
1006
-
1007
- logDebug('info', '🚀 Starting sync to Google Calendar', {
1008
- wsId,
1009
- hasGoogleToken: !!experimentalGoogleToken,
1010
- dateRange: dates.map((d) => d.toISOString()),
1011
- });
1012
-
1013
- if (!experimentalGoogleToken || !wsId) {
1014
- logDebug('error', 'Google Calendar not connected');
1015
- toast.error('Google Calendar not connected', {
1016
- description: 'Please connect your Google Calendar first',
1017
- });
1018
- return;
1019
- }
1020
-
1021
- setIsSyncing(true);
868
+ toast.info('Provider events sync when you create or edit them.');
1022
869
  setSyncStatus({
1023
- state: 'syncing',
1024
- message: 'syncing_to_google', // Translation key
870
+ state: 'success',
871
+ message: 'provider_writes_on_save',
872
+ lastSyncTime: new Date(),
1025
873
  direction: 'tuturuuu-to-google',
1026
874
  });
1027
-
1028
- try {
1029
- const startDate = dayjs(dates[0]).startOf('day');
1030
- const endDate = dayjs(dates[dates.length - 1]).endOf('day');
1031
-
1032
- logDebug('info', '📅 Date range for sync', {
1033
- startDate: startDate.toISOString(),
1034
- endDate: endDate.toISOString(),
1035
- });
1036
-
1037
- logDebug('info', '🔍 Current events in memory', {
1038
- totalEvents: events.length,
1039
- eventsWithGoogleId: events.filter((e: any) => e.google_event_id).length,
1040
- eventsWithoutGoogleId: events.filter((e: any) => !e.google_event_id)
1041
- .length,
1042
- });
1043
-
1044
- const requestBody = {
1045
- wsId,
1046
- startDate: startDate.toISOString(),
1047
- endDate: endDate.toISOString(),
1048
- };
1049
-
1050
- logDebug('info', '📤 Sending API request', requestBody);
1051
-
1052
- const response = await fetch('/api/v1/calendar/auth/sync-to-google', {
1053
- method: 'POST',
1054
- headers: { 'Content-Type': 'application/json' },
1055
- body: JSON.stringify(requestBody),
1056
- });
1057
-
1058
- logDebug('info', '📥 API response received', {
1059
- status: response.status,
1060
- ok: response.ok,
1061
- });
1062
-
1063
- const result = await response.json();
1064
-
1065
- logDebug('info', '📊 API response data', result);
1066
-
1067
- if (!response.ok) {
1068
- logDebug('error', 'API request failed', result);
1069
- throw new Error(result.error || 'Failed to sync to Google Calendar');
1070
- }
1071
-
1072
- // Update sync status
1073
- setSyncStatus({
1074
- state: 'success',
1075
- message: `Synced ${result.syncedCount} event(s) to Google Calendar`,
1076
- lastSyncTime: new Date(),
1077
- direction: 'tuturuuu-to-google',
1078
- });
1079
-
1080
- logDebug(
1081
- 'success',
1082
- `✅ Sync completed: ${result.syncedCount} events synced`,
1083
- result
1084
- );
1085
-
1086
- // Show success notification
1087
- toast.success('Synced to Google Calendar', {
1088
- description: `${result.syncedCount} event(s) synced successfully`,
1089
- });
1090
-
1091
- // If there were errors, show them
1092
- if (result.errorCount > 0 && result.errors) {
1093
- logDebug(
1094
- 'warning',
1095
- `⚠️ ${result.errorCount} events failed to sync`,
1096
- result.errors
1097
- );
1098
- toast.warning('Some events failed to sync', {
1099
- description: `${result.errorCount} event(s) failed. Check debug panel for details.`,
1100
- duration: 7000,
1101
- });
1102
- }
1103
-
1104
- // Refresh to ensure we have the latest data
1105
- logDebug('info', '🔄 Refreshing events from database');
1106
- refresh();
1107
- } catch (error) {
1108
- const errorMessage =
1109
- error instanceof Error
1110
- ? error.message
1111
- : 'An unexpected error occurred while syncing to Google';
1112
-
1113
- logDebug('error', '❌ Sync failed', { error, errorMessage });
1114
-
1115
- setError(error instanceof Error ? error : new Error(errorMessage));
1116
- setSyncStatus({
1117
- state: 'error',
1118
- message: errorMessage,
1119
- lastSyncTime: new Date(),
1120
- });
1121
-
1122
- toast.error('Failed to sync to Google Calendar', {
1123
- description: errorMessage,
1124
- duration: 7000,
1125
- });
1126
- } finally {
1127
- setIsSyncing(false);
1128
- logDebug('info', '🏁 Sync operation completed');
1129
- }
1130
- }, [wsId, dates, experimentalGoogleToken, refresh, events]);
875
+ }, []);
1131
876
 
1132
877
  const value = {
1133
878
  data,