@agent-native/core 0.114.9 → 0.114.10
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/corpus/README.md +2 -2
- package/corpus/core/CHANGELOG.md +13 -0
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/agent/engine/ai-sdk-engine.ts +31 -3
- package/corpus/core/src/agent/engine/anthropic-engine.ts +20 -3
- package/corpus/core/src/agent/engine/builder-engine.ts +56 -9
- package/corpus/core/src/agent/engine/first-event-timeout.ts +64 -0
- package/corpus/core/src/cli/pr-visual-recap-workflow.ts +2 -3
- package/corpus/core/src/client/AssistantChat.tsx +7 -3
- package/corpus/core/src/client/resources/ResourcesPanel.tsx +1 -1
- package/corpus/core/src/integrations/computer-supervision-store.ts +4 -4
- package/corpus/core/src/integrations/pending-tasks-store.ts +1 -1
- package/corpus/core/src/integrations/remote-commands-store.ts +5 -5
- package/corpus/core/src/integrations/remote-devices-store.ts +3 -3
- package/corpus/core/src/integrations/remote-push-store.ts +3 -3
- package/corpus/core/src/observability/traces.ts +38 -1
- package/corpus/core/src/server/action-routes.ts +23 -1
- package/corpus/core/src/server/http-response-telemetry.ts +6 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/observability/SKILL.md +1 -1
- package/corpus/core/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
- package/corpus/templates/analytics/actions/account-deep-dive.ts +41 -6
- package/corpus/templates/analytics/changelog/2026-07-20-account-deep-dives-finish-with-partial-data-when-hubspot-lo.md +6 -0
- package/corpus/templates/analytics/changelog/2026-07-20-dashboard-email-reports-now-include-an-image-when-a-single-p.md +6 -0
- package/corpus/templates/analytics/server/lib/dashboard-report.ts +11 -3
- package/corpus/templates/calendar/actions/create-event.ts +5 -0
- package/corpus/templates/calendar/app/components/ThemeToggle.tsx +4 -1
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +0 -1
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +309 -421
- package/corpus/templates/calendar/app/components/calendar/InlineEventPickers.tsx +349 -0
- package/corpus/templates/calendar/app/i18n-data.ts +110 -0
- package/corpus/templates/calendar/app/lib/event-form-utils.ts +6 -0
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +3 -0
- package/corpus/templates/calendar/app/root.tsx +4 -2
- package/corpus/templates/calendar/changelog/2026-07-20-events-can-be-updated-inline-with-date-time-timezone-and-rep.md +6 -0
- package/corpus/templates/calendar/changelog/2026-07-20-public-booking-pages-no-longer-hydrate-with-a-theme-mismatch.md +6 -0
- package/corpus/templates/calendar/shared/api.ts +1 -0
- package/corpus/templates/clips/AGENTS.md +7 -0
- package/corpus/templates/clips/actions/create-recording.ts +51 -10
- package/corpus/templates/clips/actions/get-recording-player-data.ts +34 -5
- package/corpus/templates/clips/actions/lib/transcript-preview.ts +48 -0
- package/corpus/templates/clips/actions/list-recordings.ts +15 -4
- package/corpus/templates/clips/actions/regenerate-summary.ts +32 -0
- package/corpus/templates/clips/actions/search-recordings.ts +28 -6
- package/corpus/templates/clips/actions/view-screen.ts +54 -47
- package/corpus/templates/clips/app/components/library/library-grid.tsx +1 -5
- package/corpus/templates/clips/app/components/library/recording-card.tsx +29 -32
- package/corpus/templates/clips/app/components/meetings/quick-ask-sidebar.tsx +13 -0
- package/corpus/templates/clips/changelog/2026-07-20-agent-summaries-use-bounded-transcript-context.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-clip-summaries-now-finish-without-sending-a-long-transcript-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-clips-agents-only-discover-recordings-you-own-or-have-alread.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-hosted-recordings-no-longer-start-with-an-impossible-buffered-upload.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-meeting-recordings-now-offer-a-cleaner-transcript-view-with-.md +6 -0
- package/corpus/templates/clips/changelog/2026-07-20-shared-clips-can-be-shared-with-limited-access-and-cards-sho.md +6 -0
- package/corpus/templates/clips/desktop/src/hooks/useMeetingTranscription.ts +12 -8
- package/corpus/templates/clips/desktop/src/overlays/pill-logo.tsx +2 -15
- package/corpus/templates/clips/desktop/src/overlays/recording-pill.tsx +107 -340
- package/corpus/templates/clips/desktop/src/styles.css +27 -145
- package/corpus/templates/clips/desktop/src-tauri/src/recording_indicator.rs +6 -7
- package/corpus/templates/clips/server/lib/agent-recording-access.ts +73 -0
- package/corpus/templates/clips/server/plugins/agent-chat.ts +5 -2
- package/corpus/templates/plan/app/lib/plan-local-bridge.ts +18 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +10 -11
- package/corpus/templates/plan/changelog/2026-07-20-bridged-local-plans-now-load-comments-from-the-local-workspace.md +6 -0
- package/dist/agent/engine/ai-sdk-engine.d.ts.map +1 -1
- package/dist/agent/engine/ai-sdk-engine.js +27 -4
- package/dist/agent/engine/ai-sdk-engine.js.map +1 -1
- package/dist/agent/engine/anthropic-engine.d.ts.map +1 -1
- package/dist/agent/engine/anthropic-engine.js +19 -4
- package/dist/agent/engine/anthropic-engine.js.map +1 -1
- package/dist/agent/engine/builder-engine.d.ts.map +1 -1
- package/dist/agent/engine/builder-engine.js +40 -7
- package/dist/agent/engine/builder-engine.js.map +1 -1
- package/dist/agent/engine/first-event-timeout.d.ts +25 -0
- package/dist/agent/engine/first-event-timeout.d.ts.map +1 -0
- package/dist/agent/engine/first-event-timeout.js +49 -0
- package/dist/agent/engine/first-event-timeout.js.map +1 -0
- package/dist/cli/pr-visual-recap-workflow.d.ts +2 -2
- package/dist/cli/pr-visual-recap-workflow.d.ts.map +1 -1
- package/dist/cli/pr-visual-recap-workflow.js +2 -2
- package/dist/cli/pr-visual-recap-workflow.js.map +1 -1
- package/dist/client/AssistantChat.d.ts.map +1 -1
- package/dist/client/AssistantChat.js +7 -3
- package/dist/client/AssistantChat.js.map +1 -1
- package/dist/client/resources/ResourcesPanel.js +1 -1
- package/dist/client/resources/ResourcesPanel.js.map +1 -1
- package/dist/collab/routes.d.ts +1 -1
- package/dist/integrations/computer-supervision-store.js +4 -4
- package/dist/integrations/computer-supervision-store.js.map +1 -1
- package/dist/integrations/pending-tasks-store.js +1 -1
- package/dist/integrations/pending-tasks-store.js.map +1 -1
- package/dist/integrations/remote-commands-store.js +5 -5
- package/dist/integrations/remote-commands-store.js.map +1 -1
- package/dist/integrations/remote-devices-store.js +3 -3
- package/dist/integrations/remote-devices-store.js.map +1 -1
- package/dist/integrations/remote-push-store.js +3 -3
- package/dist/integrations/remote-push-store.js.map +1 -1
- package/dist/notifications/routes.d.ts +2 -2
- package/dist/observability/routes.d.ts +3 -3
- package/dist/observability/traces.d.ts.map +1 -1
- package/dist/observability/traces.js +23 -1
- package/dist/observability/traces.js.map +1 -1
- package/dist/secrets/routes.d.ts +9 -9
- package/dist/server/action-routes.d.ts.map +1 -1
- package/dist/server/action-routes.js +23 -1
- package/dist/server/action-routes.js.map +1 -1
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/http-response-telemetry.d.ts +2 -0
- package/dist/server/http-response-telemetry.d.ts.map +1 -1
- package/dist/server/http-response-telemetry.js +5 -1
- package/dist/server/http-response-telemetry.js.map +1 -1
- package/dist/templates/workspace-core/.agents/skills/observability/SKILL.md +1 -1
- package/dist/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
- package/package.json +2 -2
- package/src/agent/engine/ai-sdk-engine.ts +31 -3
- package/src/agent/engine/anthropic-engine.ts +20 -3
- package/src/agent/engine/builder-engine.ts +56 -9
- package/src/agent/engine/first-event-timeout.ts +64 -0
- package/src/cli/pr-visual-recap-workflow.ts +2 -3
- package/src/client/AssistantChat.tsx +7 -3
- package/src/client/resources/ResourcesPanel.tsx +1 -1
- package/src/integrations/computer-supervision-store.ts +4 -4
- package/src/integrations/pending-tasks-store.ts +1 -1
- package/src/integrations/remote-commands-store.ts +5 -5
- package/src/integrations/remote-devices-store.ts +3 -3
- package/src/integrations/remote-push-store.ts +3 -3
- package/src/observability/traces.ts +38 -1
- package/src/server/action-routes.ts +23 -1
- package/src/server/http-response-telemetry.ts +6 -1
- package/src/templates/workspace-core/.agents/skills/observability/SKILL.md +1 -1
- package/src/templates/workspace-core/.agents/skills/tracking/SKILL.md +1 -1
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { sendToAgentChat } from "@agent-native/core/client/agent-chat";
|
|
2
1
|
import { ExtensionSlot } from "@agent-native/core/client/extensions";
|
|
3
2
|
import { useT } from "@agent-native/core/client/i18n";
|
|
4
3
|
import type {
|
|
@@ -10,19 +9,15 @@ import {
|
|
|
10
9
|
IconX,
|
|
11
10
|
IconClock,
|
|
12
11
|
IconMapPin,
|
|
13
|
-
IconUser,
|
|
14
12
|
IconVideo,
|
|
15
13
|
IconRefresh,
|
|
16
14
|
IconBell,
|
|
17
|
-
IconChevronRight,
|
|
18
15
|
IconLayoutSidebarRight,
|
|
19
16
|
IconFileText,
|
|
20
17
|
IconExternalLink,
|
|
21
18
|
IconAlignLeft,
|
|
22
19
|
IconPlus,
|
|
23
20
|
IconBrandZoom,
|
|
24
|
-
IconMessage,
|
|
25
|
-
IconPalette,
|
|
26
21
|
IconPaperclip,
|
|
27
22
|
IconCalendarTime,
|
|
28
23
|
} from "@tabler/icons-react";
|
|
@@ -42,14 +37,18 @@ import {
|
|
|
42
37
|
} from "@/components/calendar/EventDescription";
|
|
43
38
|
import {
|
|
44
39
|
AttachmentControls,
|
|
45
|
-
EventColorSwatches,
|
|
46
40
|
ReminderControls,
|
|
47
41
|
} from "@/components/calendar/EventOptionControls";
|
|
48
42
|
import { FindTimeTakeover } from "@/components/calendar/FindTimePanel";
|
|
49
43
|
import { useGuestNotificationPrompt } from "@/components/calendar/GuestNotificationDialog";
|
|
44
|
+
import {
|
|
45
|
+
DatePickerPopover,
|
|
46
|
+
RepeatPicker,
|
|
47
|
+
TimePickerPopover,
|
|
48
|
+
TimezonePickerPopover,
|
|
49
|
+
} from "@/components/calendar/InlineEventPickers";
|
|
50
50
|
import { WorkingLocationEditor } from "@/components/calendar/WorkingLocationEditor";
|
|
51
51
|
import { useCalendarContext } from "@/components/layout/AppLayout";
|
|
52
|
-
import { TimezoneCombobox } from "@/components/TimezoneCombobox";
|
|
53
52
|
import { Button } from "@/components/ui/button";
|
|
54
53
|
import {
|
|
55
54
|
Popover,
|
|
@@ -78,13 +77,11 @@ import { useViewPreferences } from "@/hooks/use-view-preferences";
|
|
|
78
77
|
import { useConnectZoom, useZoomStatus } from "@/hooks/use-zoom-auth";
|
|
79
78
|
import { defaultColorForAccount } from "@/lib/calendar-view-preferences";
|
|
80
79
|
import { shouldShowEventAccountSelector } from "@/lib/event-account-selection";
|
|
81
|
-
import { getGoogleEventColorHex } from "@/lib/event-colors";
|
|
82
80
|
import {
|
|
83
81
|
attachmentsToDrafts,
|
|
84
82
|
buildRecurrenceRules,
|
|
85
83
|
buildReminderPayload,
|
|
86
84
|
dateTimeInTimezoneToIso,
|
|
87
|
-
formatRecurrenceText,
|
|
88
85
|
formatReminderText,
|
|
89
86
|
getEventEndValidationMessage,
|
|
90
87
|
getLocalTimezone,
|
|
@@ -313,6 +310,31 @@ type EventUpdatePatch = Partial<CalendarEvent> & {
|
|
|
313
310
|
workingLocationLabel?: string;
|
|
314
311
|
};
|
|
315
312
|
|
|
313
|
+
interface TimeEditValues {
|
|
314
|
+
date: string;
|
|
315
|
+
endDate: string;
|
|
316
|
+
startTime: string;
|
|
317
|
+
endTime: string;
|
|
318
|
+
timezone: string;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function addMinutesToTimeValue(
|
|
322
|
+
date: string,
|
|
323
|
+
time: string,
|
|
324
|
+
minutes: number,
|
|
325
|
+
): { date: string; time: string } {
|
|
326
|
+
const [hour, minute] = time.split(":").map(Number);
|
|
327
|
+
const total = (hour || 0) * 60 + (minute || 0) + minutes;
|
|
328
|
+
const dayOffset = Math.floor(total / (24 * 60));
|
|
329
|
+
const minuteOfDay = ((total % (24 * 60)) + 24 * 60) % (24 * 60);
|
|
330
|
+
const nextDate = new Date(`${date}T00:00:00`);
|
|
331
|
+
nextDate.setDate(nextDate.getDate() + dayOffset);
|
|
332
|
+
return {
|
|
333
|
+
date: format(nextDate, "yyyy-MM-dd"),
|
|
334
|
+
time: `${String(Math.floor(minuteOfDay / 60)).padStart(2, "0")}:${String(minuteOfDay % 60).padStart(2, "0")}`,
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
316
338
|
function mergeAttendeesForPrompt(
|
|
317
339
|
existing: CalendarEvent["attendees"] | undefined,
|
|
318
340
|
additions: CalendarEvent["attendees"] | undefined,
|
|
@@ -569,8 +591,6 @@ export function EventDetailPopover({
|
|
|
569
591
|
const [editMeetingLink, setEditMeetingLink] = useState("");
|
|
570
592
|
const [editTimeScope, setEditTimeScope] =
|
|
571
593
|
useState<UpdateEventScope>("single");
|
|
572
|
-
const [editRecurrencePreset, setEditRecurrencePreset] =
|
|
573
|
-
useState<RecurrencePreset>(() => getRecurrencePreset(event.recurrence));
|
|
574
594
|
const [pendingVideoProvider, setPendingVideoProvider] = useState<
|
|
575
595
|
"meet" | "zoom" | null
|
|
576
596
|
>(null);
|
|
@@ -591,9 +611,7 @@ export function EventDetailPopover({
|
|
|
591
611
|
const isRecurringEvent = !!(
|
|
592
612
|
event.recurringEventId || recurrenceRules?.length
|
|
593
613
|
);
|
|
594
|
-
const
|
|
595
|
-
isRecurringEvent && !recurrenceRules?.length && masterEvent.isLoading;
|
|
596
|
-
const canEditRecurrence = !isDraft && !isOverlay && !!recurrenceRules?.length;
|
|
614
|
+
const canEditRecurrence = !isOverlay && !isWorkingLocation;
|
|
597
615
|
const { promptGuestNotification, guestNotificationDialog } =
|
|
598
616
|
useGuestNotificationPrompt();
|
|
599
617
|
const zoomStatus = useZoomStatus();
|
|
@@ -650,10 +668,6 @@ export function EventDetailPopover({
|
|
|
650
668
|
editableLocationValue,
|
|
651
669
|
]);
|
|
652
670
|
|
|
653
|
-
useEffect(() => {
|
|
654
|
-
setEditRecurrencePreset(getRecurrencePreset(recurrenceRules));
|
|
655
|
-
}, [recurrenceRules]);
|
|
656
|
-
|
|
657
671
|
// When defaultOpen changes to true (new event created), open the popover
|
|
658
672
|
useEffect(() => {
|
|
659
673
|
if (defaultOpen) {
|
|
@@ -794,39 +808,6 @@ export function EventDetailPopover({
|
|
|
794
808
|
return saved;
|
|
795
809
|
}, [editAttachments, saveField]);
|
|
796
810
|
|
|
797
|
-
const handleColorChange = useCallback(
|
|
798
|
-
(nextColorId: string | undefined) => {
|
|
799
|
-
if (!nextColorId) return;
|
|
800
|
-
saveField({
|
|
801
|
-
colorId: nextColorId,
|
|
802
|
-
color: getGoogleEventColorHex(nextColorId),
|
|
803
|
-
});
|
|
804
|
-
},
|
|
805
|
-
[saveField],
|
|
806
|
-
);
|
|
807
|
-
|
|
808
|
-
const handleDraftDescription = useCallback(() => {
|
|
809
|
-
sendToAgentChat({
|
|
810
|
-
message: t("eventForm.ai.descriptionMessage", {
|
|
811
|
-
title: event.title,
|
|
812
|
-
}),
|
|
813
|
-
context: t("eventForm.ai.existingDescriptionContext", {
|
|
814
|
-
id: event.id,
|
|
815
|
-
title: event.title,
|
|
816
|
-
start: event.start,
|
|
817
|
-
end: event.end,
|
|
818
|
-
timezone: event.startTimeZone || getLocalTimezone(),
|
|
819
|
-
location: event.location || t("eventForm.ai.none"),
|
|
820
|
-
attendees:
|
|
821
|
-
(event.attendees ?? [])
|
|
822
|
-
.map((attendee) => attendee.email)
|
|
823
|
-
.join(", ") || t("eventForm.ai.none"),
|
|
824
|
-
description: event.description || t("eventForm.ai.empty"),
|
|
825
|
-
}),
|
|
826
|
-
submit: true,
|
|
827
|
-
});
|
|
828
|
-
}, [event, t]);
|
|
829
|
-
|
|
830
811
|
const handleAddGoogleMeet = useCallback(() => {
|
|
831
812
|
if (!event.id || updateEvent.isPending) return;
|
|
832
813
|
if (isDraft) {
|
|
@@ -1030,64 +1011,166 @@ export function EventDetailPopover({
|
|
|
1030
1011
|
[event, isDraft, onDraftUpdate, t, updateEvent],
|
|
1031
1012
|
);
|
|
1032
1013
|
|
|
1033
|
-
const
|
|
1034
|
-
|
|
1014
|
+
const saveTimeValues = useCallback(
|
|
1015
|
+
(values: TimeEditValues) => {
|
|
1016
|
+
const normalizedEndDate = normalizeAllDayEditEndDate(
|
|
1017
|
+
isSingleDayWorkingLocation,
|
|
1018
|
+
values.date,
|
|
1019
|
+
values.endDate,
|
|
1020
|
+
);
|
|
1021
|
+
const allDayEnd = new Date(`${normalizedEndDate}T00:00:00`);
|
|
1022
|
+
allDayEnd.setDate(allDayEnd.getDate() + 1);
|
|
1023
|
+
const newStart = event.allDay
|
|
1024
|
+
? new Date(`${values.date}T00:00:00`).toISOString()
|
|
1025
|
+
: dateTimeInTimezoneToIso(
|
|
1026
|
+
values.date,
|
|
1027
|
+
values.startTime,
|
|
1028
|
+
values.timezone,
|
|
1029
|
+
);
|
|
1030
|
+
const newEnd = event.allDay
|
|
1031
|
+
? allDayEnd.toISOString()
|
|
1032
|
+
: dateTimeInTimezoneToIso(
|
|
1033
|
+
values.endDate,
|
|
1034
|
+
values.endTime,
|
|
1035
|
+
values.timezone,
|
|
1036
|
+
);
|
|
1037
|
+
if (new Date(newEnd).getTime() <= new Date(newStart).getTime()) {
|
|
1038
|
+
toast.error(
|
|
1039
|
+
getEventEndValidationMessage({
|
|
1040
|
+
allDay: event.allDay ?? false,
|
|
1041
|
+
startDate: values.date,
|
|
1042
|
+
endDate: values.endDate,
|
|
1043
|
+
startTime: values.startTime,
|
|
1044
|
+
endTime: values.endTime,
|
|
1045
|
+
}),
|
|
1046
|
+
);
|
|
1047
|
+
return false;
|
|
1048
|
+
}
|
|
1049
|
+
let saved = false;
|
|
1050
|
+
if (newStart !== event.start || newEnd !== event.end) {
|
|
1051
|
+
saved = saveField({
|
|
1052
|
+
start: newStart,
|
|
1053
|
+
end: newEnd,
|
|
1054
|
+
allDay: event.allDay,
|
|
1055
|
+
startTimeZone: event.allDay ? undefined : values.timezone,
|
|
1056
|
+
endTimeZone: event.allDay ? undefined : values.timezone,
|
|
1057
|
+
scope: resolveTimeEditScope(
|
|
1058
|
+
isRecurringEvent,
|
|
1059
|
+
isSingleDayWorkingLocation,
|
|
1060
|
+
editTimeScope,
|
|
1061
|
+
),
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
setEditTimeScope("single");
|
|
1065
|
+
return saved;
|
|
1066
|
+
},
|
|
1067
|
+
[
|
|
1068
|
+
event.start,
|
|
1069
|
+
event.end,
|
|
1070
|
+
event.allDay,
|
|
1035
1071
|
isSingleDayWorkingLocation,
|
|
1072
|
+
isRecurringEvent,
|
|
1073
|
+
editTimeScope,
|
|
1074
|
+
saveField,
|
|
1075
|
+
],
|
|
1076
|
+
);
|
|
1077
|
+
|
|
1078
|
+
const handleInlineTimeChange = useCallback(
|
|
1079
|
+
(field: "startTime" | "endTime", nextValue: string) => {
|
|
1080
|
+
let nextDate = editDate;
|
|
1081
|
+
let nextEndDate = editEndDate;
|
|
1082
|
+
let nextStartTime = editStartTime;
|
|
1083
|
+
let nextEndTime = editEndTime;
|
|
1084
|
+
|
|
1085
|
+
if (field === "startTime") {
|
|
1086
|
+
nextStartTime = nextValue;
|
|
1087
|
+
if (nextEndDate === nextDate && nextEndTime <= nextStartTime) {
|
|
1088
|
+
const duration = Math.max(
|
|
1089
|
+
15,
|
|
1090
|
+
differenceInMinutes(parseISO(event.end), parseISO(event.start)),
|
|
1091
|
+
);
|
|
1092
|
+
const nextEnd = addMinutesToTimeValue(
|
|
1093
|
+
nextDate,
|
|
1094
|
+
nextStartTime,
|
|
1095
|
+
duration,
|
|
1096
|
+
);
|
|
1097
|
+
nextEndDate = nextEnd.date;
|
|
1098
|
+
nextEndTime = nextEnd.time;
|
|
1099
|
+
}
|
|
1100
|
+
} else {
|
|
1101
|
+
nextEndTime = nextValue;
|
|
1102
|
+
if (nextEndDate === nextDate && nextEndTime <= nextStartTime) {
|
|
1103
|
+
const nextEnd = addMinutesToTimeValue(nextDate, nextEndTime, 24 * 60);
|
|
1104
|
+
nextEndDate = nextEnd.date;
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
setEditDate(nextDate);
|
|
1109
|
+
setEditEndDate(nextEndDate);
|
|
1110
|
+
setEditStartTime(nextStartTime);
|
|
1111
|
+
setEditEndTime(nextEndTime);
|
|
1112
|
+
saveTimeValues({
|
|
1113
|
+
date: nextDate,
|
|
1114
|
+
endDate: nextEndDate,
|
|
1115
|
+
startTime: nextStartTime,
|
|
1116
|
+
endTime: nextEndTime,
|
|
1117
|
+
timezone: editTimezone,
|
|
1118
|
+
});
|
|
1119
|
+
},
|
|
1120
|
+
[
|
|
1036
1121
|
editDate,
|
|
1037
1122
|
editEndDate,
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
);
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
allDay: event.allDay,
|
|
1065
|
-
startTimeZone: event.allDay ? undefined : editTimezone,
|
|
1066
|
-
endTimeZone: event.allDay ? undefined : editTimezone,
|
|
1067
|
-
scope: resolveTimeEditScope(
|
|
1068
|
-
isRecurringEvent,
|
|
1069
|
-
isSingleDayWorkingLocation,
|
|
1070
|
-
editTimeScope,
|
|
1071
|
-
),
|
|
1123
|
+
editStartTime,
|
|
1124
|
+
editEndTime,
|
|
1125
|
+
editTimezone,
|
|
1126
|
+
event.end,
|
|
1127
|
+
event.start,
|
|
1128
|
+
saveTimeValues,
|
|
1129
|
+
],
|
|
1130
|
+
);
|
|
1131
|
+
|
|
1132
|
+
const handleInlineDateChange = useCallback(
|
|
1133
|
+
(field: "date" | "endDate", nextValue: string) => {
|
|
1134
|
+
const nextDate = field === "date" ? nextValue : editDate;
|
|
1135
|
+
const nextEndDate =
|
|
1136
|
+
field === "endDate"
|
|
1137
|
+
? nextValue
|
|
1138
|
+
: nextValue > editEndDate
|
|
1139
|
+
? nextValue
|
|
1140
|
+
: editEndDate;
|
|
1141
|
+
setEditDate(nextDate);
|
|
1142
|
+
setEditEndDate(nextEndDate < nextDate ? nextDate : nextEndDate);
|
|
1143
|
+
saveTimeValues({
|
|
1144
|
+
date: nextDate,
|
|
1145
|
+
endDate: nextEndDate < nextDate ? nextDate : nextEndDate,
|
|
1146
|
+
startTime: editStartTime,
|
|
1147
|
+
endTime: editEndTime,
|
|
1148
|
+
timezone: editTimezone,
|
|
1072
1149
|
});
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1150
|
+
},
|
|
1151
|
+
[
|
|
1152
|
+
editDate,
|
|
1153
|
+
editEndDate,
|
|
1154
|
+
editEndTime,
|
|
1155
|
+
editStartTime,
|
|
1156
|
+
editTimezone,
|
|
1157
|
+
saveTimeValues,
|
|
1158
|
+
],
|
|
1159
|
+
);
|
|
1160
|
+
|
|
1161
|
+
const handleInlineTimezoneChange = useCallback(
|
|
1162
|
+
(nextTimezone: string) => {
|
|
1163
|
+
setEditTimezone(nextTimezone);
|
|
1164
|
+
saveTimeValues({
|
|
1165
|
+
date: editDate,
|
|
1166
|
+
endDate: editEndDate,
|
|
1167
|
+
startTime: editStartTime,
|
|
1168
|
+
endTime: editEndTime,
|
|
1169
|
+
timezone: nextTimezone,
|
|
1170
|
+
});
|
|
1171
|
+
},
|
|
1172
|
+
[editDate, editEndDate, editEndTime, editStartTime, saveTimeValues],
|
|
1173
|
+
);
|
|
1091
1174
|
|
|
1092
1175
|
const schedulingAttendees = useMemo(
|
|
1093
1176
|
() =>
|
|
@@ -1132,27 +1215,29 @@ export function EventDetailPopover({
|
|
|
1132
1215
|
[editTimeScope, findTimeTimezone, isRecurringEvent, saveField],
|
|
1133
1216
|
);
|
|
1134
1217
|
|
|
1135
|
-
const handleSaveRecurrence = useCallback(
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1218
|
+
const handleSaveRecurrence = useCallback(
|
|
1219
|
+
(preset: RecurrencePreset) => {
|
|
1220
|
+
const recurrence = buildRecurrenceRules(
|
|
1221
|
+
preset,
|
|
1222
|
+
masterEvent.data?.start || event.start,
|
|
1223
|
+
masterEvent.data?.startTimeZone || event.startTimeZone || editTimezone,
|
|
1224
|
+
);
|
|
1225
|
+
if (!recurrence) {
|
|
1226
|
+
toast.error(t("eventForm.customRepeatGoogleCalendar"));
|
|
1227
|
+
return;
|
|
1228
|
+
}
|
|
1229
|
+
saveField({ recurrence, scope: "all" });
|
|
1230
|
+
},
|
|
1231
|
+
[
|
|
1232
|
+
editTimezone,
|
|
1233
|
+
event.start,
|
|
1234
|
+
event.startTimeZone,
|
|
1235
|
+
masterEvent.data?.start,
|
|
1236
|
+
masterEvent.data?.startTimeZone,
|
|
1237
|
+
saveField,
|
|
1238
|
+
t,
|
|
1239
|
+
],
|
|
1240
|
+
);
|
|
1156
1241
|
|
|
1157
1242
|
const handleAddAttendee = useCallback(
|
|
1158
1243
|
(attendee: AttendeeRecipient) => {
|
|
@@ -1275,10 +1360,6 @@ export function EventDetailPopover({
|
|
|
1275
1360
|
const locationIsUrl = event.location ? isUrl(event.location) : false;
|
|
1276
1361
|
const locationIsMeetingLink =
|
|
1277
1362
|
meetingLink && event.location?.includes(meetingLink.url);
|
|
1278
|
-
const recurrenceText = recurrenceLoading
|
|
1279
|
-
? t("eventForm.loadingRepeat")
|
|
1280
|
-
: formatRecurrenceText(recurrenceRules) ||
|
|
1281
|
-
(isRecurringEvent ? t("eventForm.repeats") : null);
|
|
1282
1363
|
const handleOpenChange = useCallback(
|
|
1283
1364
|
(newOpen: boolean) => {
|
|
1284
1365
|
const isPopoverSuppressed =
|
|
@@ -1302,8 +1383,6 @@ export function EventDetailPopover({
|
|
|
1302
1383
|
savedPendingChange = handleSaveDescription() || savedPendingChange;
|
|
1303
1384
|
} else if (editingField === "location") {
|
|
1304
1385
|
savedPendingChange = handleSaveLocation() || savedPendingChange;
|
|
1305
|
-
} else if (editingField === "time") {
|
|
1306
|
-
savedPendingChange = handleSaveTime() || savedPendingChange;
|
|
1307
1386
|
} else if (editingField === "meetingLink") {
|
|
1308
1387
|
savedPendingChange = handleSaveMeetingLink() || savedPendingChange;
|
|
1309
1388
|
} else if (editingField === "reminders") {
|
|
@@ -1336,7 +1415,6 @@ export function EventDetailPopover({
|
|
|
1336
1415
|
editingField,
|
|
1337
1416
|
handleSaveDescription,
|
|
1338
1417
|
handleSaveLocation,
|
|
1339
|
-
handleSaveTime,
|
|
1340
1418
|
handleSaveMeetingLink,
|
|
1341
1419
|
handleSaveReminders,
|
|
1342
1420
|
handleSaveAttachments,
|
|
@@ -1396,7 +1474,8 @@ export function EventDetailPopover({
|
|
|
1396
1474
|
const target = e.target as HTMLElement;
|
|
1397
1475
|
if (
|
|
1398
1476
|
target.closest("[data-apollo-popover]") ||
|
|
1399
|
-
target.closest("[data-attendee-autocomplete]")
|
|
1477
|
+
target.closest("[data-attendee-autocomplete]") ||
|
|
1478
|
+
target.closest("[data-time-picker-popover]")
|
|
1400
1479
|
) {
|
|
1401
1480
|
e.preventDefault();
|
|
1402
1481
|
return;
|
|
@@ -1414,9 +1493,7 @@ export function EventDetailPopover({
|
|
|
1414
1493
|
? t("eventForm.workingLocation")
|
|
1415
1494
|
: isOutOfOffice
|
|
1416
1495
|
? t("eventForm.outOfOffice")
|
|
1417
|
-
:
|
|
1418
|
-
? t("eventForm.draftEvent")
|
|
1419
|
-
: t("eventForm.event")}
|
|
1496
|
+
: t("eventForm.event")}
|
|
1420
1497
|
</span>
|
|
1421
1498
|
</div>
|
|
1422
1499
|
<div className="flex items-center gap-0.5">
|
|
@@ -1524,185 +1601,112 @@ export function EventDetailPopover({
|
|
|
1524
1601
|
/>
|
|
1525
1602
|
)}
|
|
1526
1603
|
|
|
1527
|
-
{/* Time
|
|
1528
|
-
|
|
1529
|
-
<
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
<div
|
|
1533
|
-
className=
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
}`}
|
|
1538
|
-
>
|
|
1539
|
-
<input
|
|
1540
|
-
type="date"
|
|
1604
|
+
{/* Time, date, timezone, and repeat stay editable in place. */}
|
|
1605
|
+
<div className="flex items-start gap-3 rounded-md py-1.5">
|
|
1606
|
+
<IconClock className="mt-1 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
1607
|
+
<div className="min-w-0 flex-1">
|
|
1608
|
+
{event.allDay ? (
|
|
1609
|
+
<div className="flex flex-wrap items-center gap-1 text-sm">
|
|
1610
|
+
<span className="text-muted-foreground">
|
|
1611
|
+
{t("eventForm.allDay")}
|
|
1612
|
+
</span>
|
|
1613
|
+
<DatePickerPopover
|
|
1541
1614
|
value={editDate}
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
// i18n-ignore -- this expression selects a date; it is not visible copy.
|
|
1547
|
-
isSingleDayWorkingLocation
|
|
1548
|
-
? next
|
|
1549
|
-
: current < next
|
|
1550
|
-
? next
|
|
1551
|
-
: current,
|
|
1552
|
-
);
|
|
1553
|
-
}}
|
|
1554
|
-
className="w-full rounded-md border border-border bg-transparent px-2 py-1 text-sm text-foreground"
|
|
1555
|
-
aria-label={t("eventForm.startDate")}
|
|
1615
|
+
label={t("eventForm.startDate")}
|
|
1616
|
+
onChange={(value) =>
|
|
1617
|
+
handleInlineDateChange("date", value)
|
|
1618
|
+
}
|
|
1556
1619
|
/>
|
|
1557
|
-
{
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1620
|
+
{editEndDate !== editDate && (
|
|
1621
|
+
<>
|
|
1622
|
+
<span className="text-muted-foreground/50">→</span>
|
|
1623
|
+
<DatePickerPopover
|
|
1624
|
+
value={editEndDate}
|
|
1625
|
+
label={t("eventForm.endDate")}
|
|
1626
|
+
onChange={(value) =>
|
|
1627
|
+
handleInlineDateChange("endDate", value)
|
|
1628
|
+
}
|
|
1629
|
+
/>
|
|
1630
|
+
</>
|
|
1568
1631
|
)}
|
|
1569
1632
|
</div>
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1633
|
+
) : (
|
|
1634
|
+
<>
|
|
1635
|
+
<div className="flex flex-wrap items-baseline gap-1">
|
|
1636
|
+
<TimePickerPopover
|
|
1574
1637
|
value={editStartTime}
|
|
1575
|
-
|
|
1576
|
-
|
|
1638
|
+
label={t("eventForm.start")}
|
|
1639
|
+
onChange={(value) =>
|
|
1640
|
+
handleInlineTimeChange("startTime", value)
|
|
1641
|
+
}
|
|
1577
1642
|
/>
|
|
1578
|
-
<span className="text-muted-foreground/50
|
|
1579
|
-
|
|
1580
|
-
</span>
|
|
1581
|
-
<input
|
|
1582
|
-
type="time"
|
|
1643
|
+
<span className="text-muted-foreground/50">→</span>
|
|
1644
|
+
<TimePickerPopover
|
|
1583
1645
|
value={editEndTime}
|
|
1584
|
-
|
|
1585
|
-
|
|
1646
|
+
label={t("eventForm.end")}
|
|
1647
|
+
getOptionMeta={(value) => {
|
|
1648
|
+
const [hour, minute] = value.split(":").map(Number);
|
|
1649
|
+
const [startHour, startMinute] = editStartTime
|
|
1650
|
+
.split(":")
|
|
1651
|
+
.map(Number);
|
|
1652
|
+
const duration =
|
|
1653
|
+
hour * 60 +
|
|
1654
|
+
minute -
|
|
1655
|
+
(startHour * 60 + startMinute) +
|
|
1656
|
+
(editEndDate !== editDate ? 24 * 60 : 0);
|
|
1657
|
+
if (duration <= 0) return undefined;
|
|
1658
|
+
if (duration < 60) return `${duration}min`;
|
|
1659
|
+
const hours = Math.floor(duration / 60);
|
|
1660
|
+
const minutes = duration % 60;
|
|
1661
|
+
return minutes
|
|
1662
|
+
? `${hours}h ${minutes}min`
|
|
1663
|
+
: `${hours}h`;
|
|
1664
|
+
}}
|
|
1665
|
+
onChange={(value) =>
|
|
1666
|
+
handleInlineTimeChange("endTime", value)
|
|
1667
|
+
}
|
|
1586
1668
|
/>
|
|
1669
|
+
<span className="text-xs text-muted-foreground/70">
|
|
1670
|
+
{formatDuration(event.start, event.end)}
|
|
1671
|
+
</span>
|
|
1587
1672
|
</div>
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1673
|
+
<div className="mt-0.5 flex flex-wrap items-center gap-1 text-sm">
|
|
1674
|
+
<DatePickerPopover
|
|
1675
|
+
value={editDate}
|
|
1676
|
+
label={t("eventForm.startDate")}
|
|
1677
|
+
onChange={(value) =>
|
|
1678
|
+
handleInlineDateChange("date", value)
|
|
1679
|
+
}
|
|
1680
|
+
/>
|
|
1681
|
+
{editEndDate !== editDate && (
|
|
1682
|
+
<>
|
|
1683
|
+
<span className="text-muted-foreground/50">→</span>
|
|
1684
|
+
<DatePickerPopover
|
|
1685
|
+
value={editEndDate}
|
|
1686
|
+
label={t("eventForm.endDate")}
|
|
1687
|
+
onChange={(value) =>
|
|
1688
|
+
handleInlineDateChange("endDate", value)
|
|
1689
|
+
}
|
|
1690
|
+
/>
|
|
1691
|
+
</>
|
|
1692
|
+
)}
|
|
1693
|
+
</div>
|
|
1694
|
+
<TimezonePickerPopover
|
|
1592
1695
|
value={editTimezone}
|
|
1593
|
-
|
|
1696
|
+
label={t("eventForm.timezone")}
|
|
1697
|
+
onChange={handleInlineTimezoneChange}
|
|
1594
1698
|
/>
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
!isDraft &&
|
|
1598
|
-
!isSingleDayWorkingLocation && (
|
|
1599
|
-
<div className="flex items-center gap-2">
|
|
1600
|
-
<span className="text-xs text-muted-foreground">
|
|
1601
|
-
{t("eventForm.applyTo")}
|
|
1602
|
-
</span>
|
|
1603
|
-
<Select
|
|
1604
|
-
value={editTimeScope}
|
|
1605
|
-
onValueChange={(value) =>
|
|
1606
|
-
setEditTimeScope(value as UpdateEventScope)
|
|
1607
|
-
}
|
|
1608
|
-
>
|
|
1609
|
-
<SelectTrigger className="h-7 flex-1 text-xs">
|
|
1610
|
-
<SelectValue />
|
|
1611
|
-
</SelectTrigger>
|
|
1612
|
-
<SelectContent>
|
|
1613
|
-
<SelectItem value="single">
|
|
1614
|
-
{isWorkingLocation
|
|
1615
|
-
? t("eventForm.thisDayOnly")
|
|
1616
|
-
: t("eventForm.thisEvent")}
|
|
1617
|
-
</SelectItem>
|
|
1618
|
-
<SelectItem value="all">
|
|
1619
|
-
{isWorkingLocation
|
|
1620
|
-
? t("eventForm.allDays")
|
|
1621
|
-
: t("eventForm.allEvents")}
|
|
1622
|
-
</SelectItem>
|
|
1623
|
-
</SelectContent>
|
|
1624
|
-
</Select>
|
|
1625
|
-
</div>
|
|
1626
|
-
)}
|
|
1627
|
-
<div className="flex justify-end gap-1.5">
|
|
1628
|
-
<Button
|
|
1629
|
-
variant="ghost"
|
|
1630
|
-
size="sm"
|
|
1631
|
-
className="h-6 text-xs"
|
|
1632
|
-
onClick={() => {
|
|
1633
|
-
setEditDate(toDateInputValue(event.start));
|
|
1634
|
-
setEditEndDate(
|
|
1635
|
-
event.allDay
|
|
1636
|
-
? toAllDayEndDateInputValue(event.end)
|
|
1637
|
-
: toDateInputValue(event.end),
|
|
1638
|
-
);
|
|
1639
|
-
setEditStartTime(toTimeInputValue(event.start));
|
|
1640
|
-
setEditEndTime(toTimeInputValue(event.end));
|
|
1641
|
-
setEditTimezone(
|
|
1642
|
-
event.startTimeZone || getLocalTimezone(),
|
|
1643
|
-
);
|
|
1644
|
-
setEditTimeScope("single");
|
|
1645
|
-
setEditingField(null);
|
|
1646
|
-
}}
|
|
1647
|
-
>
|
|
1648
|
-
{t("eventForm.cancel")}
|
|
1649
|
-
</Button>
|
|
1650
|
-
<Button
|
|
1651
|
-
size="sm"
|
|
1652
|
-
className="h-6 text-xs"
|
|
1653
|
-
onClick={handleSaveTime}
|
|
1654
|
-
>
|
|
1655
|
-
{t("eventForm.save")}
|
|
1656
|
-
</Button>
|
|
1657
|
-
</div>
|
|
1658
|
-
</div>
|
|
1659
|
-
</div>
|
|
1660
|
-
) : (
|
|
1661
|
-
<div
|
|
1662
|
-
className={`flex items-start gap-3 py-1.5 rounded-md px-0 -mx-0 ${!isOverlay ? "cursor-pointer hover:bg-muted/50" : ""}`}
|
|
1663
|
-
onClick={() => {
|
|
1664
|
-
if (isOverlay) return;
|
|
1665
|
-
setEditingField("time");
|
|
1666
|
-
}}
|
|
1667
|
-
>
|
|
1668
|
-
<IconClock className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
1669
|
-
<div className="text-sm">
|
|
1670
|
-
{event.allDay ? (
|
|
1671
|
-
<div>
|
|
1672
|
-
<span className="text-foreground">
|
|
1673
|
-
{t("eventForm.allDay")}
|
|
1674
|
-
</span>
|
|
1675
|
-
<span className="text-muted-foreground ml-2 text-xs">
|
|
1676
|
-
{formatEventDateRange(
|
|
1677
|
-
event.start,
|
|
1678
|
-
event.end,
|
|
1679
|
-
event.allDay,
|
|
1680
|
-
)}
|
|
1681
|
-
</span>
|
|
1682
|
-
</div>
|
|
1683
|
-
) : (
|
|
1684
|
-
<>
|
|
1685
|
-
<div className="flex items-baseline gap-1">
|
|
1686
|
-
<span className="text-foreground font-medium">
|
|
1687
|
-
{formatTimeShort(event.start)}
|
|
1688
|
-
</span>
|
|
1689
|
-
<span className="text-muted-foreground/50 mx-0.5">
|
|
1690
|
-
→
|
|
1691
|
-
</span>
|
|
1692
|
-
<span className="text-foreground font-medium">
|
|
1693
|
-
{formatTimeShort(event.end)}
|
|
1694
|
-
</span>
|
|
1695
|
-
<span className="text-muted-foreground/50 text-xs ml-1">
|
|
1696
|
-
{formatDuration(event.start, event.end)}
|
|
1697
|
-
</span>
|
|
1698
|
-
</div>
|
|
1699
|
-
<div className="text-muted-foreground text-xs mt-0.5">
|
|
1700
|
-
{formatEventDateRange(event.start, event.end)}
|
|
1701
|
-
</div>
|
|
1702
|
-
</>
|
|
1703
|
-
)}
|
|
1704
|
-
</div>
|
|
1699
|
+
</>
|
|
1700
|
+
)}
|
|
1705
1701
|
</div>
|
|
1702
|
+
</div>
|
|
1703
|
+
|
|
1704
|
+
{canEditRecurrence && (
|
|
1705
|
+
<RepeatPicker
|
|
1706
|
+
preset={getRecurrencePreset(recurrenceRules)}
|
|
1707
|
+
referenceDate={event.start}
|
|
1708
|
+
onChange={handleSaveRecurrence}
|
|
1709
|
+
/>
|
|
1706
1710
|
)}
|
|
1707
1711
|
|
|
1708
1712
|
{!event.allDay && !isOverlay && !isWorkingLocation && (
|
|
@@ -1741,93 +1745,6 @@ export function EventDetailPopover({
|
|
|
1741
1745
|
/>
|
|
1742
1746
|
)}
|
|
1743
1747
|
|
|
1744
|
-
{/* Recurrence */}
|
|
1745
|
-
{editingField === "recurrence" ? (
|
|
1746
|
-
<div className="flex items-start gap-3 py-1.5">
|
|
1747
|
-
<IconRefresh className="mt-1.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
1748
|
-
<div className="flex-1 space-y-2">
|
|
1749
|
-
<Select
|
|
1750
|
-
value={editRecurrencePreset}
|
|
1751
|
-
onValueChange={(value) =>
|
|
1752
|
-
setEditRecurrencePreset(value as RecurrencePreset)
|
|
1753
|
-
}
|
|
1754
|
-
>
|
|
1755
|
-
<SelectTrigger className="h-8 text-sm">
|
|
1756
|
-
<SelectValue />
|
|
1757
|
-
</SelectTrigger>
|
|
1758
|
-
<SelectContent>
|
|
1759
|
-
<SelectItem value="none">
|
|
1760
|
-
{t("eventForm.doesNotRepeat")}
|
|
1761
|
-
</SelectItem>
|
|
1762
|
-
<SelectItem value="daily">
|
|
1763
|
-
{t("eventForm.daily")}
|
|
1764
|
-
</SelectItem>
|
|
1765
|
-
<SelectItem value="weekdays">
|
|
1766
|
-
{t("eventForm.everyWeekday")}
|
|
1767
|
-
</SelectItem>
|
|
1768
|
-
<SelectItem value="weekly">
|
|
1769
|
-
{t("eventForm.weekly")}
|
|
1770
|
-
</SelectItem>
|
|
1771
|
-
<SelectItem value="monthly">
|
|
1772
|
-
{t("eventForm.monthly")}
|
|
1773
|
-
</SelectItem>
|
|
1774
|
-
<SelectItem value="yearly">
|
|
1775
|
-
{t("eventForm.yearly")}
|
|
1776
|
-
</SelectItem>
|
|
1777
|
-
{editRecurrencePreset === "custom" && (
|
|
1778
|
-
<SelectItem value="custom" disabled>
|
|
1779
|
-
{t("eventForm.customSchedule")}
|
|
1780
|
-
</SelectItem>
|
|
1781
|
-
)}
|
|
1782
|
-
</SelectContent>
|
|
1783
|
-
</Select>
|
|
1784
|
-
<div className="flex justify-end gap-1.5">
|
|
1785
|
-
<Button
|
|
1786
|
-
variant="ghost"
|
|
1787
|
-
size="sm"
|
|
1788
|
-
className="h-6 text-xs"
|
|
1789
|
-
onClick={() => {
|
|
1790
|
-
setEditRecurrencePreset(
|
|
1791
|
-
getRecurrencePreset(recurrenceRules),
|
|
1792
|
-
);
|
|
1793
|
-
setEditingField(null);
|
|
1794
|
-
}}
|
|
1795
|
-
>
|
|
1796
|
-
{t("eventForm.cancel")}
|
|
1797
|
-
</Button>
|
|
1798
|
-
<Button
|
|
1799
|
-
size="sm"
|
|
1800
|
-
className="h-6 text-xs"
|
|
1801
|
-
onClick={handleSaveRecurrence}
|
|
1802
|
-
disabled={editRecurrencePreset === "custom"}
|
|
1803
|
-
>
|
|
1804
|
-
{t("eventForm.save")}
|
|
1805
|
-
</Button>
|
|
1806
|
-
</div>
|
|
1807
|
-
</div>
|
|
1808
|
-
</div>
|
|
1809
|
-
) : recurrenceText ? (
|
|
1810
|
-
<button
|
|
1811
|
-
type="button"
|
|
1812
|
-
className={`group flex w-full items-center gap-3 rounded-md py-1.5 text-left ${canEditRecurrence ? "cursor-pointer hover:bg-muted/50" : ""}`}
|
|
1813
|
-
onClick={() => {
|
|
1814
|
-
if (!canEditRecurrence) return;
|
|
1815
|
-
setEditRecurrencePreset(
|
|
1816
|
-
getRecurrencePreset(recurrenceRules),
|
|
1817
|
-
);
|
|
1818
|
-
setEditingField("recurrence");
|
|
1819
|
-
}}
|
|
1820
|
-
>
|
|
1821
|
-
<IconRefresh className="h-4 w-4 shrink-0 text-muted-foreground" />
|
|
1822
|
-
<span className="text-sm text-muted-foreground">
|
|
1823
|
-
{recurrenceText}
|
|
1824
|
-
</span>
|
|
1825
|
-
{canEditRecurrence && (
|
|
1826
|
-
<IconChevronRight className="ml-auto h-3.5 w-3.5 text-muted-foreground/50 opacity-0 transition-opacity group-hover:opacity-100" />
|
|
1827
|
-
)}
|
|
1828
|
-
</button>
|
|
1829
|
-
) : null}
|
|
1830
|
-
|
|
1831
1748
|
{isWorkingLocation && (
|
|
1832
1749
|
<WorkingLocationEditor
|
|
1833
1750
|
event={event}
|
|
@@ -1851,15 +1768,6 @@ export function EventDetailPopover({
|
|
|
1851
1768
|
canEditOptional={!isOverlay}
|
|
1852
1769
|
onToggleOptional={handleToggleAttendeeOptional}
|
|
1853
1770
|
/>
|
|
1854
|
-
) : !isOverlay ? (
|
|
1855
|
-
<div className="px-4 py-1">
|
|
1856
|
-
<div className="flex items-start gap-3">
|
|
1857
|
-
<IconUser className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
1858
|
-
<span className="text-sm text-muted-foreground/60">
|
|
1859
|
-
{t("eventForm.noGuests")}
|
|
1860
|
-
</span>
|
|
1861
|
-
</div>
|
|
1862
|
-
</div>
|
|
1863
1771
|
) : null}
|
|
1864
1772
|
|
|
1865
1773
|
{/* Add guest input */}
|
|
@@ -1872,7 +1780,7 @@ export function EventDetailPopover({
|
|
|
1872
1780
|
(attendee) => attendee.email,
|
|
1873
1781
|
)}
|
|
1874
1782
|
onAdd={handleAddAttendee}
|
|
1875
|
-
placeholder={t("eventForm.
|
|
1783
|
+
placeholder={t("eventForm.addGuest")}
|
|
1876
1784
|
variant="inline"
|
|
1877
1785
|
showChips={false}
|
|
1878
1786
|
showAddButton
|
|
@@ -1897,7 +1805,6 @@ export function EventDetailPopover({
|
|
|
1897
1805
|
<ExtensionSlot
|
|
1898
1806
|
id="calendar.event-detail.bottom"
|
|
1899
1807
|
context={buildEventDetailSlotContext(event)}
|
|
1900
|
-
showEmptyAffordance
|
|
1901
1808
|
/>
|
|
1902
1809
|
</div>
|
|
1903
1810
|
</>
|
|
@@ -2269,18 +2176,6 @@ export function EventDetailPopover({
|
|
|
2269
2176
|
<div className="flex items-start gap-3">
|
|
2270
2177
|
<IconAlignLeft className="mt-1.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
2271
2178
|
<div className="min-w-0 flex-1">
|
|
2272
|
-
{!isOverlay && (
|
|
2273
|
-
<Button
|
|
2274
|
-
type="button"
|
|
2275
|
-
variant="ghost"
|
|
2276
|
-
size="sm"
|
|
2277
|
-
className="mb-1 h-6 gap-1 px-1.5 text-[11px] text-muted-foreground"
|
|
2278
|
-
onClick={handleDraftDescription}
|
|
2279
|
-
>
|
|
2280
|
-
<IconMessage className="h-3 w-3" />
|
|
2281
|
-
{t("eventForm.askAi")}
|
|
2282
|
-
</Button>
|
|
2283
|
-
)}
|
|
2284
2179
|
{isOverlay ? (
|
|
2285
2180
|
event.description ? (
|
|
2286
2181
|
<RenderedDescription
|
|
@@ -2465,13 +2360,6 @@ export function EventDetailPopover({
|
|
|
2465
2360
|
</Select>
|
|
2466
2361
|
</div>
|
|
2467
2362
|
</div>
|
|
2468
|
-
<div className="mt-2 flex items-center gap-3">
|
|
2469
|
-
<IconPalette className="h-4 w-4 shrink-0 text-muted-foreground" />
|
|
2470
|
-
<EventColorSwatches
|
|
2471
|
-
value={event.colorId}
|
|
2472
|
-
onChange={handleColorChange}
|
|
2473
|
-
/>
|
|
2474
|
-
</div>
|
|
2475
2363
|
</div>
|
|
2476
2364
|
</>
|
|
2477
2365
|
) : event.status || event.visibility ? (
|