@tuturuuu/ui 0.37.2 → 0.39.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 +55 -0
- package/package.json +10 -7
- package/src/components/ui/custom/__tests__/workspace-picker-content.test.tsx +86 -0
- package/src/components/ui/custom/education-todo.test.tsx +131 -0
- package/src/components/ui/custom/education-todo.tsx +191 -0
- package/src/components/ui/custom/satellite-notification-polling.test.tsx +61 -0
- package/src/components/ui/custom/satellite-notification-popover.tsx +7 -3
- package/src/components/ui/custom/workspace-picker-content.tsx +93 -30
- package/src/components/ui/custom/workspace-select.tsx +12 -4
- package/src/components/ui/finance/invoices/attendance-calendar.test.tsx +294 -0
- package/src/components/ui/finance/invoices/attendance-calendar.tsx +14 -6
- package/src/components/ui/finance/invoices/components/invoice-history-actor.test.tsx +341 -0
- package/src/components/ui/finance/invoices/components/invoice-history-date.test.tsx +173 -0
- package/src/components/ui/finance/invoices/components/invoice-history-error.test.tsx +269 -0
- package/src/components/ui/finance/invoices/components/invoice-history-recovery.tsx +47 -0
- package/src/components/ui/finance/invoices/components/invoice-user-history-accordion.tsx +43 -10
- package/src/components/ui/finance/invoices/components/subscription-create-action.tsx +42 -0
- package/src/components/ui/finance/invoices/hooks/customer-search-scope.test.tsx +404 -0
- package/src/components/ui/finance/invoices/hooks/invoice-actor-lifetime-key.ts +11 -0
- package/src/components/ui/finance/invoices/hooks/use-infinite-user-invoices.ts +48 -0
- package/src/components/ui/finance/invoices/hooks/use-invoice-customer-search.test.tsx +6 -1
- package/src/components/ui/finance/invoices/hooks/use-invoice-customer-search.ts +58 -6
- package/src/components/ui/finance/invoices/hooks/use-subscription-auto-selection.test.tsx +34 -0
- package/src/components/ui/finance/invoices/hooks/use-subscription-auto-selection.ts +8 -2
- package/src/components/ui/finance/invoices/hooks/use-subscription-create.ts +219 -0
- package/src/components/ui/finance/invoices/hooks/use-subscription-invoice-context.ts +59 -0
- package/src/components/ui/finance/invoices/hooks/use-user-groups.ts +38 -0
- package/src/components/ui/finance/invoices/hooks.ts +3 -100
- package/src/components/ui/finance/invoices/invoiceId/compact-invoice-template.tsx +2 -7
- package/src/components/ui/finance/invoices/invoiceId/format-invoice-template-date.ts +13 -0
- package/src/components/ui/finance/invoices/invoiceId/full-invoice-template.tsx +2 -7
- package/src/components/ui/finance/invoices/invoiceId/invoice-card-date.test.tsx +169 -0
- package/src/components/ui/finance/invoices/invoiceId/invoice-card-storage.test.tsx +120 -0
- package/src/components/ui/finance/invoices/invoiceId/invoice-card.tsx +25 -62
- package/src/components/ui/finance/invoices/invoiceId/invoice-details-page.tsx +1 -0
- package/src/components/ui/finance/invoices/invoiceId/invoice-png-lifetime.test.tsx +438 -0
- package/src/components/ui/finance/invoices/invoiceId/use-invoice-png-export.ts +184 -0
- package/src/components/ui/finance/invoices/subscription-admission.test.ts +77 -0
- package/src/components/ui/finance/invoices/subscription-admission.ts +101 -0
- package/src/components/ui/finance/invoices/subscription-billing-groups.test.ts +162 -1
- package/src/components/ui/finance/invoices/subscription-billing-groups.ts +50 -9
- package/src/components/ui/finance/invoices/subscription-context-actor.test.tsx +246 -0
- package/src/components/ui/finance/invoices/subscription-create-scope.test.tsx +562 -0
- package/src/components/ui/finance/invoices/subscription-invoice.test.tsx +88 -6
- package/src/components/ui/finance/invoices/subscription-invoice.tsx +76 -135
- package/src/components/ui/finance/invoices/types.ts +3 -3
- package/src/components/ui/finance/invoices/user-groups-actor.test.tsx +334 -0
- package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoint-adjustment-dialog.tsx +47 -10
- package/src/components/ui/finance/wallets/checkpoints/wallet-checkpoints.test.tsx +95 -1
- package/src/components/ui/legacy/calendar/agenda-view.tsx +4 -1
- package/src/components/ui/legacy/calendar/all-day-event-bar.tsx +33 -369
- package/src/components/ui/legacy/calendar/all-day-event-layout.test.ts +94 -0
- package/src/components/ui/legacy/calendar/all-day-event-layout.ts +322 -0
- package/src/components/ui/legacy/calendar/calendar-draft-recovery.ts +34 -0
- package/src/components/ui/legacy/calendar/calendar-views.test.tsx +58 -27
- package/src/components/ui/legacy/calendar/event-card.test.tsx +25 -1
- package/src/components/ui/legacy/calendar/event-card.tsx +10 -12
- package/src/components/ui/legacy/calendar/event-modal-header.tsx +17 -4
- package/src/components/ui/legacy/calendar/event-modal.tsx +26 -1614
- package/src/components/ui/legacy/calendar/event-save-payload.test.ts +40 -0
- package/src/components/ui/legacy/calendar/event-save-payload.ts +79 -0
- package/src/components/ui/legacy/calendar/event-source-selection.test.ts +95 -0
- package/src/components/ui/legacy/calendar/event-source-selection.ts +78 -0
- package/src/components/ui/legacy/calendar/legacy-event-modal.tsx +1559 -0
- package/src/components/ui/legacy/calendar/month-calendar.tsx +5 -2
- package/src/components/ui/legacy/calendar/native-recurrence-context-menu.tsx +21 -0
- package/src/components/ui/legacy/calendar/native-recurrence-dialog.test.tsx +245 -0
- package/src/components/ui/legacy/calendar/native-recurrence-dialog.tsx +95 -0
- package/src/components/ui/legacy/calendar/native-recurrence-fields.tsx +137 -0
- package/src/components/ui/legacy/calendar/native-recurrence-form.tsx +340 -0
- package/src/components/ui/legacy/calendar/native-recurrence-model.test.ts +234 -0
- package/src/components/ui/legacy/calendar/native-recurrence-model.ts +245 -0
- package/src/components/ui/legacy/calendar/native-recurrence-pattern-fields.test.tsx +142 -0
- package/src/components/ui/legacy/calendar/native-recurrence-pattern-fields.tsx +178 -0
- package/src/components/ui/legacy/calendar/native-recurrence-pattern-model.test.ts +205 -0
- package/src/components/ui/legacy/calendar/native-recurrence-provider-routing.test.tsx +255 -0
- package/src/components/ui/legacy/calendar/past-event-treatment.test.ts +137 -0
- package/src/components/ui/legacy/calendar/past-event-treatment.ts +25 -0
- package/src/components/ui/legacy/calendar/provider-color-picker.test.tsx +147 -0
- package/src/components/ui/legacy/calendar/provider-color-picker.tsx +115 -0
- package/src/components/ui/legacy/calendar/provider-readonly-dialog.test.tsx +52 -0
- package/src/components/ui/legacy/calendar/provider-readonly-dialog.tsx +48 -0
- package/src/components/ui/legacy/calendar/save-calendar-event-drafts.test.ts +35 -1
- package/src/components/ui/legacy/calendar/save-calendar-event-drafts.ts +23 -11
- package/src/components/ui/legacy/calendar/use-calendar-draft-save.test.tsx +119 -0
- package/src/components/ui/legacy/calendar/use-calendar-draft-save.tsx +141 -0
- package/src/components/ui/legacy/calendar/use-provider-recurrence-operation.test.tsx +185 -0
- package/src/components/ui/legacy/calendar/use-provider-recurrence-operation.ts +186 -0
- package/src/components/ui/popover.tsx +3 -1
- package/src/components/ui/text-editor/__tests__/collaboration-carets.test.ts +142 -0
- package/src/components/ui/text-editor/__tests__/markdown-document.test.ts +50 -0
- package/src/components/ui/text-editor/clipboard-serialization.ts +10 -0
- package/src/components/ui/text-editor/collaboration-carets.ts +56 -0
- package/src/components/ui/text-editor/editor-readonly-transition.test.tsx +2 -0
- package/src/components/ui/text-editor/editor.tsx +11 -3
- package/src/components/ui/text-editor/extensions.ts +8 -2
- package/src/components/ui/text-editor/fixed-toolbar-contents.test.tsx +59 -0
- package/src/components/ui/text-editor/fixed-toolbar-contents.tsx +156 -0
- package/src/components/ui/text-editor/markdown-paste-extension.ts +1 -1
- package/src/components/ui/text-editor/markdown.ts +11 -0
- package/src/components/ui/text-editor/tool-bar.tsx +45 -102
- package/src/hooks/__tests__/calendar-event-write-payload-readonly.test.ts +40 -0
- package/src/hooks/__tests__/collaboration-awareness.test.ts +106 -0
- package/src/hooks/__tests__/native-calendar-mutation-guard.test.ts +45 -0
- package/src/hooks/__tests__/supabase-provider.test.ts +110 -7
- package/src/hooks/__tests__/use-calendar-readonly.test.tsx +39 -2
- package/src/hooks/__tests__/use-local-storage.test.tsx +42 -0
- package/src/hooks/__tests__/use-native-calendar-occurrences.test.tsx +140 -0
- package/src/hooks/__tests__/use-notifications-subscription.test.tsx +31 -1
- package/src/hooks/__tests__/use-workspace-presence.test.tsx +3 -3
- package/src/hooks/__tests__/use-yjs-collaboration.test.tsx +147 -0
- package/src/hooks/calendar-connection-types.ts +49 -0
- package/src/hooks/calendar-creation-request.test.ts +62 -0
- package/src/hooks/calendar-creation-request.ts +34 -0
- package/src/hooks/calendar-event-update-queue-types.ts +12 -0
- package/src/hooks/calendar-event-write-payload.ts +57 -0
- package/src/hooks/collaboration-document-hydration.ts +35 -0
- package/src/hooks/notification-subscription.ts +5 -2
- package/src/hooks/supabase-provider.ts +76 -112
- package/src/hooks/use-calendar-sync.tsx +41 -62
- package/src/hooks/use-calendar.tsx +44 -82
- package/src/hooks/use-exchange-rates.ts +4 -15
- package/src/hooks/use-local-storage.ts +9 -11
- package/src/hooks/use-native-calendar-occurrences.ts +85 -0
- package/src/hooks/use-workspace-presence.ts +9 -9
- package/src/hooks/use-yjs-collaboration.ts +68 -27
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { CalendarEvent } from '@tuturuuu/types/primitives/calendar-event';
|
|
2
|
+
import { expect, it } from 'vitest';
|
|
3
|
+
import { calendarEventUpdatePayload } from '../../../../hooks/calendar-event-write-payload';
|
|
4
|
+
import { eventSavePayload } from './event-save-payload';
|
|
5
|
+
|
|
6
|
+
const original = {
|
|
7
|
+
id: 'saved',
|
|
8
|
+
title: 'Synthetic',
|
|
9
|
+
description: '',
|
|
10
|
+
location: '',
|
|
11
|
+
start_at: '2026-10-02T10:00:00Z',
|
|
12
|
+
end_at: '2026-10-02T11:00:00Z',
|
|
13
|
+
color: 'BLUE',
|
|
14
|
+
locked: false,
|
|
15
|
+
} as CalendarEvent;
|
|
16
|
+
it('sends only a provider color command for an unchanged existing event', () => {
|
|
17
|
+
const providerColor = { connectionId: 'owned', kind: 'inherit' as const };
|
|
18
|
+
const payload = eventSavePayload(
|
|
19
|
+
{
|
|
20
|
+
...original,
|
|
21
|
+
providerColor,
|
|
22
|
+
source: { provider: 'google', connectionId: 'owned' },
|
|
23
|
+
},
|
|
24
|
+
original,
|
|
25
|
+
false
|
|
26
|
+
);
|
|
27
|
+
expect(payload).toEqual({ providerColor });
|
|
28
|
+
expect(calendarEventUpdatePayload(payload)).toEqual({ providerColor });
|
|
29
|
+
});
|
|
30
|
+
it('does not include unchanged native color or source with an ordinary title edit', () => {
|
|
31
|
+
expect(eventSavePayload({ ...original, title: 'Changed' }, original)).toEqual(
|
|
32
|
+
{ title: 'Changed' }
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
it('includes a calendar transfer only when its source actually changed', () => {
|
|
36
|
+
const source = { provider: 'google' as const, connectionId: 'new' };
|
|
37
|
+
expect(eventSavePayload({ ...original, source }, original, true)).toEqual({
|
|
38
|
+
source,
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { CalendarEvent } from '@tuturuuu/types/primitives/calendar-event';
|
|
2
|
+
import { newCalendarCreationRequestId } from '../../../../hooks/calendar-creation-request';
|
|
3
|
+
|
|
4
|
+
/** Existing edits send only changed fields so a color-only command neither
|
|
5
|
+
* broadcasts invitations nor accidentally becomes a calendar transfer. */
|
|
6
|
+
export function eventSavePayload(
|
|
7
|
+
draft: Partial<CalendarEvent>,
|
|
8
|
+
original?: Partial<CalendarEvent>,
|
|
9
|
+
sourceChanged = false
|
|
10
|
+
) {
|
|
11
|
+
if (!original || original.id === 'new') return draft;
|
|
12
|
+
const updates: Partial<CalendarEvent> = {};
|
|
13
|
+
for (const field of [
|
|
14
|
+
'title',
|
|
15
|
+
'description',
|
|
16
|
+
'location',
|
|
17
|
+
'start_at',
|
|
18
|
+
'end_at',
|
|
19
|
+
'locked',
|
|
20
|
+
] as const) {
|
|
21
|
+
if (
|
|
22
|
+
(draft[field] ?? (field === 'locked' ? false : '')) !==
|
|
23
|
+
(original[field] ?? (field === 'locked' ? false : ''))
|
|
24
|
+
)
|
|
25
|
+
Object.assign(updates, { [field]: draft[field] });
|
|
26
|
+
}
|
|
27
|
+
if (draft.providerColor) updates.providerColor = draft.providerColor;
|
|
28
|
+
else if (draft.color !== original.color) updates.color = draft.color;
|
|
29
|
+
if (sourceChanged) updates.source = draft.source;
|
|
30
|
+
return updates;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function eventModalSavePayload(
|
|
34
|
+
draft: Partial<CalendarEvent>,
|
|
35
|
+
original: Partial<CalendarEvent> | undefined,
|
|
36
|
+
source: CalendarEvent['source'],
|
|
37
|
+
sourceChanged: boolean
|
|
38
|
+
) {
|
|
39
|
+
return eventSavePayload(
|
|
40
|
+
{
|
|
41
|
+
title: draft.title || '',
|
|
42
|
+
description: draft.description || '',
|
|
43
|
+
start_at: draft.start_at,
|
|
44
|
+
end_at: draft.end_at,
|
|
45
|
+
color: draft.color || 'BLUE',
|
|
46
|
+
location: draft.location || '',
|
|
47
|
+
locked: draft.locked || false,
|
|
48
|
+
source,
|
|
49
|
+
providerColor: draft.providerColor,
|
|
50
|
+
},
|
|
51
|
+
original,
|
|
52
|
+
sourceChanged
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function eventModalDraft(
|
|
57
|
+
activeEvent: CalendarEvent
|
|
58
|
+
): Partial<CalendarEvent> {
|
|
59
|
+
return {
|
|
60
|
+
id: activeEvent.id,
|
|
61
|
+
requestId:
|
|
62
|
+
activeEvent.id === 'new' ? newCalendarCreationRequestId() : undefined,
|
|
63
|
+
title: activeEvent.title || '',
|
|
64
|
+
description: activeEvent.description || '',
|
|
65
|
+
start_at: activeEvent.start_at,
|
|
66
|
+
end_at: activeEvent.end_at,
|
|
67
|
+
color: activeEvent.color || 'BLUE',
|
|
68
|
+
location: activeEvent.location || '',
|
|
69
|
+
locked: activeEvent.locked || false,
|
|
70
|
+
ws_id: activeEvent.ws_id,
|
|
71
|
+
provider: activeEvent.provider,
|
|
72
|
+
source_calendar_id: activeEvent.source_calendar_id,
|
|
73
|
+
external_calendar_id: activeEvent.external_calendar_id,
|
|
74
|
+
external_event_id: activeEvent.external_event_id,
|
|
75
|
+
google_event_id: activeEvent.google_event_id,
|
|
76
|
+
google_calendar_id: activeEvent.google_calendar_id,
|
|
77
|
+
scheduling_metadata: activeEvent.scheduling_metadata,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { CalendarSourceOption } from '@tuturuuu/internal-api';
|
|
2
|
+
import { expect, it } from 'vitest';
|
|
3
|
+
import { eventModalSavePayload } from './event-save-payload';
|
|
4
|
+
import {
|
|
5
|
+
eventSourceChanged,
|
|
6
|
+
findEventSourceOption,
|
|
7
|
+
selectedEventSource,
|
|
8
|
+
} from './event-source-selection';
|
|
9
|
+
|
|
10
|
+
const options = ['one', 'two'].map((id) => ({
|
|
11
|
+
id,
|
|
12
|
+
provider: 'google',
|
|
13
|
+
connectionId: id,
|
|
14
|
+
workspaceCalendarId: id,
|
|
15
|
+
externalCalendarId: 'shared-provider-calendar',
|
|
16
|
+
})) as CalendarSourceOption[];
|
|
17
|
+
it('matches the canonical workspace source despite identical provider calendar IDs', () => {
|
|
18
|
+
expect(
|
|
19
|
+
findEventSourceOption(options, {
|
|
20
|
+
provider: 'google',
|
|
21
|
+
source_calendar_id: 'two',
|
|
22
|
+
external_calendar_id: 'shared-provider-calendar',
|
|
23
|
+
})?.id
|
|
24
|
+
).toBe('two');
|
|
25
|
+
expect(
|
|
26
|
+
findEventSourceOption(options, {
|
|
27
|
+
provider: 'google',
|
|
28
|
+
source_calendar_id: 'missing',
|
|
29
|
+
external_calendar_id: 'shared-provider-calendar',
|
|
30
|
+
})
|
|
31
|
+
).toBeUndefined();
|
|
32
|
+
});
|
|
33
|
+
it('fails closed when a legacy external calendar ID is ambiguous across accounts', () => {
|
|
34
|
+
expect(
|
|
35
|
+
findEventSourceOption(options, {
|
|
36
|
+
provider: 'google',
|
|
37
|
+
external_calendar_id: 'shared-provider-calendar',
|
|
38
|
+
})
|
|
39
|
+
).toBeUndefined();
|
|
40
|
+
expect(
|
|
41
|
+
findEventSourceOption(options.slice(0, 1), {
|
|
42
|
+
provider: 'google',
|
|
43
|
+
external_calendar_id: 'shared-provider-calendar',
|
|
44
|
+
})?.id
|
|
45
|
+
).toBe('one');
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('leaves an unresolved existing event on its original source and preserves new defaults', () => {
|
|
49
|
+
const original = {
|
|
50
|
+
id: 'saved',
|
|
51
|
+
provider: 'google' as const,
|
|
52
|
+
source_calendar_id: 'missing',
|
|
53
|
+
external_calendar_id: 'shared-provider-calendar',
|
|
54
|
+
};
|
|
55
|
+
expect(
|
|
56
|
+
selectedEventSource(options, original, null, options[0])
|
|
57
|
+
).toBeUndefined();
|
|
58
|
+
expect(eventSourceChanged(options, original, null)).toBe(false);
|
|
59
|
+
expect(
|
|
60
|
+
selectedEventSource(options, { id: 'new' }, null, options[0])?.id
|
|
61
|
+
).toBe('one');
|
|
62
|
+
expect(selectedEventSource(options, original, 'two', options[0])?.id).toBe(
|
|
63
|
+
'two'
|
|
64
|
+
);
|
|
65
|
+
expect(eventSourceChanged(options, original, 'two')).toBe(true);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('sends only a title edit when an existing source cannot be resolved', () => {
|
|
69
|
+
const original = {
|
|
70
|
+
id: 'saved',
|
|
71
|
+
title: 'Original',
|
|
72
|
+
color: 'BLUE' as const,
|
|
73
|
+
provider: 'google' as const,
|
|
74
|
+
source_calendar_id: 'missing',
|
|
75
|
+
start_at: '2026-10-02T10:00:00Z',
|
|
76
|
+
end_at: '2026-10-02T11:00:00Z',
|
|
77
|
+
};
|
|
78
|
+
const selected = selectedEventSource(options, original, null, options[0]);
|
|
79
|
+
expect(
|
|
80
|
+
eventModalSavePayload(
|
|
81
|
+
{ ...original, title: 'Changed' },
|
|
82
|
+
original,
|
|
83
|
+
selected?.provider === 'google'
|
|
84
|
+
? { provider: 'google', connectionId: selected.connectionId }
|
|
85
|
+
: undefined,
|
|
86
|
+
eventSourceChanged(options, original, null)
|
|
87
|
+
)
|
|
88
|
+
).toEqual({ title: 'Changed' });
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('recognizes a legacy Google event before considering native sources', () => {
|
|
92
|
+
const legacy = { google_event_id: 'external', source_calendar_id: 'two' };
|
|
93
|
+
expect(findEventSourceOption(options, legacy)?.id).toBe('two');
|
|
94
|
+
expect(eventSourceChanged(options, legacy, 'two')).toBe(false);
|
|
95
|
+
});
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CalendarSourceInput,
|
|
3
|
+
CalendarSourceOption,
|
|
4
|
+
} from '@tuturuuu/internal-api';
|
|
5
|
+
import type { CalendarEvent } from '@tuturuuu/types/primitives/calendar-event';
|
|
6
|
+
|
|
7
|
+
export function sourceInputFromOption(
|
|
8
|
+
option?: CalendarSourceOption | null
|
|
9
|
+
): CalendarSourceInput | undefined {
|
|
10
|
+
if (!option) return undefined;
|
|
11
|
+
|
|
12
|
+
if (option.provider === 'tuturuuu') {
|
|
13
|
+
return {
|
|
14
|
+
provider: 'tuturuuu',
|
|
15
|
+
workspaceCalendarId: option.workspaceCalendarId,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
provider: option.provider,
|
|
21
|
+
connectionId: option.connectionId,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function findEventSourceOption(
|
|
26
|
+
options: CalendarSourceOption[],
|
|
27
|
+
event: Partial<CalendarEvent>
|
|
28
|
+
) {
|
|
29
|
+
const provider =
|
|
30
|
+
event.provider ?? (event.google_event_id ? 'google' : undefined);
|
|
31
|
+
if (provider === 'google' || provider === 'microsoft') {
|
|
32
|
+
const matches = options.filter((option) => {
|
|
33
|
+
if (option.provider === 'tuturuuu' || option.provider !== provider)
|
|
34
|
+
return false;
|
|
35
|
+
if (event.source_calendar_id)
|
|
36
|
+
return option.workspaceCalendarId === event.source_calendar_id;
|
|
37
|
+
return (
|
|
38
|
+
option.externalCalendarId ===
|
|
39
|
+
(event.external_calendar_id ?? event.google_calendar_id)
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
// Legacy rows without a canonical source must not choose another account
|
|
43
|
+
// merely because it contains the same provider calendar ID.
|
|
44
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return options.find(
|
|
48
|
+
(option) =>
|
|
49
|
+
option.provider === 'tuturuuu' &&
|
|
50
|
+
option.workspaceCalendarId === event.source_calendar_id
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function selectedEventSource(
|
|
55
|
+
options: CalendarSourceOption[],
|
|
56
|
+
event: Partial<CalendarEvent>,
|
|
57
|
+
selectedId: string | null,
|
|
58
|
+
defaultSource?: CalendarSourceOption
|
|
59
|
+
) {
|
|
60
|
+
const selected = options.find((option) => option.id === selectedId);
|
|
61
|
+
const existing = !!event.id && event.id !== 'new';
|
|
62
|
+
return (
|
|
63
|
+
selected ??
|
|
64
|
+
findEventSourceOption(options, event) ??
|
|
65
|
+
(existing ? undefined : (defaultSource ?? options[0]))
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
export function eventSourceChanged(
|
|
69
|
+
options: CalendarSourceOption[],
|
|
70
|
+
event: Partial<CalendarEvent>,
|
|
71
|
+
selectedId: string | null
|
|
72
|
+
) {
|
|
73
|
+
return (
|
|
74
|
+
selectedId !== null &&
|
|
75
|
+
options.some((option) => option.id === selectedId) &&
|
|
76
|
+
selectedId !== findEventSourceOption(options, event)?.id
|
|
77
|
+
);
|
|
78
|
+
}
|