@tuturuuu/ui 0.30.2 → 0.32.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.
- package/CHANGELOG.md +49 -0
- package/biome.json +1 -1
- package/package.json +95 -49
- package/src/components/ui/calendar-app/calendar-client-page.test.tsx +57 -0
- package/src/components/ui/calendar-app/calendar-client-page.tsx +13 -4
- package/src/components/ui/calendar-app/calendar-page-shell.tsx +7 -1
- package/src/components/ui/calendar-app/components/calendar-connections-compact.tsx +47 -39
- package/src/components/ui/calendar-app/components/calendar-connections-settings-content.tsx +14 -0
- package/src/components/ui/calendar-app/components/calendar-connections-unified.tsx +4 -0
- package/src/components/ui/calendar-app/components/calendar-sync-attention-button.tsx +47 -0
- package/src/components/ui/calendar-app/components/calendar-sync-recovery.test.tsx +112 -0
- package/src/components/ui/calendar-app/components/calendar-sync-recovery.tsx +206 -0
- package/src/components/ui/calendar-app/components/calendar-types.ts +1 -1
- package/src/components/ui/calendar-app/components/use-calendar-connections-manager.ts +86 -90
- package/src/components/ui/chat/ai-message-markdown.tsx +17 -2
- package/src/components/ui/custom/language-dropdown-item.tsx +6 -14
- package/src/components/ui/custom/nav-link.tsx +7 -7
- package/src/components/ui/custom/nav.tsx +15 -63
- package/src/components/ui/custom/notification-card.tsx +361 -0
- package/src/components/ui/custom/notification-list.tsx +178 -0
- package/src/components/ui/custom/notification-popover-client.tsx +12 -780
- package/src/components/ui/custom/notification-runtime.tsx +23 -0
- package/src/components/ui/custom/onboarding-settings-panel.tsx +8 -55
- package/src/components/ui/custom/satellite-account-switcher-menu.tsx +212 -0
- package/src/components/ui/custom/satellite-brand.tsx +122 -0
- package/src/components/ui/custom/satellite-content.tsx +148 -0
- package/src/components/ui/custom/satellite-footer-actions.tsx +350 -0
- package/src/components/ui/custom/satellite-language-item.tsx +26 -0
- package/src/components/ui/custom/satellite-navigation.tsx +86 -0
- package/src/components/ui/custom/satellite-notification-popover.tsx +272 -0
- package/src/components/ui/custom/satellite-onboarding-settings-panel.tsx +80 -0
- package/src/components/ui/custom/satellite-settings-dialog-shell.tsx +621 -0
- package/src/components/ui/custom/satellite-shell-utils.ts +163 -0
- package/src/components/ui/custom/satellite-shell.test.tsx +169 -0
- package/src/components/ui/custom/satellite-shell.tsx +50 -0
- package/src/components/ui/custom/satellite-theme-dropdown-items.tsx +92 -0
- package/src/components/ui/custom/satellite-user-menu-items.tsx +190 -0
- package/src/components/ui/custom/satellite-user-menu.tsx +93 -0
- package/src/components/ui/custom/satellite-workspace-section.tsx +46 -0
- package/src/components/ui/custom/settings/keyboard-shortcuts-settings.tsx +16 -0
- package/src/components/ui/custom/settings-dialog-shell.tsx +21 -600
- package/src/components/ui/custom/sidebar-context.tsx +5 -14
- package/src/components/ui/custom/sidebar-footer-actions.tsx +19 -322
- package/src/components/ui/custom/structure.tsx +14 -0
- package/src/components/ui/custom/system-language-dropdown-item.tsx +7 -14
- package/src/components/ui/custom/theme-dropdown-items.tsx +2 -85
- package/src/components/ui/custom/use-satellite-shell.tsx +165 -0
- package/src/components/ui/legacy/calendar/agenda-view.tsx +139 -47
- package/src/components/ui/legacy/calendar/calendar-content.test.tsx +116 -0
- package/src/components/ui/legacy/calendar/calendar-content.tsx +38 -74
- package/src/components/ui/legacy/calendar/calendar-event-tone.ts +20 -0
- package/src/components/ui/legacy/calendar/calendar-meeting-link.test.ts +27 -0
- package/src/components/ui/legacy/calendar/calendar-meeting-link.ts +34 -0
- package/src/components/ui/legacy/calendar/calendar-period.ts +28 -0
- package/src/components/ui/legacy/calendar/calendar-shortcuts.test.tsx +107 -0
- package/src/components/ui/legacy/calendar/calendar-views.test.tsx +169 -0
- package/src/components/ui/legacy/calendar/event-modal-header.tsx +52 -0
- package/src/components/ui/legacy/calendar/event-modal.tsx +107 -156
- package/src/components/ui/legacy/calendar/month-calendar.tsx +242 -933
- package/src/components/ui/legacy/calendar/save-calendar-event-drafts.test.ts +25 -0
- package/src/components/ui/legacy/calendar/save-calendar-event-drafts.ts +32 -0
- package/src/components/ui/legacy/calendar/smart-calendar.tsx +3 -0
- package/src/components/ui/legacy/calendar/use-calendar-view-shortcuts.ts +54 -0
- package/src/components/ui/legacy/calendar/use-event-draft-session.test.tsx +59 -0
- package/src/components/ui/legacy/calendar/use-event-draft-session.ts +20 -0
- package/src/components/ui/legacy/calendar/year-calendar.tsx +34 -16
- package/src/components/ui/pwa-status.test.tsx +53 -0
- package/src/components/ui/pwa-status.tsx +113 -0
- package/src/components/ui/report-problem-dialog-content.tsx +350 -0
- package/src/components/ui/report-problem-dialog.tsx +6 -975
- package/src/components/ui/report-problem-model.ts +133 -0
- package/src/components/ui/report-problem-mutation.ts +122 -0
- package/src/components/ui/save-notification.test.tsx +36 -0
- package/src/components/ui/save-notification.tsx +17 -0
- package/src/components/ui/use-report-problem.ts +488 -0
- package/src/declarations.d.ts +2 -0
- package/src/hooks/__tests__/use-calendar-readonly.test.tsx +46 -4
- package/src/hooks/__tests__/use-calendar-sync.test.tsx +24 -0
- package/src/hooks/calendar-date-utils.ts +11 -0
- package/src/hooks/calendar-event-lookup.test.ts +77 -0
- package/src/hooks/calendar-event-lookup.ts +47 -0
- package/src/hooks/calendar-range-cache.ts +52 -0
- package/src/hooks/pwa-install-prompt.ts +44 -0
- package/src/hooks/use-calendar-sync.tsx +23 -100
- package/src/hooks/use-calendar.tsx +21 -77
- package/src/hooks/use-notifications.ts +9 -24
- package/src/hooks/use-open-initial-calendar-event.ts +27 -0
- package/src/hooks/use-settings-dialog-shortcut.test.tsx +41 -0
- package/src/hooks/use-settings-dialog-shortcut.ts +12 -14
- package/src/hooks/use-workspace-presence.ts +0 -18
- package/src/lib/tasks-app-url.test.ts +25 -0
- package/src/lib/tasks-app-url.ts +8 -0
|
@@ -34,13 +34,7 @@ import type { CalendarEvent } from '@tuturuuu/types/primitives/calendar-event';
|
|
|
34
34
|
import type { SupportedColor } from '@tuturuuu/types/primitives/SupportedColors';
|
|
35
35
|
import { Badge } from '@tuturuuu/ui/badge';
|
|
36
36
|
import { Button } from '@tuturuuu/ui/button';
|
|
37
|
-
import {
|
|
38
|
-
Dialog,
|
|
39
|
-
DialogContent,
|
|
40
|
-
DialogDescription,
|
|
41
|
-
DialogHeader,
|
|
42
|
-
DialogTitle,
|
|
43
|
-
} from '@tuturuuu/ui/dialog';
|
|
37
|
+
import { Dialog, DialogContent } from '@tuturuuu/ui/dialog';
|
|
44
38
|
import {
|
|
45
39
|
Form,
|
|
46
40
|
FormControl,
|
|
@@ -52,7 +46,7 @@ import {
|
|
|
52
46
|
} from '@tuturuuu/ui/form';
|
|
53
47
|
import { useCalendar } from '@tuturuuu/ui/hooks/use-calendar';
|
|
54
48
|
import { useForm } from '@tuturuuu/ui/hooks/use-form';
|
|
55
|
-
import {
|
|
49
|
+
import { notifySave } from '@tuturuuu/ui/save-notification';
|
|
56
50
|
import { ScrollArea } from '@tuturuuu/ui/scroll-area';
|
|
57
51
|
import {
|
|
58
52
|
Select,
|
|
@@ -73,10 +67,18 @@ import { format } from 'date-fns';
|
|
|
73
67
|
import dayjs from 'dayjs';
|
|
74
68
|
import ts from 'dayjs/plugin/timezone';
|
|
75
69
|
import utc from 'dayjs/plugin/utc';
|
|
76
|
-
import {
|
|
70
|
+
import {
|
|
71
|
+
useCallback,
|
|
72
|
+
useEffect,
|
|
73
|
+
useEffectEvent,
|
|
74
|
+
useMemo,
|
|
75
|
+
useRef,
|
|
76
|
+
useState,
|
|
77
|
+
} from 'react';
|
|
77
78
|
import { z } from 'zod';
|
|
78
79
|
import { Alert, AlertDescription, AlertTitle } from '../../alert';
|
|
79
80
|
import { AutosizeTextarea } from '../../custom/autosize-textarea';
|
|
81
|
+
import { getCalendarMeetingMetadata } from './calendar-meeting-link';
|
|
80
82
|
import {
|
|
81
83
|
COLOR_OPTIONS,
|
|
82
84
|
DateError,
|
|
@@ -88,11 +90,10 @@ import {
|
|
|
88
90
|
EventToggleSwitch,
|
|
89
91
|
OverlapWarning,
|
|
90
92
|
} from './event-form-components';
|
|
91
|
-
import {
|
|
92
|
-
|
|
93
|
-
getCalendarEventProviderDisplay,
|
|
94
|
-
} from './event-provider-display';
|
|
93
|
+
import { EventModalHeader } from './event-modal-header';
|
|
94
|
+
import { saveCalendarEventDrafts } from './save-calendar-event-drafts';
|
|
95
95
|
import { useCalendarSettings } from './settings/settings-context';
|
|
96
|
+
import { useEventDraftSession } from './use-event-draft-session';
|
|
96
97
|
|
|
97
98
|
dayjs.extend(ts);
|
|
98
99
|
dayjs.extend(utc);
|
|
@@ -153,7 +154,7 @@ function findEventSourceOption(
|
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
export function EventModal() {
|
|
156
|
-
const
|
|
157
|
+
const toast = notifySave;
|
|
157
158
|
const startPickerRef = useRef<HTMLButtonElement>(null);
|
|
158
159
|
const endPickerRef = useRef<HTMLButtonElement>(null);
|
|
159
160
|
|
|
@@ -171,7 +172,6 @@ export function EventModal() {
|
|
|
171
172
|
|
|
172
173
|
const tz = settings?.timezone?.timezone;
|
|
173
174
|
|
|
174
|
-
// State for manual event creation/editing
|
|
175
175
|
const [event, setEvent] = useState<Partial<CalendarEvent>>({
|
|
176
176
|
title: '',
|
|
177
177
|
description: '',
|
|
@@ -196,6 +196,7 @@ export function EventModal() {
|
|
|
196
196
|
);
|
|
197
197
|
const selectedSourceOption =
|
|
198
198
|
sourceOptions.find((option) => option.id === selectedSourceId) ??
|
|
199
|
+
findEventSourceOption(sourceOptions, event) ??
|
|
199
200
|
sourceData?.defaultSource ??
|
|
200
201
|
sourceOptions[0];
|
|
201
202
|
|
|
@@ -207,13 +208,11 @@ export function EventModal() {
|
|
|
207
208
|
Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
208
209
|
);
|
|
209
210
|
|
|
210
|
-
// Get the current event being previewed
|
|
211
211
|
const generatedEvent = generatedEvents?.[currentEventIndex];
|
|
212
212
|
|
|
213
213
|
// Determine if we're editing an existing event
|
|
214
214
|
const isEditing = !!(activeEvent?.id && activeEvent.id !== 'new');
|
|
215
215
|
|
|
216
|
-
// Shared state
|
|
217
216
|
const [activeTab, setActiveTab] = useState<'manual' | 'ai' | 'preview'>(
|
|
218
217
|
isEditing ? 'manual' : defaultNewEventTab // Default tab for new events
|
|
219
218
|
);
|
|
@@ -243,13 +242,11 @@ export function EventModal() {
|
|
|
243
242
|
},
|
|
244
243
|
});
|
|
245
244
|
|
|
246
|
-
// AI generation
|
|
247
245
|
const { object, submit, error, isLoading } = useObject({
|
|
248
246
|
api: '/api/v1/calendar/events/generate',
|
|
249
247
|
schema: calendarEventsSchema,
|
|
250
248
|
});
|
|
251
249
|
|
|
252
|
-
// Function to check for overlapping events
|
|
253
250
|
const checkForOverlaps = useCallback(
|
|
254
251
|
(eventToCheck: Partial<CalendarEvent>) => {
|
|
255
252
|
if (!eventToCheck.start_at || !eventToCheck.end_at) return;
|
|
@@ -284,104 +281,95 @@ export function EventModal() {
|
|
|
284
281
|
[activeEvent, getEvents]
|
|
285
282
|
);
|
|
286
283
|
|
|
287
|
-
|
|
284
|
+
const receiveGeneratedEvents = useEffectEvent(() => {
|
|
285
|
+
const processedEvents = (object?.events || []) as Partial<CalendarEvent>[];
|
|
286
|
+
setGeneratedEvents(processedEvents);
|
|
287
|
+
setCurrentEventIndex(0);
|
|
288
|
+
const firstEvent = processedEvents[0];
|
|
289
|
+
if (
|
|
290
|
+
firstEvent?.start_at &&
|
|
291
|
+
firstEvent.end_at &&
|
|
292
|
+
aiForm.getValues().smart_scheduling
|
|
293
|
+
) {
|
|
294
|
+
checkForOverlaps(firstEvent);
|
|
295
|
+
}
|
|
296
|
+
setActiveTab('preview');
|
|
297
|
+
});
|
|
288
298
|
useEffect(() => {
|
|
289
|
-
if (object?.events && !isLoading)
|
|
290
|
-
|
|
291
|
-
|
|
299
|
+
if (object?.events && !isLoading) receiveGeneratedEvents();
|
|
300
|
+
}, [object, isLoading]);
|
|
301
|
+
|
|
302
|
+
// Initialize once per dialog session; live calendar refreshes must not erase edits.
|
|
303
|
+
useEventDraftSession({
|
|
304
|
+
isOpen: isModalOpen,
|
|
305
|
+
eventId: activeEvent?.id,
|
|
306
|
+
workspaceId: activeEvent?.ws_id,
|
|
307
|
+
initialize: () => {
|
|
308
|
+
if (activeEvent) {
|
|
309
|
+
const cleanEventData: Partial<CalendarEvent> = {
|
|
310
|
+
id: activeEvent.id,
|
|
311
|
+
title: activeEvent.title || '',
|
|
312
|
+
description: activeEvent.description || '',
|
|
313
|
+
start_at: activeEvent.start_at,
|
|
314
|
+
end_at: activeEvent.end_at,
|
|
315
|
+
color: activeEvent.color || 'BLUE',
|
|
316
|
+
location: activeEvent.location || '',
|
|
317
|
+
locked: activeEvent.locked || false,
|
|
318
|
+
ws_id: activeEvent.ws_id,
|
|
319
|
+
provider: activeEvent.provider,
|
|
320
|
+
source_calendar_id: activeEvent.source_calendar_id,
|
|
321
|
+
external_calendar_id: activeEvent.external_calendar_id,
|
|
322
|
+
external_event_id: activeEvent.external_event_id,
|
|
323
|
+
google_event_id: activeEvent.google_event_id,
|
|
324
|
+
google_calendar_id: activeEvent.google_calendar_id,
|
|
325
|
+
scheduling_metadata: getCalendarMeetingMetadata(activeEvent),
|
|
326
|
+
};
|
|
292
327
|
|
|
293
|
-
|
|
294
|
-
|
|
328
|
+
setEvent(cleanEventData);
|
|
329
|
+
const sourceOption = findEventSourceOption(
|
|
330
|
+
sourceOptions,
|
|
331
|
+
cleanEventData
|
|
332
|
+
);
|
|
333
|
+
setSelectedSourceId(
|
|
334
|
+
sourceOption?.id ?? sourceData?.defaultSource?.id ?? null
|
|
335
|
+
);
|
|
295
336
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
checkForOverlaps(firstEvent as Partial<CalendarEvent>);
|
|
337
|
+
if (activeEvent.id !== 'new') {
|
|
338
|
+
setIsAllDay(isAllDayEvent(cleanEventData as CalendarEvent));
|
|
339
|
+
} else {
|
|
340
|
+
setIsAllDay(false);
|
|
301
341
|
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
setActiveTab('preview');
|
|
305
|
-
}
|
|
306
|
-
}, [object, isLoading, aiForm, checkForOverlaps]);
|
|
307
|
-
|
|
308
|
-
// Reset form when modal opens/closes or active event changes
|
|
309
|
-
useEffect(() => {
|
|
310
|
-
if (activeEvent) {
|
|
311
|
-
// Clean the event data to only include valid CalendarEvent fields
|
|
312
|
-
const cleanEventData: Partial<CalendarEvent> = {
|
|
313
|
-
id: activeEvent.id,
|
|
314
|
-
title: activeEvent.title || '',
|
|
315
|
-
description: activeEvent.description || '',
|
|
316
|
-
start_at: activeEvent.start_at,
|
|
317
|
-
end_at: activeEvent.end_at,
|
|
318
|
-
color: activeEvent.color || 'BLUE',
|
|
319
|
-
location: activeEvent.location || '',
|
|
320
|
-
locked: activeEvent.locked || false,
|
|
321
|
-
ws_id: activeEvent.ws_id,
|
|
322
|
-
provider: activeEvent.provider,
|
|
323
|
-
source_calendar_id: activeEvent.source_calendar_id,
|
|
324
|
-
external_calendar_id: activeEvent.external_calendar_id,
|
|
325
|
-
external_event_id: activeEvent.external_event_id,
|
|
326
|
-
google_event_id: activeEvent.google_event_id,
|
|
327
|
-
google_calendar_id: activeEvent.google_calendar_id,
|
|
328
|
-
};
|
|
329
342
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
setSelectedSourceId(
|
|
333
|
-
sourceOption?.id ?? sourceData?.defaultSource?.id ?? null
|
|
334
|
-
);
|
|
343
|
+
// Check for overlapping events
|
|
344
|
+
checkForOverlaps(cleanEventData);
|
|
335
345
|
|
|
336
|
-
|
|
337
|
-
if (activeEvent.id !== 'new') {
|
|
338
|
-
setIsAllDay(isAllDayEvent(cleanEventData as CalendarEvent));
|
|
346
|
+
setActiveTab(isEditing ? 'manual' : defaultNewEventTab);
|
|
339
347
|
} else {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
const newEvent = {
|
|
355
|
-
title: '',
|
|
356
|
-
description: '',
|
|
357
|
-
start_at: now.toISOString(),
|
|
358
|
-
end_at: oneHourLater.toISOString(),
|
|
359
|
-
color: 'BLUE' as SupportedColor,
|
|
360
|
-
location: '',
|
|
361
|
-
locked: false,
|
|
362
|
-
ws_id: '',
|
|
363
|
-
};
|
|
348
|
+
const now = new Date();
|
|
349
|
+
const oneHourLater = new Date(now.getTime() + 60 * 60 * 1000);
|
|
350
|
+
|
|
351
|
+
const newEvent = {
|
|
352
|
+
title: '',
|
|
353
|
+
description: '',
|
|
354
|
+
start_at: now.toISOString(),
|
|
355
|
+
end_at: oneHourLater.toISOString(),
|
|
356
|
+
color: 'BLUE' as SupportedColor,
|
|
357
|
+
location: '',
|
|
358
|
+
locked: false,
|
|
359
|
+
ws_id: '',
|
|
360
|
+
};
|
|
364
361
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
362
|
+
setEvent(newEvent);
|
|
363
|
+
setSelectedSourceId(sourceData?.defaultSource?.id ?? null);
|
|
364
|
+
setIsAllDay(false);
|
|
368
365
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
}
|
|
366
|
+
aiForm.reset();
|
|
367
|
+
setGeneratedEvents([]);
|
|
368
|
+
}
|
|
373
369
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
}
|
|
377
|
-
activeEvent,
|
|
378
|
-
checkForOverlaps,
|
|
379
|
-
aiForm,
|
|
380
|
-
defaultNewEventTab,
|
|
381
|
-
isEditing,
|
|
382
|
-
sourceData?.defaultSource?.id,
|
|
383
|
-
sourceOptions,
|
|
384
|
-
]);
|
|
370
|
+
setDateError(null);
|
|
371
|
+
},
|
|
372
|
+
});
|
|
385
373
|
|
|
386
374
|
// Handle manual event save
|
|
387
375
|
const handleManualSave = async () => {
|
|
@@ -412,7 +400,8 @@ export function EventModal() {
|
|
|
412
400
|
};
|
|
413
401
|
|
|
414
402
|
if (activeEvent?.id === 'new') {
|
|
415
|
-
await addEvent(eventData as Omit<CalendarEvent, 'id'>);
|
|
403
|
+
const saved = await addEvent(eventData as Omit<CalendarEvent, 'id'>);
|
|
404
|
+
if (!saved?.id) throw new Error();
|
|
416
405
|
} else if (activeEvent?.id) {
|
|
417
406
|
// For multi-day events, always use the original event ID
|
|
418
407
|
// The activeEvent should already contain the original event from the database
|
|
@@ -499,29 +488,13 @@ export function EventModal() {
|
|
|
499
488
|
setIsSaving(true);
|
|
500
489
|
try {
|
|
501
490
|
const eventsToSave = generatedEvents;
|
|
502
|
-
const savedEvents
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
title: eventData.title || 'New Event',
|
|
510
|
-
description: eventData.description || '',
|
|
511
|
-
start_at: eventData.start_at || '',
|
|
512
|
-
end_at: eventData.end_at || '',
|
|
513
|
-
color: eventData.color || 'BLUE',
|
|
514
|
-
location: eventData.location || '',
|
|
515
|
-
locked: eventData.locked || false,
|
|
516
|
-
source: sourceInputFromOption(selectedSourceOption),
|
|
517
|
-
};
|
|
518
|
-
|
|
519
|
-
const savedEvent = await addEvent(calendarEvent);
|
|
520
|
-
if (savedEvent) savedEvents.push(savedEvent);
|
|
521
|
-
} catch (error) {
|
|
522
|
-
failedEvents.push({ event: eventData as CalendarEvent, error });
|
|
523
|
-
}
|
|
524
|
-
}
|
|
491
|
+
const { savedEvents, failedEvents } = await saveCalendarEventDrafts(
|
|
492
|
+
eventsToSave ?? [],
|
|
493
|
+
addEvent,
|
|
494
|
+
sourceInputFromOption(selectedSourceOption)
|
|
495
|
+
);
|
|
496
|
+
setGeneratedEvents(failedEvents);
|
|
497
|
+
setCurrentEventIndex(0);
|
|
525
498
|
|
|
526
499
|
// Show success notification
|
|
527
500
|
if (savedEvents.length > 0) {
|
|
@@ -529,7 +502,7 @@ export function EventModal() {
|
|
|
529
502
|
title: 'Success',
|
|
530
503
|
description: `${savedEvents.length}/${eventsToSave?.length || 0} event${savedEvents.length > 1 ? 's' : ''} saved`,
|
|
531
504
|
});
|
|
532
|
-
closeModal();
|
|
505
|
+
if (failedEvents.length === 0) closeModal();
|
|
533
506
|
}
|
|
534
507
|
|
|
535
508
|
// If there are failed events, show an error notification
|
|
@@ -958,32 +931,10 @@ export function EventModal() {
|
|
|
958
931
|
}
|
|
959
932
|
};
|
|
960
933
|
|
|
961
|
-
const providerDisplay = getCalendarEventProviderDisplay(event);
|
|
962
|
-
|
|
963
934
|
return (
|
|
964
935
|
<Dialog open={isModalOpen} onOpenChange={(open) => !open && closeModal()}>
|
|
965
936
|
<DialogContent className="max-h-[90vh] max-w-3xl overflow-hidden p-0">
|
|
966
|
-
<
|
|
967
|
-
<DialogTitle className="flex items-center gap-2 font-semibold text-xl">
|
|
968
|
-
<span>{isEditing ? 'Edit Event' : 'Create Event'}</span>
|
|
969
|
-
{providerDisplay && (
|
|
970
|
-
<div className="ml-3 flex items-center gap-2 rounded-md border bg-muted/40 px-3 py-1 text-sm">
|
|
971
|
-
<CalendarEventProviderIcon
|
|
972
|
-
event={event}
|
|
973
|
-
className="h-4.5 w-4.5"
|
|
974
|
-
/>
|
|
975
|
-
<span className="font-medium text-xs">
|
|
976
|
-
{providerDisplay.label}
|
|
977
|
-
</span>
|
|
978
|
-
</div>
|
|
979
|
-
)}
|
|
980
|
-
</DialogTitle>
|
|
981
|
-
<DialogDescription>
|
|
982
|
-
{isEditing
|
|
983
|
-
? 'Make changes to your existing event'
|
|
984
|
-
: 'Add a new event to your calendar'}
|
|
985
|
-
</DialogDescription>
|
|
986
|
-
</DialogHeader>
|
|
937
|
+
<EventModalHeader event={event} isEditing={isEditing} />
|
|
987
938
|
|
|
988
939
|
<Tabs
|
|
989
940
|
value={activeTab}
|
|
@@ -1040,7 +991,7 @@ export function EventModal() {
|
|
|
1040
991
|
)}
|
|
1041
992
|
</div>
|
|
1042
993
|
<Select
|
|
1043
|
-
value={
|
|
994
|
+
value={selectedSourceOption?.id}
|
|
1044
995
|
onValueChange={(value) => setSelectedSourceId(value)}
|
|
1045
996
|
disabled={sourceOptions.length === 0 || isSaving}
|
|
1046
997
|
>
|