@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
@@ -0,0 +1,414 @@
1
+ import {
2
+ Calendar,
3
+ ChevronDown,
4
+ ExternalLink,
5
+ Eye,
6
+ EyeOff,
7
+ Loader2,
8
+ Lock,
9
+ RefreshCw,
10
+ Settings,
11
+ } from '@tuturuuu/icons';
12
+ import Image from 'next/image';
13
+ import { Badge } from '../../badge';
14
+ import { Button } from '../../button';
15
+ import {
16
+ Collapsible,
17
+ CollapsibleContent,
18
+ CollapsibleTrigger,
19
+ } from '../../collapsible';
20
+ import {
21
+ Dialog,
22
+ DialogContent,
23
+ DialogDescription,
24
+ DialogHeader,
25
+ DialogTitle,
26
+ DialogTrigger,
27
+ } from '../../dialog';
28
+ import { Popover, PopoverContent, PopoverTrigger } from '../../popover';
29
+ import { Separator } from '../../separator';
30
+ import { CalendarConnectionsSettingsContent } from './calendar-connections-settings-content';
31
+ import type { CalendarConnectionsManagerState } from './use-calendar-connections-manager';
32
+
33
+ export function CalendarConnectionsCompact({
34
+ state,
35
+ }: {
36
+ state: CalendarConnectionsManagerState;
37
+ }) {
38
+ const {
39
+ accounts,
40
+ calendarConnections,
41
+ calendarsByAccount,
42
+ enabledCount,
43
+ expandedPopoverAccounts,
44
+ getCalendarColor,
45
+ googleAuthMutation,
46
+ handleToggle,
47
+ hasConnectedAccounts,
48
+ isLoadingCalendars,
49
+ manualSyncDisabled,
50
+ microsoftAuthMutation,
51
+ setExpandedPopoverAccounts,
52
+ syncHealth,
53
+ syncMutation,
54
+ syncStatusStyles,
55
+ syncToTuturuuu,
56
+ t,
57
+ togglingIds,
58
+ togglingTuturuuuIds,
59
+ toggleWorkspaceCalendarMutation,
60
+ userEmail,
61
+ workspaceCalendars,
62
+ } = state;
63
+
64
+ return (
65
+ <div className="flex items-center gap-2">
66
+ {/* Quick calendar visibility toggle */}
67
+ <Popover>
68
+ <PopoverTrigger asChild>
69
+ <Button variant="outline" size="sm" className="gap-2">
70
+ <Calendar className="h-4 w-4" />
71
+ <span className="hidden sm:inline">{t('calendars')}</span>
72
+ {enabledCount > 0 && (
73
+ <Badge variant="secondary" className="ml-1">
74
+ {enabledCount}
75
+ </Badge>
76
+ )}
77
+ </Button>
78
+ </PopoverTrigger>
79
+ <PopoverContent className="w-80" align="end">
80
+ <div className="space-y-4">
81
+ <div className="flex items-center justify-between">
82
+ <div>
83
+ <h4 className="font-medium text-sm">
84
+ {t('visible_calendars')}
85
+ </h4>
86
+ <p className="text-muted-foreground text-xs">
87
+ {enabledCount} {t('calendars_selected') || 'selected'}
88
+ </p>
89
+ </div>
90
+ <Dialog>
91
+ <DialogTrigger asChild>
92
+ <Button variant="ghost" size="icon" className="h-8 w-8">
93
+ <Settings className="h-4 w-4" />
94
+ </Button>
95
+ </DialogTrigger>
96
+ <DialogContent className="max-h-[80vh] overflow-y-auto sm:max-w-lg">
97
+ <DialogHeader>
98
+ <DialogTitle>{t('manage_calendar_accounts')}</DialogTitle>
99
+ <DialogDescription>
100
+ {t('manage_calendar_accounts_desc')}
101
+ </DialogDescription>
102
+ </DialogHeader>
103
+
104
+ <CalendarConnectionsSettingsContent state={state} />
105
+ </DialogContent>
106
+ </Dialog>
107
+ </div>
108
+
109
+ {!hasConnectedAccounts && (
110
+ <div className="rounded-xl border border-border/60 bg-muted/20 p-3">
111
+ <div className="space-y-1">
112
+ <p className="font-medium text-sm">
113
+ {t('connect_calendar_accounts') ||
114
+ 'Connect calendar accounts'}
115
+ </p>
116
+ <p className="text-muted-foreground text-xs">
117
+ {t('connect_calendar_accounts_desc') ||
118
+ 'Link Google or Outlook to keep external calendars visible and continuously synchronized.'}
119
+ </p>
120
+ </div>
121
+ <div className="mt-3 grid grid-cols-2 gap-2">
122
+ <Button
123
+ variant="outline"
124
+ size="sm"
125
+ className="gap-2"
126
+ onClick={() => googleAuthMutation.mutate()}
127
+ disabled={googleAuthMutation.isPending}
128
+ >
129
+ {googleAuthMutation.isPending ? (
130
+ <Loader2 className="h-4 w-4 animate-spin" />
131
+ ) : (
132
+ <Image
133
+ src="/media/logos/google.svg"
134
+ alt="Google"
135
+ width={16}
136
+ height={16}
137
+ />
138
+ )}
139
+ {t('google')}
140
+ </Button>
141
+ <Button
142
+ variant="outline"
143
+ size="sm"
144
+ className="gap-2"
145
+ onClick={() => microsoftAuthMutation.mutate()}
146
+ disabled={microsoftAuthMutation.isPending}
147
+ >
148
+ {microsoftAuthMutation.isPending ? (
149
+ <Loader2 className="h-4 w-4 animate-spin" />
150
+ ) : (
151
+ <Image
152
+ src="/media/logos/microsoft.svg"
153
+ alt="Microsoft"
154
+ width={16}
155
+ height={16}
156
+ />
157
+ )}
158
+ {t('outlook')}
159
+ </Button>
160
+ </div>
161
+ </div>
162
+ )}
163
+
164
+ <Separator />
165
+
166
+ {/* Calendar list grouped by source */}
167
+ <div className="max-h-64 space-y-3 overflow-y-auto">
168
+ {isLoadingCalendars ? (
169
+ <div className="flex items-center justify-center py-6">
170
+ <Loader2 className="h-5 w-5 animate-spin text-muted-foreground" />
171
+ </div>
172
+ ) : (
173
+ <>
174
+ {/* Tuturuuu Calendars */}
175
+ {workspaceCalendars.length > 0 && (
176
+ <Collapsible
177
+ open={expandedPopoverAccounts.has('tuturuuu')}
178
+ onOpenChange={(open) => {
179
+ setExpandedPopoverAccounts((prev) => {
180
+ const next = new Set(prev);
181
+ if (open) next.add('tuturuuu');
182
+ else next.delete('tuturuuu');
183
+ return next;
184
+ });
185
+ }}
186
+ >
187
+ <CollapsibleTrigger className="flex w-full cursor-pointer items-center gap-2 rounded-md px-1 py-1 hover:bg-muted/50">
188
+ <ChevronDown
189
+ className={`h-3 w-3 shrink-0 text-muted-foreground transition-transform ${expandedPopoverAccounts.has('tuturuuu') ? '' : '-rotate-90'}`}
190
+ />
191
+ <Image
192
+ src="/icon-512x512.png"
193
+ alt="Tuturuuu"
194
+ width={14}
195
+ height={14}
196
+ />
197
+ <div className="flex min-w-0 flex-1 flex-col text-left">
198
+ <span className="font-medium text-muted-foreground text-xs">
199
+ {t('tuturuuu_calendars') || 'Tuturuuu'}
200
+ </span>
201
+ {userEmail && (
202
+ <span className="truncate text-[10px] text-muted-foreground">
203
+ {userEmail}
204
+ </span>
205
+ )}
206
+ </div>
207
+ </CollapsibleTrigger>
208
+ <CollapsibleContent className="space-y-1 pt-1">
209
+ {workspaceCalendars.map((cal) => (
210
+ <div
211
+ key={cal.id}
212
+ className="flex items-center justify-between rounded-md px-2 py-1 hover:bg-muted/50"
213
+ >
214
+ <div className="flex items-center gap-2">
215
+ <div
216
+ className="h-2.5 w-2.5 rounded-full"
217
+ style={{
218
+ backgroundColor: getCalendarColor(
219
+ cal.color || 'BLUE'
220
+ ),
221
+ }}
222
+ />
223
+ <span className="line-clamp-1 max-w-45 break-all text-sm">
224
+ {cal.name}
225
+ </span>
226
+ {cal.is_system && (
227
+ <Lock className="h-2.5 w-2.5 text-muted-foreground" />
228
+ )}
229
+ </div>
230
+ <Button
231
+ variant="ghost"
232
+ size="icon"
233
+ className="h-6 w-6"
234
+ onClick={() =>
235
+ toggleWorkspaceCalendarMutation.mutate({
236
+ id: cal.id,
237
+ is_enabled: !cal.is_enabled,
238
+ })
239
+ }
240
+ disabled={togglingTuturuuuIds.has(cal.id)}
241
+ >
242
+ {togglingTuturuuuIds.has(cal.id) ? (
243
+ <Loader2 className="h-3.5 w-3.5 animate-spin" />
244
+ ) : cal.is_enabled ? (
245
+ <Eye className="h-3.5 w-3.5 text-primary" />
246
+ ) : (
247
+ <EyeOff className="h-3.5 w-3.5 text-muted-foreground" />
248
+ )}
249
+ </Button>
250
+ </div>
251
+ ))}
252
+ </CollapsibleContent>
253
+ </Collapsible>
254
+ )}
255
+
256
+ {/* External accounts calendars */}
257
+ {accounts.map((account) => {
258
+ const accountCals = calendarsByAccount[account.id] || [];
259
+ if (accountCals.length === 0) return null;
260
+ const accountKey = `account-${account.id}`;
261
+
262
+ return (
263
+ <Collapsible
264
+ key={account.id}
265
+ open={expandedPopoverAccounts.has(accountKey)}
266
+ onOpenChange={(open) => {
267
+ setExpandedPopoverAccounts((prev) => {
268
+ const next = new Set(prev);
269
+ if (open) next.add(accountKey);
270
+ else next.delete(accountKey);
271
+ return next;
272
+ });
273
+ }}
274
+ >
275
+ <CollapsibleTrigger className="flex w-full cursor-pointer items-center gap-2 rounded-md px-1 py-1 hover:bg-muted/50">
276
+ <ChevronDown
277
+ className={`h-3 w-3 shrink-0 text-muted-foreground transition-transform ${expandedPopoverAccounts.has(accountKey) ? '' : '-rotate-90'}`}
278
+ />
279
+ <Image
280
+ src={
281
+ account.provider === 'google'
282
+ ? '/media/logos/google.svg'
283
+ : '/media/logos/microsoft.svg'
284
+ }
285
+ alt={account.provider}
286
+ width={12}
287
+ height={12}
288
+ />
289
+ <span className="min-w-0 flex-1 truncate text-left text-muted-foreground text-xs">
290
+ {account.account_email || account.account_name}
291
+ </span>
292
+ </CollapsibleTrigger>
293
+ <CollapsibleContent className="space-y-1 pt-1">
294
+ {accountCals.map((cal) => (
295
+ <div
296
+ key={cal.id}
297
+ className="flex items-center justify-between rounded-md px-2 py-1 hover:bg-muted/50"
298
+ >
299
+ <div className="flex items-center gap-2">
300
+ <div
301
+ className="h-2.5 w-2.5 rounded-full"
302
+ style={{
303
+ backgroundColor: cal.color || '#4285f4',
304
+ }}
305
+ />
306
+ <span className="line-clamp-1 max-w-45 break-all text-sm">
307
+ {cal.calendar_name}
308
+ </span>
309
+ </div>
310
+ <Button
311
+ variant="ghost"
312
+ size="icon"
313
+ className="h-6 w-6"
314
+ onClick={() =>
315
+ handleToggle(cal.id, cal.is_enabled, {
316
+ calendar_id: cal.calendar_id,
317
+ calendar_name: cal.calendar_name,
318
+ color: cal.color,
319
+ connectionExists: cal.connectionExists,
320
+ accountId: cal.accountId,
321
+ })
322
+ }
323
+ disabled={togglingIds.has(cal.id)}
324
+ >
325
+ {togglingIds.has(cal.id) ? (
326
+ <Loader2 className="h-3.5 w-3.5 animate-spin" />
327
+ ) : cal.is_enabled ? (
328
+ <Eye className="h-3.5 w-3.5 text-primary" />
329
+ ) : (
330
+ <EyeOff className="h-3.5 w-3.5 text-muted-foreground" />
331
+ )}
332
+ </Button>
333
+ </div>
334
+ ))}
335
+ </CollapsibleContent>
336
+ </Collapsible>
337
+ );
338
+ })}
339
+
340
+ {calendarConnections.length === 0 &&
341
+ workspaceCalendars.length === 0 && (
342
+ <div className="rounded-lg border border-dashed p-4 text-center">
343
+ <p className="font-medium text-sm">
344
+ {t('no_calendars_synced')}
345
+ </p>
346
+ <p className="mt-1 text-muted-foreground text-xs">
347
+ {t('choose_calendars_to_show') ||
348
+ 'Connect an account, then choose which calendars should appear here.'}
349
+ </p>
350
+ </div>
351
+ )}
352
+ </>
353
+ )}
354
+ </div>
355
+
356
+ <Separator />
357
+
358
+ {/* Sync actions */}
359
+ <div className="flex items-center justify-between">
360
+ <div
361
+ className={`rounded-full px-2 py-0.5 text-xs ${syncStatusStyles}`}
362
+ >
363
+ {syncHealth?.state === 'syncing' && (
364
+ <Loader2 className="mr-1 inline h-3 w-3 animate-spin" />
365
+ )}
366
+ {syncHealth?.state === 'degraded'
367
+ ? t('degraded') || 'Degraded'
368
+ : syncHealth?.state === 'healthy'
369
+ ? t('healthy') || 'Healthy'
370
+ : syncHealth?.state === 'disconnected'
371
+ ? t('connect_accounts') || 'Connect accounts'
372
+ : t('syncing_calendars') || 'Syncing calendars'}
373
+ </div>
374
+ <div className="flex gap-1">
375
+ <Button
376
+ variant="ghost"
377
+ size="icon"
378
+ className="h-7 w-7"
379
+ onClick={() => syncMutation.mutate()}
380
+ disabled={manualSyncDisabled}
381
+ title={t('sync_now') || 'Sync now'}
382
+ >
383
+ <ExternalLink
384
+ className={`h-3.5 w-3.5 ${
385
+ manualSyncDisabled ? 'animate-pulse' : ''
386
+ }`}
387
+ />
388
+ </Button>
389
+ <Button
390
+ variant="ghost"
391
+ size="icon"
392
+ className="h-7 w-7"
393
+ onClick={() => syncToTuturuuu()}
394
+ disabled={manualSyncDisabled}
395
+ title={t('sync_from_google')}
396
+ >
397
+ <RefreshCw
398
+ className={`h-3.5 w-3.5 ${manualSyncDisabled ? 'animate-spin' : ''}`}
399
+ />
400
+ </Button>
401
+ </div>
402
+ </div>
403
+ {syncHealth?.lastSuccessAt && (
404
+ <p className="text-muted-foreground text-xs">
405
+ {t('last_synced_at') || 'Last synced'}:{' '}
406
+ {new Date(syncHealth.lastSuccessAt).toLocaleString()}
407
+ </p>
408
+ )}
409
+ </div>
410
+ </PopoverContent>
411
+ </Popover>
412
+ </div>
413
+ );
414
+ }
@@ -1,2 +1,6 @@
1
- export { default as CalendarConnections } from './calendar-connections';
1
+ export type { CalendarConnectionsUnifiedVariant } from './calendar-connections-unified';
2
+ export {
3
+ default,
4
+ default as CalendarConnections,
5
+ } from './calendar-connections-unified';
2
6
  export { ConnectedAccountsDialog } from './connected-accounts-dialog';