@agent-native/core 0.98.7 → 0.98.9
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 +1 -1
- package/corpus/core/CHANGELOG.md +12 -0
- package/corpus/core/docs/content/external-agents.mdx +17 -11
- package/corpus/core/docs/content/locales/ar-SA/external-agents.mdx +6 -2
- package/corpus/core/docs/content/locales/de-DE/external-agents.mdx +6 -2
- package/corpus/core/docs/content/locales/es-ES/external-agents.mdx +6 -2
- package/corpus/core/docs/content/locales/fr-FR/external-agents.mdx +6 -2
- package/corpus/core/docs/content/locales/hi-IN/external-agents.mdx +6 -2
- package/corpus/core/docs/content/locales/ja-JP/external-agents.mdx +6 -2
- package/corpus/core/docs/content/locales/ko-KR/external-agents.mdx +6 -2
- package/corpus/core/docs/content/locales/pt-BR/external-agents.mdx +6 -2
- package/corpus/core/docs/content/locales/zh-CN/external-agents.mdx +6 -2
- package/corpus/core/docs/content/locales/zh-TW/external-agents.mdx +6 -2
- package/corpus/core/package.json +1 -1
- package/corpus/core/src/client/session-replay.ts +29 -7
- package/corpus/core/src/integrations/adapters/slack.ts +13 -2
- package/corpus/core/src/integrations/pending-tasks-retry-job.ts +2 -1
- package/corpus/core/src/integrations/pending-tasks-store.ts +1 -1
- package/corpus/core/src/integrations/plugin.ts +183 -48
- package/corpus/core/src/integrations/types.ts +7 -0
- package/corpus/core/src/mcp/build-server.ts +99 -26
- package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +31 -2
- package/corpus/templates/calendar/AGENTS.md +11 -0
- package/corpus/templates/calendar/README.md +2 -1
- package/corpus/templates/calendar/actions/create-event.ts +7 -6
- package/corpus/templates/calendar/actions/event-action-helpers.ts +49 -0
- package/corpus/templates/calendar/actions/update-event.ts +225 -14
- package/corpus/templates/calendar/app/components/calendar/DayView.tsx +185 -70
- package/corpus/templates/calendar/app/components/calendar/EventCard.tsx +26 -4
- package/corpus/templates/calendar/app/components/calendar/EventDetailPanel.tsx +112 -69
- package/corpus/templates/calendar/app/components/calendar/EventDetailPopover.tsx +621 -524
- package/corpus/templates/calendar/app/components/calendar/WeekView.tsx +275 -164
- package/corpus/templates/calendar/app/components/calendar/WorkingLocationEditor.tsx +222 -0
- package/corpus/templates/calendar/app/hooks/use-events.ts +151 -79
- package/corpus/templates/calendar/app/i18n/zh-TW.ts +6 -0
- package/corpus/templates/calendar/app/i18n-data.ts +60 -0
- package/corpus/templates/calendar/app/lib/all-day-layout.ts +126 -0
- package/corpus/templates/calendar/app/lib/event-form-utils.ts +18 -1
- package/corpus/templates/calendar/app/lib/event-mutation-inputs.ts +1 -1
- package/corpus/templates/calendar/app/lib/working-location.ts +163 -0
- package/corpus/templates/calendar/app/pages/CalendarView.tsx +21 -2
- package/corpus/templates/calendar/changelog/2026-07-07-working-locations-from-google-calendar-now-appear-as-native-.md +6 -0
- package/corpus/templates/calendar/server/lib/calendar-availability.ts +2 -0
- package/corpus/templates/calendar/server/lib/google-api.ts +6 -1
- package/corpus/templates/calendar/server/lib/google-calendar.ts +49 -19
- package/corpus/templates/calendar/shared/api.ts +2 -0
- package/dist/client/session-replay.d.ts.map +1 -1
- package/dist/client/session-replay.js +24 -7
- package/dist/client/session-replay.js.map +1 -1
- package/dist/collab/routes.d.ts +2 -2
- package/dist/integrations/adapters/slack.js +11 -2
- package/dist/integrations/adapters/slack.js.map +1 -1
- package/dist/integrations/pending-tasks-retry-job.d.ts.map +1 -1
- package/dist/integrations/pending-tasks-retry-job.js +2 -1
- package/dist/integrations/pending-tasks-retry-job.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/plugin.d.ts.map +1 -1
- package/dist/integrations/plugin.js +140 -34
- package/dist/integrations/plugin.js.map +1 -1
- package/dist/integrations/types.d.ts +7 -0
- package/dist/integrations/types.d.ts.map +1 -1
- package/dist/integrations/types.js.map +1 -1
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +82 -28
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +5 -5
- package/dist/secrets/routes.d.ts +9 -9
- package/docs/content/external-agents.mdx +17 -11
- package/docs/content/locales/ar-SA/external-agents.mdx +6 -2
- package/docs/content/locales/de-DE/external-agents.mdx +6 -2
- package/docs/content/locales/es-ES/external-agents.mdx +6 -2
- package/docs/content/locales/fr-FR/external-agents.mdx +6 -2
- package/docs/content/locales/hi-IN/external-agents.mdx +6 -2
- package/docs/content/locales/ja-JP/external-agents.mdx +6 -2
- package/docs/content/locales/ko-KR/external-agents.mdx +6 -2
- package/docs/content/locales/pt-BR/external-agents.mdx +6 -2
- package/docs/content/locales/zh-CN/external-agents.mdx +6 -2
- package/docs/content/locales/zh-TW/external-agents.mdx +6 -2
- package/package.json +1 -1
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
} from "@/components/calendar/EventOptionControls";
|
|
48
48
|
import { FindTimeTakeover } from "@/components/calendar/FindTimePanel";
|
|
49
49
|
import { useGuestNotificationPrompt } from "@/components/calendar/GuestNotificationDialog";
|
|
50
|
+
import { WorkingLocationEditor } from "@/components/calendar/WorkingLocationEditor";
|
|
50
51
|
import { useCalendarContext } from "@/components/layout/AppLayout";
|
|
51
52
|
import { TimezoneCombobox } from "@/components/TimezoneCombobox";
|
|
52
53
|
import { Button } from "@/components/ui/button";
|
|
@@ -89,7 +90,9 @@ import {
|
|
|
89
90
|
getEventEndValidationMessage,
|
|
90
91
|
getLocalTimezone,
|
|
91
92
|
getRecurrencePreset,
|
|
93
|
+
normalizeAllDayEditEndDate,
|
|
92
94
|
remindersToDraftState,
|
|
95
|
+
resolveTimeEditScope,
|
|
93
96
|
type AttachmentDraft,
|
|
94
97
|
type RecurrencePreset,
|
|
95
98
|
type ReminderDraft,
|
|
@@ -102,6 +105,13 @@ import {
|
|
|
102
105
|
setEventDetailPopoverOpen,
|
|
103
106
|
} from "@/lib/popover-click-guard";
|
|
104
107
|
import { shortcutModifierLabel } from "@/lib/utils";
|
|
108
|
+
import {
|
|
109
|
+
buildWorkingLocationUpdate,
|
|
110
|
+
createWorkingLocationDisplayLabels,
|
|
111
|
+
getWorkingLocationTitle,
|
|
112
|
+
isWorkingLocationEvent,
|
|
113
|
+
type WorkingLocationSelection,
|
|
114
|
+
} from "@/lib/working-location";
|
|
105
115
|
|
|
106
116
|
const ZOOM_AFTER_CONNECT_EVENT_ID_KEY = "calendar.zoomAfterConnectEventId";
|
|
107
117
|
const ZOOM_AFTER_CONNECT_MAX_AGE_MS = 10 * 60 * 1000;
|
|
@@ -299,6 +309,8 @@ type EventUpdatePatch = Partial<CalendarEvent> & {
|
|
|
299
309
|
addZoom?: boolean;
|
|
300
310
|
addAttendees?: CalendarEvent["attendees"];
|
|
301
311
|
scope?: UpdateEventScope;
|
|
312
|
+
workingLocationType?: "homeOffice" | "officeLocation" | "customLocation";
|
|
313
|
+
workingLocationLabel?: string;
|
|
302
314
|
};
|
|
303
315
|
|
|
304
316
|
function mergeAttendeesForPrompt(
|
|
@@ -495,6 +507,7 @@ export function EventDetailPopover({
|
|
|
495
507
|
onDraftDiscard,
|
|
496
508
|
}: EventDetailPopoverProps) {
|
|
497
509
|
const t = useT();
|
|
510
|
+
const workingLocationLabels = createWorkingLocationDisplayLabels(t);
|
|
498
511
|
const isMobile = useIsMobile();
|
|
499
512
|
const [open, setOpen] = useState(defaultOpen);
|
|
500
513
|
const [editingTitle, setEditingTitle] = useState(
|
|
@@ -513,6 +526,9 @@ export function EventDetailPopover({
|
|
|
513
526
|
setSidebarEvent,
|
|
514
527
|
setFocusedEvent,
|
|
515
528
|
} = useCalendarContext();
|
|
529
|
+
const isWorkingLocation = isWorkingLocationEvent(event);
|
|
530
|
+
const isSingleDayWorkingLocation = isWorkingLocation && event.allDay;
|
|
531
|
+
const editableLocationValue = event.location || "";
|
|
516
532
|
|
|
517
533
|
// Inline editing state
|
|
518
534
|
const [editingField, setEditingField] = useState<string | null>(null);
|
|
@@ -520,7 +536,7 @@ export function EventDetailPopover({
|
|
|
520
536
|
const [editDescription, setEditDescription] = useState(
|
|
521
537
|
event.description || "",
|
|
522
538
|
);
|
|
523
|
-
const [editLocation, setEditLocation] = useState(
|
|
539
|
+
const [editLocation, setEditLocation] = useState(editableLocationValue);
|
|
524
540
|
const [editDate, setEditDate] = useState(() => toDateInputValue(event.start));
|
|
525
541
|
const [editEndDate, setEditEndDate] = useState(() =>
|
|
526
542
|
event.allDay
|
|
@@ -588,7 +604,7 @@ export function EventDetailPopover({
|
|
|
588
604
|
useEffect(() => {
|
|
589
605
|
if (editingField !== "description")
|
|
590
606
|
setEditDescription(event.description || "");
|
|
591
|
-
if (editingField !== "location") setEditLocation(
|
|
607
|
+
if (editingField !== "location") setEditLocation(editableLocationValue);
|
|
592
608
|
if (editingField !== "time") {
|
|
593
609
|
setEditDate(toDateInputValue(event.start));
|
|
594
610
|
setEditEndDate(
|
|
@@ -618,6 +634,7 @@ export function EventDetailPopover({
|
|
|
618
634
|
event.id,
|
|
619
635
|
event.description,
|
|
620
636
|
event.location,
|
|
637
|
+
event.workingLocationProperties,
|
|
621
638
|
event.start,
|
|
622
639
|
event.end,
|
|
623
640
|
event.allDay,
|
|
@@ -625,6 +642,7 @@ export function EventDetailPopover({
|
|
|
625
642
|
event.reminders,
|
|
626
643
|
event.remindersUseDefault,
|
|
627
644
|
event.attachments,
|
|
645
|
+
editableLocationValue,
|
|
628
646
|
]);
|
|
629
647
|
|
|
630
648
|
useEffect(() => {
|
|
@@ -673,7 +691,6 @@ export function EventDetailPopover({
|
|
|
673
691
|
? event.visibility
|
|
674
692
|
: "default";
|
|
675
693
|
const reminderValue = getReminderValue(event);
|
|
676
|
-
|
|
677
694
|
// Save a field update
|
|
678
695
|
const saveField = useCallback(
|
|
679
696
|
(updates: EventUpdatePatch) => {
|
|
@@ -958,13 +975,14 @@ export function EventDetailPopover({
|
|
|
958
975
|
const locationContainsMeetingLink =
|
|
959
976
|
!!meetingLink && event.location?.includes(meetingLink.url);
|
|
960
977
|
if (locationContainsMeetingLink && !trimmed) {
|
|
961
|
-
setEditLocation(
|
|
978
|
+
setEditLocation(editableLocationValue);
|
|
962
979
|
setEditingField(null);
|
|
963
980
|
return false;
|
|
964
981
|
}
|
|
965
982
|
let saved = false;
|
|
966
|
-
|
|
967
|
-
|
|
983
|
+
const currentValue = (event.location || "").trim();
|
|
984
|
+
if (trimmed !== currentValue.trim()) {
|
|
985
|
+
const updates: EventUpdatePatch = { location: trimmed };
|
|
968
986
|
if (
|
|
969
987
|
locationContainsMeetingLink &&
|
|
970
988
|
meetingLink &&
|
|
@@ -982,10 +1000,38 @@ export function EventDetailPopover({
|
|
|
982
1000
|
}
|
|
983
1001
|
setEditingField(null);
|
|
984
1002
|
return saved;
|
|
985
|
-
}, [
|
|
1003
|
+
}, [
|
|
1004
|
+
editLocation,
|
|
1005
|
+
editableLocationValue,
|
|
1006
|
+
event.description,
|
|
1007
|
+
event.location,
|
|
1008
|
+
meetingLink,
|
|
1009
|
+
saveField,
|
|
1010
|
+
t,
|
|
1011
|
+
]);
|
|
1012
|
+
|
|
1013
|
+
const handleSaveWorkingLocation = useCallback(
|
|
1014
|
+
(selection: WorkingLocationSelection) => {
|
|
1015
|
+
const update = buildWorkingLocationUpdate(event, selection);
|
|
1016
|
+
if (isDraft) {
|
|
1017
|
+
const { id: _id, scope: _scope, ...draftUpdate } = update;
|
|
1018
|
+
onDraftUpdate?.(event.id, draftUpdate);
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
1021
|
+
updateEvent.mutate(update, {
|
|
1022
|
+
onError: () => toast.error(t("calendarView.failedUpdateEvent")),
|
|
1023
|
+
});
|
|
1024
|
+
},
|
|
1025
|
+
[event, isDraft, onDraftUpdate, t, updateEvent],
|
|
1026
|
+
);
|
|
986
1027
|
|
|
987
1028
|
const handleSaveTime = useCallback(() => {
|
|
988
|
-
const
|
|
1029
|
+
const normalizedEndDate = normalizeAllDayEditEndDate(
|
|
1030
|
+
isSingleDayWorkingLocation,
|
|
1031
|
+
editDate,
|
|
1032
|
+
editEndDate,
|
|
1033
|
+
);
|
|
1034
|
+
const allDayEnd = new Date(`${normalizedEndDate}T00:00:00`);
|
|
989
1035
|
allDayEnd.setDate(allDayEnd.getDate() + 1);
|
|
990
1036
|
const newStart = event.allDay
|
|
991
1037
|
? new Date(`${editDate}T00:00:00`).toISOString()
|
|
@@ -1013,7 +1059,11 @@ export function EventDetailPopover({
|
|
|
1013
1059
|
allDay: event.allDay,
|
|
1014
1060
|
startTimeZone: event.allDay ? undefined : editTimezone,
|
|
1015
1061
|
endTimeZone: event.allDay ? undefined : editTimezone,
|
|
1016
|
-
scope:
|
|
1062
|
+
scope: resolveTimeEditScope(
|
|
1063
|
+
isRecurringEvent,
|
|
1064
|
+
isSingleDayWorkingLocation,
|
|
1065
|
+
editTimeScope,
|
|
1066
|
+
),
|
|
1017
1067
|
});
|
|
1018
1068
|
}
|
|
1019
1069
|
setEditTimeScope("single");
|
|
@@ -1028,6 +1078,7 @@ export function EventDetailPopover({
|
|
|
1028
1078
|
event.start,
|
|
1029
1079
|
event.end,
|
|
1030
1080
|
event.allDay,
|
|
1081
|
+
isSingleDayWorkingLocation,
|
|
1031
1082
|
isRecurringEvent,
|
|
1032
1083
|
editTimeScope,
|
|
1033
1084
|
saveField,
|
|
@@ -1346,7 +1397,11 @@ export function EventDetailPopover({
|
|
|
1346
1397
|
<div className="flex items-center justify-between px-4 py-2.5 border-b border-border">
|
|
1347
1398
|
<div className="text-[11px] font-medium uppercase tracking-wider text-muted-foreground">
|
|
1348
1399
|
<span>
|
|
1349
|
-
{
|
|
1400
|
+
{isWorkingLocation
|
|
1401
|
+
? t("eventForm.workingLocation")
|
|
1402
|
+
: isDraft
|
|
1403
|
+
? t("eventForm.draftEvent")
|
|
1404
|
+
: t("eventForm.event")}
|
|
1350
1405
|
</span>
|
|
1351
1406
|
</div>
|
|
1352
1407
|
<div className="flex items-center gap-0.5">
|
|
@@ -1382,7 +1437,7 @@ export function EventDetailPopover({
|
|
|
1382
1437
|
<div className="flex-1 overflow-y-auto">
|
|
1383
1438
|
<div className="px-4 pt-4 pb-1">
|
|
1384
1439
|
{/* Title — always editable */}
|
|
1385
|
-
{isEditingTitle ? (
|
|
1440
|
+
{isEditingTitle && !isWorkingLocation ? (
|
|
1386
1441
|
<input
|
|
1387
1442
|
ref={titleInputRef}
|
|
1388
1443
|
value={editingTitle}
|
|
@@ -1432,14 +1487,14 @@ export function EventDetailPopover({
|
|
|
1432
1487
|
/>
|
|
1433
1488
|
) : (
|
|
1434
1489
|
<h2
|
|
1435
|
-
className=
|
|
1490
|
+
className={`mb-4 -mx-0.5 rounded px-0.5 text-lg font-semibold leading-tight text-foreground ${!isOverlay && !isWorkingLocation ? "cursor-text hover:bg-muted/50" : ""}`}
|
|
1436
1491
|
onClick={() => {
|
|
1437
|
-
if (isOverlay) return;
|
|
1492
|
+
if (isOverlay || isWorkingLocation) return;
|
|
1438
1493
|
setEditingTitle(event.title);
|
|
1439
1494
|
setIsEditingTitle(true);
|
|
1440
1495
|
}}
|
|
1441
1496
|
>
|
|
1442
|
-
{event
|
|
1497
|
+
{getWorkingLocationTitle(event, workingLocationLabels)}
|
|
1443
1498
|
</h2>
|
|
1444
1499
|
)}
|
|
1445
1500
|
</div>
|
|
@@ -1459,7 +1514,13 @@ export function EventDetailPopover({
|
|
|
1459
1514
|
<div className="flex items-start gap-3 py-1.5">
|
|
1460
1515
|
<IconClock className="mt-2 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
1461
1516
|
<div className="flex-1 space-y-2">
|
|
1462
|
-
<div
|
|
1517
|
+
<div
|
|
1518
|
+
className={`grid gap-2 ${
|
|
1519
|
+
isSingleDayWorkingLocation
|
|
1520
|
+
? "grid-cols-1"
|
|
1521
|
+
: "grid-cols-2"
|
|
1522
|
+
}`}
|
|
1523
|
+
>
|
|
1463
1524
|
<input
|
|
1464
1525
|
type="date"
|
|
1465
1526
|
value={editDate}
|
|
@@ -1467,40 +1528,49 @@ export function EventDetailPopover({
|
|
|
1467
1528
|
const next = e.target.value;
|
|
1468
1529
|
setEditDate(next);
|
|
1469
1530
|
setEditEndDate((current) =>
|
|
1470
|
-
|
|
1531
|
+
// i18n-ignore -- this expression selects a date; it is not visible copy.
|
|
1532
|
+
isSingleDayWorkingLocation
|
|
1533
|
+
? next
|
|
1534
|
+
: current < next
|
|
1535
|
+
? next
|
|
1536
|
+
: current,
|
|
1471
1537
|
);
|
|
1472
1538
|
}}
|
|
1473
1539
|
className="w-full rounded-md border border-border bg-transparent px-2 py-1 text-sm text-foreground"
|
|
1474
1540
|
aria-label={t("eventForm.startDate")}
|
|
1475
1541
|
/>
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
<input
|
|
1489
|
-
type="time"
|
|
1490
|
-
value={editStartTime}
|
|
1491
|
-
onChange={(e) => setEditStartTime(e.target.value)}
|
|
1492
|
-
className="flex-1 rounded-md border border-border bg-transparent px-2 py-1 text-sm text-foreground"
|
|
1493
|
-
/>
|
|
1494
|
-
<span className="text-muted-foreground/50 text-xs">
|
|
1495
|
-
→
|
|
1496
|
-
</span>
|
|
1497
|
-
<input
|
|
1498
|
-
type="time"
|
|
1499
|
-
value={editEndTime}
|
|
1500
|
-
onChange={(e) => setEditEndTime(e.target.value)}
|
|
1501
|
-
className="flex-1 rounded-md border border-border bg-transparent px-2 py-1 text-sm text-foreground"
|
|
1502
|
-
/>
|
|
1542
|
+
{!isSingleDayWorkingLocation && (
|
|
1543
|
+
<input
|
|
1544
|
+
type="date"
|
|
1545
|
+
min={editDate}
|
|
1546
|
+
value={editEndDate}
|
|
1547
|
+
onChange={(e) =>
|
|
1548
|
+
setEditEndDate(e.target.value || editDate)
|
|
1549
|
+
}
|
|
1550
|
+
className="w-full rounded-md border border-border bg-transparent px-2 py-1 text-sm text-foreground"
|
|
1551
|
+
aria-label={t("eventForm.endDate")}
|
|
1552
|
+
/>
|
|
1553
|
+
)}
|
|
1503
1554
|
</div>
|
|
1555
|
+
{!event.allDay && (
|
|
1556
|
+
<div className="flex items-center gap-2">
|
|
1557
|
+
<input
|
|
1558
|
+
type="time"
|
|
1559
|
+
value={editStartTime}
|
|
1560
|
+
onChange={(e) => setEditStartTime(e.target.value)}
|
|
1561
|
+
className="flex-1 rounded-md border border-border bg-transparent px-2 py-1 text-sm text-foreground"
|
|
1562
|
+
/>
|
|
1563
|
+
<span className="text-muted-foreground/50 text-xs">
|
|
1564
|
+
→
|
|
1565
|
+
</span>
|
|
1566
|
+
<input
|
|
1567
|
+
type="time"
|
|
1568
|
+
value={editEndTime}
|
|
1569
|
+
onChange={(e) => setEditEndTime(e.target.value)}
|
|
1570
|
+
className="flex-1 rounded-md border border-border bg-transparent px-2 py-1 text-sm text-foreground"
|
|
1571
|
+
/>
|
|
1572
|
+
</div>
|
|
1573
|
+
)}
|
|
1504
1574
|
{!event.allDay && (
|
|
1505
1575
|
<TimezoneCombobox
|
|
1506
1576
|
id={`event-timezone-${event.id}`}
|
|
@@ -1508,31 +1578,37 @@ export function EventDetailPopover({
|
|
|
1508
1578
|
onChange={setEditTimezone}
|
|
1509
1579
|
/>
|
|
1510
1580
|
)}
|
|
1511
|
-
{isRecurringEvent &&
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1581
|
+
{isRecurringEvent &&
|
|
1582
|
+
!isDraft &&
|
|
1583
|
+
!isSingleDayWorkingLocation && (
|
|
1584
|
+
<div className="flex items-center gap-2">
|
|
1585
|
+
<span className="text-xs text-muted-foreground">
|
|
1586
|
+
{t("eventForm.applyTo")}
|
|
1587
|
+
</span>
|
|
1588
|
+
<Select
|
|
1589
|
+
value={editTimeScope}
|
|
1590
|
+
onValueChange={(value) =>
|
|
1591
|
+
setEditTimeScope(value as UpdateEventScope)
|
|
1592
|
+
}
|
|
1593
|
+
>
|
|
1594
|
+
<SelectTrigger className="h-7 flex-1 text-xs">
|
|
1595
|
+
<SelectValue />
|
|
1596
|
+
</SelectTrigger>
|
|
1597
|
+
<SelectContent>
|
|
1598
|
+
<SelectItem value="single">
|
|
1599
|
+
{isWorkingLocation
|
|
1600
|
+
? t("eventForm.thisDayOnly")
|
|
1601
|
+
: t("eventForm.thisEvent")}
|
|
1602
|
+
</SelectItem>
|
|
1603
|
+
<SelectItem value="all">
|
|
1604
|
+
{isWorkingLocation
|
|
1605
|
+
? t("eventForm.allDays")
|
|
1606
|
+
: t("eventForm.allEvents")}
|
|
1607
|
+
</SelectItem>
|
|
1608
|
+
</SelectContent>
|
|
1609
|
+
</Select>
|
|
1610
|
+
</div>
|
|
1611
|
+
)}
|
|
1536
1612
|
<div className="flex justify-end gap-1.5">
|
|
1537
1613
|
<Button
|
|
1538
1614
|
variant="ghost"
|
|
@@ -1614,7 +1690,7 @@ export function EventDetailPopover({
|
|
|
1614
1690
|
</div>
|
|
1615
1691
|
)}
|
|
1616
1692
|
|
|
1617
|
-
{!event.allDay && !isOverlay && (
|
|
1693
|
+
{!event.allDay && !isOverlay && !isWorkingLocation && (
|
|
1618
1694
|
<div className="flex items-center gap-3 py-1">
|
|
1619
1695
|
<div className="h-4 w-4 shrink-0" />
|
|
1620
1696
|
<Button
|
|
@@ -1630,7 +1706,7 @@ export function EventDetailPopover({
|
|
|
1630
1706
|
</div>
|
|
1631
1707
|
)}
|
|
1632
1708
|
|
|
1633
|
-
{!event.allDay && !isOverlay && (
|
|
1709
|
+
{!event.allDay && !isOverlay && !isWorkingLocation && (
|
|
1634
1710
|
<FindTimeTakeover
|
|
1635
1711
|
open={findTimeOpen}
|
|
1636
1712
|
onOpenChange={setFindTimeOpen}
|
|
@@ -1742,222 +1818,237 @@ export function EventDetailPopover({
|
|
|
1742
1818
|
)}
|
|
1743
1819
|
</button>
|
|
1744
1820
|
) : null}
|
|
1821
|
+
|
|
1822
|
+
{isWorkingLocation && (
|
|
1823
|
+
<WorkingLocationEditor
|
|
1824
|
+
event={event}
|
|
1825
|
+
isRecurring={isRecurringEvent}
|
|
1826
|
+
readOnly={isOverlay}
|
|
1827
|
+
disabled={updateEvent.isPending}
|
|
1828
|
+
onSave={handleSaveWorkingLocation}
|
|
1829
|
+
/>
|
|
1830
|
+
)}
|
|
1745
1831
|
</div>
|
|
1746
1832
|
|
|
1747
|
-
{
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
<div className="px-4 py-1">
|
|
1759
|
-
<div className="flex items-start gap-3">
|
|
1760
|
-
<IconUser className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
1761
|
-
<span className="text-sm text-muted-foreground/60">
|
|
1762
|
-
{t("eventForm.noGuests")}
|
|
1763
|
-
</span>
|
|
1764
|
-
</div>
|
|
1765
|
-
</div>
|
|
1766
|
-
) : null}
|
|
1767
|
-
|
|
1768
|
-
{/* Add guest input */}
|
|
1769
|
-
{!isOverlay && (
|
|
1770
|
-
<div className="px-4 py-1">
|
|
1771
|
-
<div className="flex items-center gap-3">
|
|
1772
|
-
<IconPlus className="h-4 w-4 shrink-0 text-muted-foreground/40" />
|
|
1773
|
-
<AttendeeAutocomplete
|
|
1774
|
-
selectedEmails={(event.attendees || []).map(
|
|
1775
|
-
(attendee) => attendee.email,
|
|
1776
|
-
)}
|
|
1777
|
-
onAdd={handleAddAttendee}
|
|
1778
|
-
placeholder={t("eventForm.addGuests")}
|
|
1779
|
-
variant="inline"
|
|
1780
|
-
showChips={false}
|
|
1781
|
-
showAddButton
|
|
1782
|
-
inputClassName="text-foreground placeholder:text-muted-foreground/40"
|
|
1833
|
+
{!isWorkingLocation && (
|
|
1834
|
+
<>
|
|
1835
|
+
{/* Separator */}
|
|
1836
|
+
<div className="mx-4 my-2 border-t border-border/50" />
|
|
1837
|
+
|
|
1838
|
+
{/* Attendees — always shown */}
|
|
1839
|
+
{event.attendees && event.attendees.length > 0 ? (
|
|
1840
|
+
<EventAttendeesSection
|
|
1841
|
+
event={event}
|
|
1842
|
+
canEditOptional={!isOverlay}
|
|
1843
|
+
onToggleOptional={handleToggleAttendeeOptional}
|
|
1783
1844
|
/>
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1845
|
+
) : !isOverlay ? (
|
|
1846
|
+
<div className="px-4 py-1">
|
|
1847
|
+
<div className="flex items-start gap-3">
|
|
1848
|
+
<IconUser className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
1849
|
+
<span className="text-sm text-muted-foreground/60">
|
|
1850
|
+
{t("eventForm.noGuests")}
|
|
1851
|
+
</span>
|
|
1852
|
+
</div>
|
|
1853
|
+
</div>
|
|
1854
|
+
) : null}
|
|
1855
|
+
|
|
1856
|
+
{/* Add guest input */}
|
|
1857
|
+
{!isOverlay && (
|
|
1858
|
+
<div className="px-4 py-1">
|
|
1859
|
+
<div className="flex items-center gap-3">
|
|
1860
|
+
<IconPlus className="h-4 w-4 shrink-0 text-muted-foreground/40" />
|
|
1861
|
+
<AttendeeAutocomplete
|
|
1862
|
+
selectedEmails={(event.attendees || []).map(
|
|
1863
|
+
(attendee) => attendee.email,
|
|
1864
|
+
)}
|
|
1865
|
+
onAdd={handleAddAttendee}
|
|
1866
|
+
placeholder={t("eventForm.addGuests")}
|
|
1867
|
+
variant="inline"
|
|
1868
|
+
showChips={false}
|
|
1869
|
+
showAddButton
|
|
1870
|
+
inputClassName="text-foreground placeholder:text-muted-foreground/40"
|
|
1871
|
+
/>
|
|
1872
|
+
</div>
|
|
1873
|
+
</div>
|
|
1874
|
+
)}
|
|
1875
|
+
|
|
1876
|
+
{/* Research Meeting button */}
|
|
1877
|
+
{event.attendees && event.attendees.length > 0 && (
|
|
1878
|
+
<>
|
|
1879
|
+
<div className="mx-4 my-2 border-t border-border/50" />
|
|
1880
|
+
<div className="px-4 py-1">
|
|
1881
|
+
<ResearchMeetingButton event={event} />
|
|
1882
|
+
</div>
|
|
1883
|
+
</>
|
|
1884
|
+
)}
|
|
1787
1885
|
|
|
1788
|
-
{/* Research Meeting button */}
|
|
1789
|
-
{event.attendees && event.attendees.length > 0 && (
|
|
1790
|
-
<>
|
|
1791
1886
|
<div className="mx-4 my-2 border-t border-border/50" />
|
|
1792
1887
|
<div className="px-4 py-1">
|
|
1793
|
-
<
|
|
1888
|
+
<ExtensionSlot
|
|
1889
|
+
id="calendar.event-detail.bottom"
|
|
1890
|
+
context={buildEventDetailSlotContext(event)}
|
|
1891
|
+
showEmptyAffordance
|
|
1892
|
+
/>
|
|
1794
1893
|
</div>
|
|
1795
1894
|
</>
|
|
1796
1895
|
)}
|
|
1797
1896
|
|
|
1798
|
-
<div className="mx-4 my-2 border-t border-border/50" />
|
|
1799
|
-
<div className="px-4 py-1">
|
|
1800
|
-
<ExtensionSlot
|
|
1801
|
-
id="calendar.event-detail.bottom"
|
|
1802
|
-
context={buildEventDetailSlotContext(event)}
|
|
1803
|
-
showEmptyAffordance
|
|
1804
|
-
/>
|
|
1805
|
-
</div>
|
|
1806
|
-
|
|
1807
1897
|
{/* Meeting link */}
|
|
1808
|
-
{
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
<
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
<IconVideo className="h-5 w-5 mr-2 opacity-80" />
|
|
1819
|
-
<span>{getMeetingLabel(meetingLink.type, t)}</span>
|
|
1820
|
-
<span className="absolute right-4 hidden items-center gap-1 opacity-50 sm:flex">
|
|
1821
|
-
<kbd className="text-xs font-normal">
|
|
1822
|
-
{shortcutModifierLabel()}
|
|
1823
|
-
</kbd>
|
|
1824
|
-
<kbd className="inline-flex h-5 w-5 items-center justify-center rounded bg-white/20 text-[11px] font-medium">
|
|
1825
|
-
J
|
|
1826
|
-
</kbd>
|
|
1827
|
-
</span>
|
|
1828
|
-
</a>
|
|
1829
|
-
{(meetingLink.pin || meetingLink.passcode) && (
|
|
1830
|
-
<div className="mt-1.5 text-xs text-muted-foreground/60">
|
|
1831
|
-
{meetingLink.pin && (
|
|
1832
|
-
<span>
|
|
1833
|
-
{t("eventForm.pin", { pin: meetingLink.pin })}
|
|
1834
|
-
</span>
|
|
1835
|
-
)}
|
|
1836
|
-
{meetingLink.pin && meetingLink.passcode && (
|
|
1837
|
-
<span className="mx-1">·</span>
|
|
1838
|
-
)}
|
|
1839
|
-
{meetingLink.passcode && (
|
|
1840
|
-
<span>
|
|
1841
|
-
{t("eventForm.passcode", {
|
|
1842
|
-
passcode: meetingLink.passcode,
|
|
1843
|
-
})}
|
|
1844
|
-
</span>
|
|
1845
|
-
)}
|
|
1846
|
-
</div>
|
|
1847
|
-
)}
|
|
1848
|
-
</div>
|
|
1849
|
-
</>
|
|
1850
|
-
) : pendingConferenceProvider ? (
|
|
1851
|
-
<>
|
|
1852
|
-
<div className="mx-4 my-2 border-t border-border/50" />
|
|
1853
|
-
<div className="px-4 py-1.5">
|
|
1854
|
-
<div className="flex w-full items-center rounded-xl bg-[#4965E0] py-2 pl-4 pr-2 text-white">
|
|
1855
|
-
{pendingConferenceProvider === "zoom" ? (
|
|
1856
|
-
<IconBrandZoom className="mr-2 h-5 w-5 opacity-90" />
|
|
1857
|
-
) : (
|
|
1858
|
-
<IconVideo className="mr-2 h-5 w-5 opacity-90" />
|
|
1859
|
-
)}
|
|
1860
|
-
<span className="text-[15px] font-semibold">
|
|
1861
|
-
{pendingConferenceProvider === "zoom"
|
|
1862
|
-
? t("eventForm.zoom")
|
|
1863
|
-
: t("eventForm.googleMeet")}
|
|
1864
|
-
</span>
|
|
1865
|
-
<button
|
|
1866
|
-
type="button"
|
|
1867
|
-
onClick={handleRemovePendingConference}
|
|
1868
|
-
aria-label={`Remove ${
|
|
1869
|
-
pendingConferenceProvider === "zoom"
|
|
1870
|
-
? t("eventForm.zoom")
|
|
1871
|
-
: t("eventForm.googleMeet")
|
|
1872
|
-
}`}
|
|
1873
|
-
className="ml-auto rounded-md p-1 text-white/70 transition-colors hover:bg-white/15 hover:text-white"
|
|
1898
|
+
{!isWorkingLocation &&
|
|
1899
|
+
(meetingLink ? (
|
|
1900
|
+
<>
|
|
1901
|
+
<div className="mx-4 my-2 border-t border-border/50" />
|
|
1902
|
+
<div className="px-4 py-1.5">
|
|
1903
|
+
<a
|
|
1904
|
+
href={meetingLink.url}
|
|
1905
|
+
target="_blank"
|
|
1906
|
+
rel="noopener noreferrer"
|
|
1907
|
+
className="flex items-center justify-center w-full rounded-xl bg-[#4965E0] hover:bg-[#5A75F0] text-white font-semibold py-2 px-4 text-[15px] relative"
|
|
1874
1908
|
>
|
|
1875
|
-
<
|
|
1876
|
-
|
|
1909
|
+
<IconVideo className="h-5 w-5 mr-2 opacity-80" />
|
|
1910
|
+
<span>{getMeetingLabel(meetingLink.type, t)}</span>
|
|
1911
|
+
<span className="absolute right-4 hidden items-center gap-1 opacity-50 sm:flex">
|
|
1912
|
+
<kbd className="text-xs font-normal">
|
|
1913
|
+
{shortcutModifierLabel()}
|
|
1914
|
+
</kbd>
|
|
1915
|
+
<kbd className="inline-flex h-5 w-5 items-center justify-center rounded bg-white/20 text-[11px] font-medium">
|
|
1916
|
+
J
|
|
1917
|
+
</kbd>
|
|
1918
|
+
</span>
|
|
1919
|
+
</a>
|
|
1920
|
+
{(meetingLink.pin || meetingLink.passcode) && (
|
|
1921
|
+
<div className="mt-1.5 text-xs text-muted-foreground/60">
|
|
1922
|
+
{meetingLink.pin && (
|
|
1923
|
+
<span>
|
|
1924
|
+
{t("eventForm.pin", { pin: meetingLink.pin })}
|
|
1925
|
+
</span>
|
|
1926
|
+
)}
|
|
1927
|
+
{meetingLink.pin && meetingLink.passcode && (
|
|
1928
|
+
<span className="mx-1">·</span>
|
|
1929
|
+
)}
|
|
1930
|
+
{meetingLink.passcode && (
|
|
1931
|
+
<span>
|
|
1932
|
+
{t("eventForm.passcode", {
|
|
1933
|
+
passcode: meetingLink.passcode,
|
|
1934
|
+
})}
|
|
1935
|
+
</span>
|
|
1936
|
+
)}
|
|
1937
|
+
</div>
|
|
1938
|
+
)}
|
|
1877
1939
|
</div>
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
</>
|
|
1883
|
-
) : !isOverlay ? (
|
|
1884
|
-
<>
|
|
1885
|
-
<div className="mx-4 my-2 border-t border-border/50" />
|
|
1886
|
-
{editingField === "meetingLink" ? (
|
|
1940
|
+
</>
|
|
1941
|
+
) : pendingConferenceProvider ? (
|
|
1942
|
+
<>
|
|
1943
|
+
<div className="mx-4 my-2 border-t border-border/50" />
|
|
1887
1944
|
<div className="px-4 py-1.5">
|
|
1888
|
-
<div className="flex items-center
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1945
|
+
<div className="flex w-full items-center rounded-xl bg-[#4965E0] py-2 pl-4 pr-2 text-white">
|
|
1946
|
+
{pendingConferenceProvider === "zoom" ? (
|
|
1947
|
+
<IconBrandZoom className="mr-2 h-5 w-5 opacity-90" />
|
|
1948
|
+
) : (
|
|
1949
|
+
<IconVideo className="mr-2 h-5 w-5 opacity-90" />
|
|
1950
|
+
)}
|
|
1951
|
+
<span className="text-[15px] font-semibold">
|
|
1952
|
+
{pendingConferenceProvider === "zoom"
|
|
1953
|
+
? t("eventForm.zoom")
|
|
1954
|
+
: t("eventForm.googleMeet")}
|
|
1955
|
+
</span>
|
|
1956
|
+
<button
|
|
1957
|
+
type="button"
|
|
1958
|
+
onClick={handleRemovePendingConference}
|
|
1959
|
+
aria-label={`Remove ${
|
|
1960
|
+
pendingConferenceProvider === "zoom"
|
|
1961
|
+
? t("eventForm.zoom")
|
|
1962
|
+
: t("eventForm.googleMeet")
|
|
1963
|
+
}`}
|
|
1964
|
+
className="ml-auto rounded-md p-1 text-white/70 transition-colors hover:bg-white/15 hover:text-white"
|
|
1965
|
+
>
|
|
1966
|
+
<IconX className="h-4 w-4" />
|
|
1967
|
+
</button>
|
|
1910
1968
|
</div>
|
|
1969
|
+
<p className="mt-1.5 text-xs text-muted-foreground/60">
|
|
1970
|
+
{t("eventForm.conferencingLinkOnSave")}
|
|
1971
|
+
</p>
|
|
1911
1972
|
</div>
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1973
|
+
</>
|
|
1974
|
+
) : !isOverlay ? (
|
|
1975
|
+
<>
|
|
1976
|
+
<div className="mx-4 my-2 border-t border-border/50" />
|
|
1977
|
+
{editingField === "meetingLink" ? (
|
|
1978
|
+
<div className="px-4 py-1.5">
|
|
1917
1979
|
<div className="flex items-center gap-2">
|
|
1918
|
-
<
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
}
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
<IconBrandZoom className="h-3.5 w-3.5" />
|
|
1940
|
-
{t("eventForm.zoom")}
|
|
1941
|
-
</Button>
|
|
1942
|
-
<Button
|
|
1943
|
-
type="button"
|
|
1944
|
-
variant="ghost"
|
|
1945
|
-
size="sm"
|
|
1946
|
-
className="h-8 flex-1 justify-center gap-1.5 px-2 text-xs text-muted-foreground"
|
|
1947
|
-
onClick={() => setEditingField("meetingLink")}
|
|
1948
|
-
>
|
|
1949
|
-
<IconPlus className="h-3.5 w-3.5" />
|
|
1950
|
-
{t("eventForm.pasteLink")}
|
|
1951
|
-
</Button>
|
|
1980
|
+
<IconVideo className="h-4 w-4 shrink-0 text-muted-foreground" />
|
|
1981
|
+
<input
|
|
1982
|
+
ref={meetingLinkRef}
|
|
1983
|
+
value={editMeetingLink}
|
|
1984
|
+
onChange={(e) => setEditMeetingLink(e.target.value)}
|
|
1985
|
+
onKeyDown={(e) => {
|
|
1986
|
+
if (e.key === "Enter") {
|
|
1987
|
+
e.preventDefault();
|
|
1988
|
+
handleSaveMeetingLink();
|
|
1989
|
+
}
|
|
1990
|
+
if (e.key === "Escape") {
|
|
1991
|
+
e.preventDefault();
|
|
1992
|
+
setEditMeetingLink("");
|
|
1993
|
+
setEditingField(null);
|
|
1994
|
+
}
|
|
1995
|
+
e.stopPropagation();
|
|
1996
|
+
}}
|
|
1997
|
+
onBlur={handleSaveMeetingLink}
|
|
1998
|
+
placeholder={t("eventForm.pasteMeetingLink")}
|
|
1999
|
+
className="flex-1 bg-transparent border-none outline-none text-sm text-foreground placeholder:text-muted-foreground/40 focus:ring-0"
|
|
2000
|
+
/>
|
|
1952
2001
|
</div>
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
2002
|
+
</div>
|
|
2003
|
+
) : (
|
|
2004
|
+
<div className="px-4 py-1.5">
|
|
2005
|
+
{pendingVideoProvider ? (
|
|
2006
|
+
<MeetingLinkSkeleton provider={pendingVideoProvider} />
|
|
2007
|
+
) : (
|
|
2008
|
+
<div className="flex items-center gap-2">
|
|
2009
|
+
<Button
|
|
2010
|
+
type="button"
|
|
2011
|
+
variant="outline"
|
|
2012
|
+
size="sm"
|
|
2013
|
+
className="h-8 flex-1 justify-center gap-1.5 px-2 text-xs"
|
|
2014
|
+
disabled={updateEvent.isPending}
|
|
2015
|
+
onClick={handleAddGoogleMeet}
|
|
2016
|
+
>
|
|
2017
|
+
<IconVideo className="h-3.5 w-3.5" />
|
|
2018
|
+
{t("eventForm.meet")}
|
|
2019
|
+
</Button>
|
|
2020
|
+
<Button
|
|
2021
|
+
type="button"
|
|
2022
|
+
variant="outline"
|
|
2023
|
+
size="sm"
|
|
2024
|
+
className="h-8 flex-1 justify-center gap-1.5 px-2 text-xs"
|
|
2025
|
+
disabled={
|
|
2026
|
+
updateEvent.isPending || connectZoom.isPending
|
|
2027
|
+
}
|
|
2028
|
+
onClick={handleAddZoom}
|
|
2029
|
+
>
|
|
2030
|
+
<IconBrandZoom className="h-3.5 w-3.5" />
|
|
2031
|
+
{t("eventForm.zoom")}
|
|
2032
|
+
</Button>
|
|
2033
|
+
<Button
|
|
2034
|
+
type="button"
|
|
2035
|
+
variant="ghost"
|
|
2036
|
+
size="sm"
|
|
2037
|
+
className="h-8 flex-1 justify-center gap-1.5 px-2 text-xs text-muted-foreground"
|
|
2038
|
+
onClick={() => setEditingField("meetingLink")}
|
|
2039
|
+
>
|
|
2040
|
+
<IconPlus className="h-3.5 w-3.5" />
|
|
2041
|
+
{t("eventForm.pasteLink")}
|
|
2042
|
+
</Button>
|
|
2043
|
+
</div>
|
|
2044
|
+
)}
|
|
2045
|
+
</div>
|
|
2046
|
+
)}
|
|
2047
|
+
</>
|
|
2048
|
+
) : null)}
|
|
1958
2049
|
|
|
1959
2050
|
{/* Attachments */}
|
|
1960
|
-
{!isOverlay && (
|
|
2051
|
+
{!isOverlay && !isWorkingLocation && (
|
|
1961
2052
|
<>
|
|
1962
2053
|
<div className="mx-4 my-2 border-t border-border/50" />
|
|
1963
2054
|
{editingField === "attachments" ? (
|
|
@@ -2051,85 +2142,104 @@ export function EventDetailPopover({
|
|
|
2051
2142
|
</>
|
|
2052
2143
|
)}
|
|
2053
2144
|
|
|
2054
|
-
{
|
|
2055
|
-
<div className="mx-4 my-2 border-t border-border/50" />
|
|
2056
|
-
{editingField === "location" ? (
|
|
2057
|
-
<div className="flex items-start gap-3 px-4 py-1.5">
|
|
2058
|
-
<IconMapPin className="mt-1.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
2059
|
-
<input
|
|
2060
|
-
ref={locationRef}
|
|
2061
|
-
value={editLocation}
|
|
2062
|
-
onChange={(e) => setEditLocation(e.target.value)}
|
|
2063
|
-
onKeyDown={(e) => {
|
|
2064
|
-
if (e.key === "Enter") {
|
|
2065
|
-
e.preventDefault();
|
|
2066
|
-
handleSaveLocation();
|
|
2067
|
-
}
|
|
2068
|
-
if (e.key === "Escape") {
|
|
2069
|
-
e.preventDefault();
|
|
2070
|
-
setEditLocation(event.location || "");
|
|
2071
|
-
setEditingField(null);
|
|
2072
|
-
}
|
|
2073
|
-
e.stopPropagation();
|
|
2074
|
-
}}
|
|
2075
|
-
onBlur={handleSaveLocation}
|
|
2076
|
-
placeholder={t("eventForm.addLocation")}
|
|
2077
|
-
className="flex-1 bg-transparent border-none outline-none text-sm text-foreground placeholder:text-muted-foreground/40 focus:ring-0"
|
|
2078
|
-
/>
|
|
2079
|
-
</div>
|
|
2080
|
-
) : event.location && !locationIsMeetingLink ? (
|
|
2081
|
-
<div
|
|
2082
|
-
className={`flex items-start gap-3 px-4 py-1.5 ${!isOverlay ? "cursor-pointer hover:bg-muted/50 rounded-md" : ""}`}
|
|
2083
|
-
onClick={() => {
|
|
2084
|
-
if (isOverlay) return;
|
|
2085
|
-
setEditingField("location");
|
|
2086
|
-
}}
|
|
2087
|
-
>
|
|
2088
|
-
<IconMapPin className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
2089
|
-
{locationIsUrl ? (
|
|
2090
|
-
<Tooltip>
|
|
2091
|
-
<TooltipTrigger asChild>
|
|
2092
|
-
<a
|
|
2093
|
-
href={event.location}
|
|
2094
|
-
target="_blank"
|
|
2095
|
-
rel="noopener noreferrer"
|
|
2096
|
-
className="text-sm text-primary hover:underline truncate block max-w-full"
|
|
2097
|
-
onClick={(e) => e.stopPropagation()}
|
|
2098
|
-
>
|
|
2099
|
-
{event.location}
|
|
2100
|
-
</a>
|
|
2101
|
-
</TooltipTrigger>
|
|
2102
|
-
<TooltipContent>{event.location}</TooltipContent>
|
|
2103
|
-
</Tooltip>
|
|
2104
|
-
) : (
|
|
2105
|
-
<span className="text-sm text-muted-foreground">
|
|
2106
|
-
{event.location}
|
|
2107
|
-
</span>
|
|
2108
|
-
)}
|
|
2109
|
-
</div>
|
|
2110
|
-
) : locationIsMeetingLink && meetingLink ? (
|
|
2145
|
+
{!isWorkingLocation && (
|
|
2111
2146
|
<>
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
{
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2147
|
+
{/* Location — always shown, editable */}
|
|
2148
|
+
<div className="mx-4 my-2 border-t border-border/50" />
|
|
2149
|
+
{editingField === "location" ? (
|
|
2150
|
+
<div className="flex items-start gap-3 px-4 py-1.5">
|
|
2151
|
+
<IconMapPin className="mt-1.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
2152
|
+
<input
|
|
2153
|
+
ref={locationRef}
|
|
2154
|
+
value={editLocation}
|
|
2155
|
+
onChange={(e) => setEditLocation(e.target.value)}
|
|
2156
|
+
onKeyDown={(e) => {
|
|
2157
|
+
if (e.key === "Enter") {
|
|
2158
|
+
e.preventDefault();
|
|
2159
|
+
handleSaveLocation();
|
|
2160
|
+
}
|
|
2161
|
+
if (e.key === "Escape") {
|
|
2162
|
+
e.preventDefault();
|
|
2163
|
+
setEditLocation(editableLocationValue);
|
|
2164
|
+
setEditingField(null);
|
|
2165
|
+
}
|
|
2166
|
+
e.stopPropagation();
|
|
2167
|
+
}}
|
|
2168
|
+
onBlur={handleSaveLocation}
|
|
2169
|
+
placeholder={t("eventForm.addLocation")}
|
|
2170
|
+
className="flex-1 bg-transparent border-none outline-none text-sm text-foreground placeholder:text-muted-foreground/40 focus:ring-0"
|
|
2171
|
+
/>
|
|
2126
2172
|
</div>
|
|
2127
|
-
|
|
2128
|
-
|
|
2173
|
+
) : event.location && !locationIsMeetingLink ? (
|
|
2174
|
+
<div
|
|
2175
|
+
className={`flex items-start gap-3 px-4 py-1.5 ${!isOverlay ? "cursor-pointer hover:bg-muted/50 rounded-md" : ""}`}
|
|
2176
|
+
onClick={() => {
|
|
2177
|
+
if (isOverlay) return;
|
|
2178
|
+
setEditingField("location");
|
|
2179
|
+
}}
|
|
2180
|
+
>
|
|
2181
|
+
<IconMapPin className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
2182
|
+
{locationIsUrl ? (
|
|
2183
|
+
<Tooltip>
|
|
2184
|
+
<TooltipTrigger asChild>
|
|
2185
|
+
<a
|
|
2186
|
+
href={event.location}
|
|
2187
|
+
target="_blank"
|
|
2188
|
+
rel="noopener noreferrer"
|
|
2189
|
+
className="text-sm text-primary hover:underline truncate block max-w-full"
|
|
2190
|
+
onClick={(e) => e.stopPropagation()}
|
|
2191
|
+
>
|
|
2192
|
+
{event.location}
|
|
2193
|
+
</a>
|
|
2194
|
+
</TooltipTrigger>
|
|
2195
|
+
<TooltipContent>{event.location}</TooltipContent>
|
|
2196
|
+
</Tooltip>
|
|
2197
|
+
) : (
|
|
2198
|
+
<span className="text-sm text-muted-foreground">
|
|
2199
|
+
{event.location}
|
|
2200
|
+
</span>
|
|
2201
|
+
)}
|
|
2202
|
+
</div>
|
|
2203
|
+
) : locationIsMeetingLink && meetingLink ? (
|
|
2204
|
+
<>
|
|
2205
|
+
<div className="flex items-start gap-3 px-4 py-1.5 rounded-md">
|
|
2206
|
+
<IconVideo className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
2207
|
+
<div className="min-w-0">
|
|
2208
|
+
<a
|
|
2209
|
+
href={meetingLink.url}
|
|
2210
|
+
target="_blank"
|
|
2211
|
+
rel="noopener noreferrer"
|
|
2212
|
+
className="block max-w-full truncate text-sm text-primary hover:underline"
|
|
2213
|
+
>
|
|
2214
|
+
{getMeetingLabel(meetingLink.type, t)}
|
|
2215
|
+
</a>
|
|
2216
|
+
<div className="text-xs text-muted-foreground">
|
|
2217
|
+
{t("eventForm.savedAsVideoLink")}
|
|
2218
|
+
</div>
|
|
2219
|
+
</div>
|
|
2220
|
+
</div>
|
|
2221
|
+
{!isOverlay && (
|
|
2222
|
+
<div
|
|
2223
|
+
className="flex items-center gap-3 px-4 py-1.5 cursor-pointer hover:bg-muted/50 rounded-md"
|
|
2224
|
+
onClick={() => {
|
|
2225
|
+
setEditLocation(editableLocationValue);
|
|
2226
|
+
setEditingField("location");
|
|
2227
|
+
}}
|
|
2228
|
+
>
|
|
2229
|
+
<IconMapPin className="h-4 w-4 shrink-0 text-muted-foreground/40" />
|
|
2230
|
+
<span className="text-sm text-muted-foreground/40">
|
|
2231
|
+
{t("eventForm.addLocation")}
|
|
2232
|
+
</span>
|
|
2233
|
+
</div>
|
|
2234
|
+
)}
|
|
2235
|
+
</>
|
|
2236
|
+
) : !isOverlay ? (
|
|
2129
2237
|
<div
|
|
2130
2238
|
className="flex items-center gap-3 px-4 py-1.5 cursor-pointer hover:bg-muted/50 rounded-md"
|
|
2131
2239
|
onClick={() => {
|
|
2132
|
-
setEditLocation(
|
|
2240
|
+
setEditLocation(
|
|
2241
|
+
locationIsMeetingLink ? "" : editableLocationValue,
|
|
2242
|
+
);
|
|
2133
2243
|
setEditingField("location");
|
|
2134
2244
|
}}
|
|
2135
2245
|
>
|
|
@@ -2138,27 +2248,12 @@ export function EventDetailPopover({
|
|
|
2138
2248
|
{t("eventForm.addLocation")}
|
|
2139
2249
|
</span>
|
|
2140
2250
|
</div>
|
|
2141
|
-
)}
|
|
2251
|
+
) : null}
|
|
2142
2252
|
</>
|
|
2143
|
-
)
|
|
2144
|
-
<div
|
|
2145
|
-
className="flex items-center gap-3 px-4 py-1.5 cursor-pointer hover:bg-muted/50 rounded-md"
|
|
2146
|
-
onClick={() => {
|
|
2147
|
-
setEditLocation(
|
|
2148
|
-
locationIsMeetingLink ? "" : event.location || "",
|
|
2149
|
-
);
|
|
2150
|
-
setEditingField("location");
|
|
2151
|
-
}}
|
|
2152
|
-
>
|
|
2153
|
-
<IconMapPin className="h-4 w-4 shrink-0 text-muted-foreground/40" />
|
|
2154
|
-
<span className="text-sm text-muted-foreground/40">
|
|
2155
|
-
{t("eventForm.addLocation")}
|
|
2156
|
-
</span>
|
|
2157
|
-
</div>
|
|
2158
|
-
) : null}
|
|
2253
|
+
)}
|
|
2159
2254
|
|
|
2160
2255
|
{/* Description — always shown for editable events; hidden for overlay events with no description */}
|
|
2161
|
-
{(!isOverlay || event.description) && (
|
|
2256
|
+
{!isWorkingLocation && (!isOverlay || event.description) && (
|
|
2162
2257
|
<>
|
|
2163
2258
|
<div className="mx-4 my-2 border-t border-border/50" />
|
|
2164
2259
|
<div className="px-4 py-1.5">
|
|
@@ -2210,180 +2305,182 @@ export function EventDetailPopover({
|
|
|
2210
2305
|
)}
|
|
2211
2306
|
|
|
2212
2307
|
{/* Reminders */}
|
|
2213
|
-
{!
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
<div className="
|
|
2218
|
-
<
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
<
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2308
|
+
{!isWorkingLocation &&
|
|
2309
|
+
(!isOverlay && editingField === "reminders" ? (
|
|
2310
|
+
<>
|
|
2311
|
+
<div className="mx-4 my-2 border-t border-border/50" />
|
|
2312
|
+
<div className="px-4 py-1.5">
|
|
2313
|
+
<div className="mb-2 flex items-center gap-3">
|
|
2314
|
+
<IconBell className="h-4 w-4 shrink-0 text-muted-foreground" />
|
|
2315
|
+
<span className="text-sm font-medium text-foreground">
|
|
2316
|
+
{t("eventForm.eventAlerts")}
|
|
2317
|
+
</span>
|
|
2318
|
+
</div>
|
|
2319
|
+
<ReminderControls
|
|
2320
|
+
idPrefix={`event-${event.id}`}
|
|
2321
|
+
mode={editReminderMode}
|
|
2322
|
+
reminders={editReminders}
|
|
2323
|
+
onModeChange={setEditReminderMode}
|
|
2324
|
+
onRemindersChange={setEditReminders}
|
|
2325
|
+
/>
|
|
2326
|
+
<div className="mt-2 flex justify-end gap-1.5">
|
|
2327
|
+
<Button
|
|
2328
|
+
variant="ghost"
|
|
2329
|
+
size="sm"
|
|
2330
|
+
className="h-6 text-xs"
|
|
2331
|
+
onClick={() => {
|
|
2332
|
+
const reminderState = remindersToDraftState(event);
|
|
2333
|
+
setEditReminderMode(reminderState.mode);
|
|
2334
|
+
setEditReminders(reminderState.reminders);
|
|
2335
|
+
setEditingField(null);
|
|
2336
|
+
}}
|
|
2337
|
+
>
|
|
2338
|
+
{t("eventForm.cancel")}
|
|
2339
|
+
</Button>
|
|
2340
|
+
<Button
|
|
2341
|
+
size="sm"
|
|
2342
|
+
className="h-6 text-xs"
|
|
2343
|
+
onClick={handleSaveReminders}
|
|
2344
|
+
>
|
|
2345
|
+
{t("eventForm.save")}
|
|
2346
|
+
</Button>
|
|
2347
|
+
</div>
|
|
2251
2348
|
</div>
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2349
|
+
</>
|
|
2350
|
+
) : event.reminders && event.reminders.length > 0 ? (
|
|
2351
|
+
<>
|
|
2352
|
+
<div className="mx-4 my-2 border-t border-border/50" />
|
|
2353
|
+
<div className="flex items-start gap-3 px-4 py-1.5">
|
|
2354
|
+
<IconBell className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
|
|
2355
|
+
<div className="space-y-0.5">
|
|
2356
|
+
{event.reminders.map((r, i) => (
|
|
2357
|
+
<div key={i} className="text-sm text-muted-foreground">
|
|
2358
|
+
{formatReminderText(r.minutes)}
|
|
2359
|
+
</div>
|
|
2360
|
+
))}
|
|
2361
|
+
</div>
|
|
2265
2362
|
</div>
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
) : null}
|
|
2363
|
+
</>
|
|
2364
|
+
) : null)}
|
|
2269
2365
|
|
|
2270
2366
|
{/* Availability, visibility, and alerts */}
|
|
2271
|
-
{!
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
<div className="
|
|
2276
|
-
<div className="
|
|
2277
|
-
<
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
<
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
<
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
<
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
<
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
<
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2367
|
+
{!isWorkingLocation &&
|
|
2368
|
+
(!isOverlay ? (
|
|
2369
|
+
<>
|
|
2370
|
+
<div className="mx-4 my-2 border-t border-border/50" />
|
|
2371
|
+
<div className="px-4 py-1.5">
|
|
2372
|
+
<div className="grid grid-cols-3 gap-2">
|
|
2373
|
+
<div className="space-y-1">
|
|
2374
|
+
<span className="text-[10px] text-muted-foreground">
|
|
2375
|
+
{t("eventForm.showAs")}
|
|
2376
|
+
</span>
|
|
2377
|
+
<Select
|
|
2378
|
+
value={availabilityValue}
|
|
2379
|
+
onValueChange={(value) =>
|
|
2380
|
+
handleAvailabilityChange(value as AvailabilityValue)
|
|
2381
|
+
}
|
|
2382
|
+
disabled={updateEvent.isPending}
|
|
2383
|
+
>
|
|
2384
|
+
<SelectTrigger className="h-8 text-xs">
|
|
2385
|
+
<SelectValue />
|
|
2386
|
+
</SelectTrigger>
|
|
2387
|
+
<SelectContent>
|
|
2388
|
+
<SelectItem value="opaque">
|
|
2389
|
+
{t("eventForm.busy")}
|
|
2390
|
+
</SelectItem>
|
|
2391
|
+
<SelectItem value="transparent">
|
|
2392
|
+
{t("eventForm.free")}
|
|
2393
|
+
</SelectItem>
|
|
2394
|
+
</SelectContent>
|
|
2395
|
+
</Select>
|
|
2396
|
+
</div>
|
|
2397
|
+
<div className="space-y-1">
|
|
2398
|
+
<span className="text-[10px] text-muted-foreground">
|
|
2399
|
+
{t("eventForm.visibility")}
|
|
2400
|
+
</span>
|
|
2401
|
+
<Select
|
|
2402
|
+
value={visibilityValue}
|
|
2403
|
+
onValueChange={(value) =>
|
|
2404
|
+
handleVisibilityChange(value as VisibilityValue)
|
|
2405
|
+
}
|
|
2406
|
+
disabled={updateEvent.isPending}
|
|
2407
|
+
>
|
|
2408
|
+
<SelectTrigger className="h-8 text-xs">
|
|
2409
|
+
<SelectValue />
|
|
2410
|
+
</SelectTrigger>
|
|
2411
|
+
<SelectContent>
|
|
2412
|
+
<SelectItem value="default">
|
|
2413
|
+
{t("eventForm.default")}
|
|
2414
|
+
</SelectItem>
|
|
2415
|
+
<SelectItem value="public">
|
|
2416
|
+
{t("eventForm.public")}
|
|
2417
|
+
</SelectItem>
|
|
2418
|
+
<SelectItem value="private">
|
|
2419
|
+
{t("eventForm.private")}
|
|
2420
|
+
</SelectItem>
|
|
2421
|
+
</SelectContent>
|
|
2422
|
+
</Select>
|
|
2423
|
+
</div>
|
|
2424
|
+
<div className="space-y-1">
|
|
2425
|
+
<span className="text-[10px] text-muted-foreground">
|
|
2426
|
+
{t("eventForm.alerts")}
|
|
2427
|
+
</span>
|
|
2428
|
+
<Select
|
|
2429
|
+
value={reminderValue}
|
|
2430
|
+
onValueChange={(value) =>
|
|
2431
|
+
handleReminderChange(value as ReminderValue)
|
|
2432
|
+
}
|
|
2433
|
+
disabled={updateEvent.isPending}
|
|
2434
|
+
>
|
|
2435
|
+
<SelectTrigger className="h-8 text-xs">
|
|
2436
|
+
<SelectValue />
|
|
2437
|
+
</SelectTrigger>
|
|
2438
|
+
<SelectContent>
|
|
2439
|
+
<SelectItem value="default">
|
|
2440
|
+
{t("eventForm.default")}
|
|
2441
|
+
</SelectItem>
|
|
2442
|
+
<SelectItem value="none">
|
|
2443
|
+
{t("eventForm.none")}
|
|
2444
|
+
</SelectItem>
|
|
2445
|
+
<SelectItem value="0">
|
|
2446
|
+
{t("eventForm.atStart")}
|
|
2447
|
+
</SelectItem>
|
|
2448
|
+
<SelectItem value="10">10 min</SelectItem>
|
|
2449
|
+
<SelectItem value="30">30 min</SelectItem>
|
|
2450
|
+
<SelectItem value="60">1 hour</SelectItem>
|
|
2451
|
+
<SelectItem value="1440">1 day</SelectItem>
|
|
2452
|
+
<SelectItem value="custom">
|
|
2453
|
+
{t("eventForm.customEllipsis")}
|
|
2454
|
+
</SelectItem>
|
|
2455
|
+
</SelectContent>
|
|
2456
|
+
</Select>
|
|
2457
|
+
</div>
|
|
2326
2458
|
</div>
|
|
2327
|
-
<div className="
|
|
2328
|
-
<
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
onValueChange={(value) =>
|
|
2334
|
-
handleReminderChange(value as ReminderValue)
|
|
2335
|
-
}
|
|
2336
|
-
disabled={updateEvent.isPending}
|
|
2337
|
-
>
|
|
2338
|
-
<SelectTrigger className="h-8 text-xs">
|
|
2339
|
-
<SelectValue />
|
|
2340
|
-
</SelectTrigger>
|
|
2341
|
-
<SelectContent>
|
|
2342
|
-
<SelectItem value="default">
|
|
2343
|
-
{t("eventForm.default")}
|
|
2344
|
-
</SelectItem>
|
|
2345
|
-
<SelectItem value="none">
|
|
2346
|
-
{t("eventForm.none")}
|
|
2347
|
-
</SelectItem>
|
|
2348
|
-
<SelectItem value="0">
|
|
2349
|
-
{t("eventForm.atStart")}
|
|
2350
|
-
</SelectItem>
|
|
2351
|
-
<SelectItem value="10">10 min</SelectItem>
|
|
2352
|
-
<SelectItem value="30">30 min</SelectItem>
|
|
2353
|
-
<SelectItem value="60">1 hour</SelectItem>
|
|
2354
|
-
<SelectItem value="1440">1 day</SelectItem>
|
|
2355
|
-
<SelectItem value="custom">
|
|
2356
|
-
{t("eventForm.customEllipsis")}
|
|
2357
|
-
</SelectItem>
|
|
2358
|
-
</SelectContent>
|
|
2359
|
-
</Select>
|
|
2459
|
+
<div className="mt-2 flex items-center gap-3">
|
|
2460
|
+
<IconPalette className="h-4 w-4 shrink-0 text-muted-foreground" />
|
|
2461
|
+
<EventColorSwatches
|
|
2462
|
+
value={event.colorId}
|
|
2463
|
+
onChange={handleColorChange}
|
|
2464
|
+
/>
|
|
2360
2465
|
</div>
|
|
2361
2466
|
</div>
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
/>
|
|
2467
|
+
</>
|
|
2468
|
+
) : event.status || event.visibility ? (
|
|
2469
|
+
<>
|
|
2470
|
+
<div className="mx-4 my-2 border-t border-border/50" />
|
|
2471
|
+
<div className="flex items-center gap-3 px-4 py-1.5 text-sm text-muted-foreground">
|
|
2472
|
+
<div className="h-4 w-4 shrink-0" />
|
|
2473
|
+
<span>
|
|
2474
|
+
{event.transparency === "transparent"
|
|
2475
|
+
? t("eventForm.free")
|
|
2476
|
+
: t("eventForm.busy")}
|
|
2477
|
+
{event.visibility && event.visibility !== "default"
|
|
2478
|
+
? ` · ${event.visibility} visibility`
|
|
2479
|
+
: ""}
|
|
2480
|
+
</span>
|
|
2368
2481
|
</div>
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
) : event.status || event.visibility ? (
|
|
2372
|
-
<>
|
|
2373
|
-
<div className="mx-4 my-2 border-t border-border/50" />
|
|
2374
|
-
<div className="flex items-center gap-3 px-4 py-1.5 text-sm text-muted-foreground">
|
|
2375
|
-
<div className="h-4 w-4 shrink-0" />
|
|
2376
|
-
<span>
|
|
2377
|
-
{event.transparency === "transparent"
|
|
2378
|
-
? t("eventForm.free")
|
|
2379
|
-
: t("eventForm.busy")}
|
|
2380
|
-
{event.visibility && event.visibility !== "default"
|
|
2381
|
-
? ` · ${event.visibility} visibility`
|
|
2382
|
-
: ""}
|
|
2383
|
-
</span>
|
|
2384
|
-
</div>
|
|
2385
|
-
</>
|
|
2386
|
-
) : null}
|
|
2482
|
+
</>
|
|
2483
|
+
) : null)}
|
|
2387
2484
|
|
|
2388
2485
|
{/* Overlay person badge */}
|
|
2389
2486
|
{event.overlayEmail && (
|