@algenium/blocks 1.19.0 → 1.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import { Drawer as Drawer$1 } from 'vaul';
14
14
  import * as PopoverPrimitive from '@radix-ui/react-popover';
15
15
  import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
16
16
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
17
- import { startOfMonth, endOfMonth, eachDayOfInterval, endOfWeek, startOfWeek, format, isSameDay, subMonths, addMonths } from 'date-fns';
17
+ import { startOfMonth, endOfMonth, eachDayOfInterval, endOfWeek, startOfWeek, format, isSameDay, startOfDay, subMonths, addMonths, addDays, addHours, setMinutes, setHours } from 'date-fns';
18
18
  import { DayPicker } from 'react-day-picker';
19
19
  import { Command as Command$1 } from 'cmdk';
20
20
  import valid from 'card-validator';
@@ -6522,6 +6522,258 @@ function CalendarView({
6522
6522
  ] })
6523
6523
  ] });
6524
6524
  }
6525
+ var TIME_SLOTS = Array.from({ length: 24 * 4 }, (_, i) => {
6526
+ const hours = Math.floor(i / 4);
6527
+ const minutes = i % 4 * 15;
6528
+ return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
6529
+ });
6530
+ function formatTimeDisplay(value) {
6531
+ const parsed = parseTimeInput(value);
6532
+ if (!parsed) return value;
6533
+ const [h, m] = parsed.split(":").map(Number);
6534
+ const period = h >= 12 ? "pm" : "am";
6535
+ const hour12 = h % 12 === 0 ? 12 : h % 12;
6536
+ return `${hour12}:${String(m).padStart(2, "0")}${period}`;
6537
+ }
6538
+ function parseTimeInput(raw) {
6539
+ const cleaned = raw.trim().toLowerCase().replace(/\s+/g, "");
6540
+ if (!cleaned) return null;
6541
+ const ampmMatch = cleaned.match(/^(\d{1,2})(?::(\d{2}))?(am|pm)$/);
6542
+ if (ampmMatch) {
6543
+ let hours = Number(ampmMatch[1]);
6544
+ const minutes = Number(ampmMatch[2] ?? "0");
6545
+ const period = ampmMatch[3];
6546
+ if (hours < 1 || hours > 12 || minutes > 59) return null;
6547
+ if (period === "am") {
6548
+ if (hours === 12) hours = 0;
6549
+ } else if (hours !== 12) {
6550
+ hours += 12;
6551
+ }
6552
+ return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
6553
+ }
6554
+ const colonMatch = cleaned.match(/^(\d{1,2}):(\d{2})$/);
6555
+ if (colonMatch) {
6556
+ const hours = Number(colonMatch[1]);
6557
+ const minutes = Number(colonMatch[2]);
6558
+ if (hours > 23 || minutes > 59) return null;
6559
+ return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
6560
+ }
6561
+ const compactMatch = cleaned.match(/^(\d{1,2})(\d{2})$/);
6562
+ if (compactMatch) {
6563
+ const hours = Number(compactMatch[1]);
6564
+ const minutes = Number(compactMatch[2]);
6565
+ if (hours > 23 || minutes > 59) return null;
6566
+ return `${String(hours).padStart(2, "0")}:${String(minutes).padStart(2, "0")}`;
6567
+ }
6568
+ const hourOnly = cleaned.match(/^(\d{1,2})$/);
6569
+ if (hourOnly) {
6570
+ const hours = Number(hourOnly[1]);
6571
+ if (hours > 23) return null;
6572
+ return `${String(hours).padStart(2, "0")}:00`;
6573
+ }
6574
+ return null;
6575
+ }
6576
+ function minutesFromMidnight(value) {
6577
+ const [h, m] = value.split(":").map(Number);
6578
+ return h * 60 + m;
6579
+ }
6580
+ function formatDurationHintClean(start, end) {
6581
+ let mins = minutesFromMidnight(end) - minutesFromMidnight(start);
6582
+ if (mins <= 0) mins += 24 * 60;
6583
+ if (mins < 60) return `${mins} mins`;
6584
+ const hours = Math.floor(mins / 60);
6585
+ const rem = mins % 60;
6586
+ if (rem === 0) return hours === 1 ? "1 hr" : `${hours} hrs`;
6587
+ if (rem === 30) return `${hours}.5 hrs`;
6588
+ return `${hours} hr ${rem} mins`;
6589
+ }
6590
+ function TimeCombobox({
6591
+ value,
6592
+ onChange,
6593
+ disabled,
6594
+ durationFrom,
6595
+ className,
6596
+ placeholder = "Time",
6597
+ "aria-label": ariaLabel
6598
+ }) {
6599
+ const [open, setOpen] = useState(false);
6600
+ const [draft, setDraft] = useState(formatTimeDisplay(value));
6601
+ const listRef = useRef(null);
6602
+ const selectedRef = useRef(null);
6603
+ useEffect(() => {
6604
+ if (!open) {
6605
+ setDraft(formatTimeDisplay(value));
6606
+ }
6607
+ }, [value, open]);
6608
+ useEffect(() => {
6609
+ if (open) {
6610
+ requestAnimationFrame(() => {
6611
+ selectedRef.current?.scrollIntoView({ block: "center" });
6612
+ });
6613
+ }
6614
+ }, [open]);
6615
+ const filtered = useMemo(() => {
6616
+ const q = draft.trim().toLowerCase().replace(/\s+/g, "");
6617
+ if (!q || parseTimeInput(draft) === value) return TIME_SLOTS;
6618
+ return TIME_SLOTS.filter((slot) => {
6619
+ const display = formatTimeDisplay(slot).toLowerCase();
6620
+ return display.includes(q) || slot.includes(q);
6621
+ });
6622
+ }, [draft, value]);
6623
+ const commitDraft = () => {
6624
+ const parsed = parseTimeInput(draft);
6625
+ if (parsed) {
6626
+ onChange(parsed);
6627
+ setDraft(formatTimeDisplay(parsed));
6628
+ } else {
6629
+ setDraft(formatTimeDisplay(value));
6630
+ }
6631
+ };
6632
+ return /* @__PURE__ */ jsxs(Popover, { open, onOpenChange: setOpen, children: [
6633
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
6634
+ "button",
6635
+ {
6636
+ type: "button",
6637
+ disabled,
6638
+ "aria-label": ariaLabel,
6639
+ className: cn(
6640
+ "inline-flex h-9 min-w-[5.5rem] items-center justify-center rounded-md border border-input bg-background px-2 text-sm tabular-nums ring-offset-background transition-colors",
6641
+ "hover:bg-accent hover:text-accent-foreground",
6642
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
6643
+ "disabled:cursor-not-allowed disabled:opacity-50",
6644
+ className
6645
+ ),
6646
+ onClick: () => setOpen(true),
6647
+ children: formatTimeDisplay(value) || placeholder
6648
+ }
6649
+ ) }),
6650
+ /* @__PURE__ */ jsxs(
6651
+ PopoverContent,
6652
+ {
6653
+ className: "z-[100] w-40 p-0",
6654
+ align: "start",
6655
+ onOpenAutoFocus: (e) => {
6656
+ e.preventDefault();
6657
+ const input = e.currentTarget?.querySelector(
6658
+ "input"
6659
+ );
6660
+ input?.focus();
6661
+ },
6662
+ children: [
6663
+ /* @__PURE__ */ jsx("div", { className: "border-b p-1.5", children: /* @__PURE__ */ jsx(
6664
+ "input",
6665
+ {
6666
+ value: draft,
6667
+ onChange: (e) => setDraft(e.target.value),
6668
+ onKeyDown: (e) => {
6669
+ if (e.key === "Enter") {
6670
+ e.preventDefault();
6671
+ commitDraft();
6672
+ setOpen(false);
6673
+ } else if (e.key === "Escape") {
6674
+ setDraft(formatTimeDisplay(value));
6675
+ setOpen(false);
6676
+ }
6677
+ },
6678
+ onBlur: commitDraft,
6679
+ placeholder,
6680
+ className: "w-full rounded-md border border-input bg-background px-2 py-1.5 text-sm tabular-nums focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
6681
+ "aria-label": ariaLabel
6682
+ }
6683
+ ) }),
6684
+ /* @__PURE__ */ jsxs("div", { ref: listRef, className: "max-h-56 overflow-y-auto py-1", children: [
6685
+ filtered.map((slot) => {
6686
+ const selected = slot === value;
6687
+ const label = formatTimeDisplay(slot);
6688
+ const hint = durationFrom != null ? formatDurationHintClean(durationFrom, slot) : null;
6689
+ return /* @__PURE__ */ jsxs(
6690
+ "button",
6691
+ {
6692
+ ref: selected ? selectedRef : void 0,
6693
+ type: "button",
6694
+ className: cn(
6695
+ "flex w-full items-center justify-between px-3 py-1.5 text-left text-sm tabular-nums hover:bg-accent",
6696
+ selected && "bg-accent font-medium"
6697
+ ),
6698
+ onMouseDown: (e) => {
6699
+ e.preventDefault();
6700
+ },
6701
+ onClick: () => {
6702
+ onChange(slot);
6703
+ setDraft(formatTimeDisplay(slot));
6704
+ setOpen(false);
6705
+ },
6706
+ children: [
6707
+ /* @__PURE__ */ jsx("span", { children: label }),
6708
+ hint && /* @__PURE__ */ jsx("span", { className: "ml-2 text-xs text-muted-foreground", children: hint })
6709
+ ]
6710
+ },
6711
+ slot
6712
+ );
6713
+ }),
6714
+ filtered.length === 0 && /* @__PURE__ */ jsx("p", { className: "px-3 py-2 text-xs text-muted-foreground", children: "No matching times" })
6715
+ ] })
6716
+ ]
6717
+ }
6718
+ )
6719
+ ] });
6720
+ }
6721
+ function toTimeValue(date) {
6722
+ return `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`;
6723
+ }
6724
+ function dateKey(date) {
6725
+ return format(date, "yyyy-MM-dd");
6726
+ }
6727
+ function combineDateAndTime(date, time) {
6728
+ const [h, m] = time.split(":").map(Number);
6729
+ return setMinutes(setHours(startOfDay(date), h), m);
6730
+ }
6731
+ function defaultTimes(from) {
6732
+ const base = /* @__PURE__ */ new Date();
6733
+ const nextHour = addHours(setMinutes(setHours(base, base.getHours()), 0), 1);
6734
+ const end = addHours(nextHour, 1);
6735
+ return {
6736
+ date: startOfDay(nextHour),
6737
+ startTime: toTimeValue(nextHour),
6738
+ endTime: toTimeValue(end)
6739
+ };
6740
+ }
6741
+ function splitEventTimes(event) {
6742
+ if (event?.dtstart) {
6743
+ const start = new Date(event.dtstart);
6744
+ const end = event.dtend ? new Date(event.dtend) : addHours(start, 1);
6745
+ return {
6746
+ date: startOfDay(start),
6747
+ startTime: toTimeValue(start),
6748
+ endTime: toTimeValue(end),
6749
+ allDay: event.allDay ?? false
6750
+ };
6751
+ }
6752
+ const defaults = defaultTimes();
6753
+ return { ...defaults, allDay: event?.allDay ?? false };
6754
+ }
6755
+ var dayPickerClassNames = {
6756
+ months: "flex flex-col",
6757
+ month: "space-y-2",
6758
+ month_caption: "flex justify-center relative items-center pt-1",
6759
+ caption_label: "text-sm font-medium",
6760
+ nav: "space-x-1 flex items-center",
6761
+ button_previous: "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 absolute left-1 top-1 inline-flex items-center justify-center rounded-md hover:bg-accent",
6762
+ button_next: "h-7 w-7 bg-transparent p-0 opacity-50 hover:opacity-100 absolute right-1 top-1 inline-flex items-center justify-center rounded-md hover:bg-accent",
6763
+ month_grid: "w-full border-collapse space-y-1",
6764
+ weekdays: "flex",
6765
+ weekday: "text-muted-foreground rounded-md w-8 font-normal text-[0.8rem]",
6766
+ week: "flex w-full mt-1",
6767
+ day: "relative p-0 text-center text-sm focus-within:relative focus-within:z-20",
6768
+ day_button: cn(
6769
+ "h-8 w-8 p-0 font-normal",
6770
+ "hover:bg-accent hover:text-accent-foreground rounded-md"
6771
+ ),
6772
+ selected: "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground rounded-md",
6773
+ today: "bg-accent text-accent-foreground rounded-md",
6774
+ outside: "text-muted-foreground opacity-50",
6775
+ disabled: "text-muted-foreground opacity-50"
6776
+ };
6525
6777
  function EventDialog({
6526
6778
  open,
6527
6779
  onOpenChange,
@@ -6541,13 +6793,11 @@ function EventDialog({
6541
6793
  const [summary, setSummary] = useState(event?.summary ?? "");
6542
6794
  const [description, setDescription] = useState(event?.description ?? "");
6543
6795
  const [location, setLocation] = useState(event?.location ?? "");
6544
- const [dtstart, setDtstart] = useState(
6545
- event?.dtstart ? new Date(event.dtstart).toISOString().slice(0, 16) : ""
6546
- );
6547
- const [dtend, setDtend] = useState(
6548
- event?.dtend ? new Date(event.dtend).toISOString().slice(0, 16) : ""
6549
- );
6550
- const [allDay, setAllDay] = useState(event?.allDay ?? false);
6796
+ const initialTimes = splitEventTimes(event);
6797
+ const [date, setDate] = useState(initialTimes.date);
6798
+ const [startTime, setStartTime] = useState(initialTimes.startTime);
6799
+ const [endTime, setEndTime] = useState(initialTimes.endTime);
6800
+ const [allDay, setAllDay] = useState(initialTimes.allDay);
6551
6801
  const [categoryId, setCategoryId] = useState(event?.categoryId ?? "");
6552
6802
  const [referenceType, setReferenceType] = useState(
6553
6803
  event?.referenceType ?? ""
@@ -6558,20 +6808,47 @@ function EventDialog({
6558
6808
  const [linkOptions, setLinkOptions] = useState([]);
6559
6809
  const [saving, setSaving] = useState(false);
6560
6810
  const [completing, setCompleting] = useState(false);
6811
+ const [datePickerOpen, setDatePickerOpen] = useState(false);
6812
+ const [confirmDiscard, setConfirmDiscard] = useState(false);
6813
+ const [discardIntent, setDiscardIntent] = useState(
6814
+ "close"
6815
+ );
6816
+ const snapshotRef = useRef(null);
6817
+ const allowCloseRef = useRef(false);
6561
6818
  const linkTimer = useRef(null);
6819
+ const requestDiscard = (intent) => {
6820
+ setDiscardIntent(intent);
6821
+ setConfirmDiscard(true);
6822
+ };
6823
+ const revertToSnapshot = () => {
6824
+ const snap = snapshotRef.current;
6825
+ if (!snap) return;
6826
+ setSummary(snap.summary);
6827
+ setDescription(snap.description);
6828
+ setLocation(snap.location);
6829
+ setDate(startOfDay(/* @__PURE__ */ new Date(`${snap.dateKey}T00:00:00`)));
6830
+ setStartTime(snap.startTime);
6831
+ setEndTime(snap.endTime);
6832
+ setAllDay(snap.allDay);
6833
+ setCategoryId(snap.categoryId);
6834
+ setReferenceType(snap.referenceType);
6835
+ setReferenceId(snap.referenceId);
6836
+ };
6562
6837
  useEffect(() => {
6563
- if (!open) return;
6838
+ if (!open) {
6839
+ setConfirmDiscard(false);
6840
+ allowCloseRef.current = false;
6841
+ return;
6842
+ }
6564
6843
  setMode(event?.id ? "preview" : "edit");
6565
6844
  setSummary(event?.summary ?? "");
6566
6845
  setDescription(event?.description ?? "");
6567
6846
  setLocation(event?.location ?? "");
6568
- setDtstart(
6569
- event?.dtstart ? new Date(event.dtstart).toISOString().slice(0, 16) : ""
6570
- );
6571
- setDtend(
6572
- event?.dtend ? new Date(event.dtend).toISOString().slice(0, 16) : ""
6573
- );
6574
- setAllDay(event?.allDay ?? false);
6847
+ const times = splitEventTimes(event);
6848
+ setDate(times.date);
6849
+ setStartTime(times.startTime);
6850
+ setEndTime(times.endTime);
6851
+ setAllDay(times.allDay);
6575
6852
  setCategoryId(event?.categoryId ?? "");
6576
6853
  setReferenceType(event?.referenceType ?? "");
6577
6854
  setReferenceId(event?.referenceId ?? "");
@@ -6580,6 +6857,21 @@ function EventDialog({
6580
6857
  );
6581
6858
  setLinkQuery("");
6582
6859
  setLinkOptions([]);
6860
+ setConfirmDiscard(false);
6861
+ allowCloseRef.current = false;
6862
+ const snap = {
6863
+ summary: (event?.summary ?? "").trim(),
6864
+ description: (event?.description ?? "").trim(),
6865
+ location: (event?.location ?? "").trim(),
6866
+ dateKey: dateKey(times.date),
6867
+ startTime: times.startTime,
6868
+ endTime: times.endTime,
6869
+ allDay: times.allDay,
6870
+ categoryId: event?.categoryId ?? "",
6871
+ referenceType: event?.referenceType ?? "",
6872
+ referenceId: event?.referenceId ?? ""
6873
+ };
6874
+ snapshotRef.current = snap;
6583
6875
  }, [open, event]);
6584
6876
  useEffect(() => {
6585
6877
  if (!linkSearch || !linkQuery.trim()) {
@@ -6599,25 +6891,69 @@ function EventDialog({
6599
6891
  if (linkTimer.current) clearTimeout(linkTimer.current);
6600
6892
  };
6601
6893
  }, [linkQuery, linkSearch]);
6894
+ const isDirty = useMemo(() => {
6895
+ const snap = snapshotRef.current;
6896
+ if (!snap) return false;
6897
+ return summary.trim() !== snap.summary || description.trim() !== snap.description || location.trim() !== snap.location || dateKey(date) !== snap.dateKey || startTime !== snap.startTime || endTime !== snap.endTime || allDay !== snap.allDay || categoryId !== snap.categoryId || referenceType !== snap.referenceType || referenceId !== snap.referenceId;
6898
+ }, [
6899
+ summary,
6900
+ description,
6901
+ location,
6902
+ date,
6903
+ startTime,
6904
+ endTime,
6905
+ allDay,
6906
+ categoryId,
6907
+ referenceType,
6908
+ referenceId
6909
+ ]);
6602
6910
  const selectedCategory = categories.find((c) => c.id === categoryId) ?? event?.category ?? null;
6911
+ const handleOpenChange = (nextOpen) => {
6912
+ if (nextOpen) {
6913
+ onOpenChange(true);
6914
+ return;
6915
+ }
6916
+ if (allowCloseRef.current || saving) {
6917
+ allowCloseRef.current = false;
6918
+ onOpenChange(false);
6919
+ return;
6920
+ }
6921
+ if (mode === "edit" && isDirty) {
6922
+ requestDiscard("close");
6923
+ return;
6924
+ }
6925
+ onOpenChange(false);
6926
+ };
6927
+ const forceClose = () => {
6928
+ allowCloseRef.current = true;
6929
+ setConfirmDiscard(false);
6930
+ onOpenChange(false);
6931
+ };
6603
6932
  const handleSave = async () => {
6604
- if (!summary.trim() || !dtstart || !dtend) return;
6933
+ if (!summary.trim() || !date || !startTime || !endTime) return;
6605
6934
  setSaving(true);
6606
6935
  try {
6936
+ const start = allDay ? startOfDay(date) : combineDateAndTime(date, startTime);
6937
+ let end = allDay ? startOfDay(addDays(date, 1)) : combineDateAndTime(date, endTime);
6938
+ if (!allDay && end.getTime() <= start.getTime()) {
6939
+ end = addDays(end, 1);
6940
+ }
6607
6941
  await onSave({
6608
6942
  ...event?.id ? { id: event.id } : {},
6609
6943
  summary: summary.trim(),
6610
6944
  description: description.trim() || null,
6611
6945
  location: location.trim() || null,
6612
- dtstart: new Date(dtstart).toISOString(),
6613
- dtend: new Date(dtend).toISOString(),
6946
+ dtstart: start.toISOString(),
6947
+ dtend: end.toISOString(),
6614
6948
  allDay,
6615
6949
  categoryId: categoryId || null,
6616
6950
  category: selectedCategory,
6617
6951
  referenceType: referenceType || null,
6618
6952
  referenceId: referenceId || null
6619
6953
  });
6620
- onOpenChange(false);
6954
+ forceClose();
6955
+ } catch (err) {
6956
+ console.error("[EventDialog] Failed to save event:", err);
6621
6957
  } finally {
6622
6958
  setSaving(false);
6623
6959
  }
@@ -6627,275 +6963,379 @@ function EventDialog({
6627
6963
  setCompleting(true);
6628
6964
  try {
6629
6965
  await onComplete();
6630
- onOpenChange(false);
6966
+ forceClose();
6631
6967
  } finally {
6632
6968
  setCompleting(false);
6633
6969
  }
6634
6970
  };
6971
+ const handleStartTimeChange = (value) => {
6972
+ setStartTime(value);
6973
+ const [sh, sm] = value.split(":").map(Number);
6974
+ const [eh, em] = endTime.split(":").map(Number);
6975
+ if (eh * 60 + em <= sh * 60 + sm) {
6976
+ const next = addHours(combineDateAndTime(date, value), 1);
6977
+ setEndTime(toTimeValue(next));
6978
+ }
6979
+ };
6635
6980
  const CategoryIcon = getCategoryIcon(selectedCategory?.alias);
6636
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-[520px]", children: [
6637
- /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: mode === "preview" ? labels.previewTitle ?? "Event details" : isExisting ? labels.editTitle ?? "Edit event" : labels.createTitle ?? "New event" }) }),
6638
- mode === "preview" ? /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-2", children: [
6639
- /* @__PURE__ */ jsxs("div", { children: [
6640
- /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: event?.summary }),
6641
- isCompleted && /* @__PURE__ */ jsxs("p", { className: "mt-1 inline-flex items-center gap-1 text-sm text-emerald-600", children: [
6642
- /* @__PURE__ */ jsx(CheckCircle2, { className: "h-3.5 w-3.5" }),
6643
- labels.completed ?? "Completed"
6644
- ] })
6645
- ] }),
6646
- event?.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground whitespace-pre-wrap", children: event.description }),
6647
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2 text-sm text-muted-foreground", children: [
6648
- /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
6649
- /* @__PURE__ */ jsx(Clock, { className: "h-3.5 w-3.5" }),
6650
- event?.dtstart ? new Date(event.dtstart).toLocaleString() : "\u2014",
6651
- event?.dtend ? ` \u2013 ${new Date(event.dtend).toLocaleString()}` : ""
6981
+ const dateLabel = format(date, "EEEE, d MMMM");
6982
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
6983
+ /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: handleOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-[520px]", children: [
6984
+ /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: mode === "preview" ? labels.previewTitle ?? "Event details" : isExisting ? labels.editTitle ?? "Edit event" : labels.createTitle ?? "New event" }) }),
6985
+ mode === "preview" ? /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-2", children: [
6986
+ /* @__PURE__ */ jsxs("div", { children: [
6987
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: event?.summary }),
6988
+ isCompleted && /* @__PURE__ */ jsxs("p", { className: "mt-1 inline-flex items-center gap-1 text-sm text-emerald-600", children: [
6989
+ /* @__PURE__ */ jsx(CheckCircle2, { className: "h-3.5 w-3.5" }),
6990
+ labels.completed ?? "Completed"
6991
+ ] })
6652
6992
  ] }),
6653
- event?.location && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
6654
- /* @__PURE__ */ jsx(MapPin, { className: "h-3.5 w-3.5" }),
6655
- event.location
6656
- ] })
6657
- ] }),
6658
- selectedCategory && /* @__PURE__ */ jsxs(
6659
- "span",
6660
- {
6661
- className: "inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium",
6662
- style: {
6663
- backgroundColor: selectedCategory.bgColor,
6664
- color: selectedCategory.iconColor
6665
- },
6666
- children: [
6667
- /* @__PURE__ */ jsx(CategoryIcon, { className: "h-3.5 w-3.5" }),
6668
- selectedCategory.name
6669
- ]
6670
- }
6671
- ),
6672
- (event?.referenceId || linkLabel) && /* @__PURE__ */ jsxs("p", { className: "inline-flex items-center gap-1.5 text-sm", children: [
6673
- /* @__PURE__ */ jsx(Link2, { className: "h-3.5 w-3.5 text-muted-foreground" }),
6674
- /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
6675
- linkSearch?.label ?? labels.link ?? "Linked case",
6676
- ":"
6993
+ event?.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground whitespace-pre-wrap", children: event.description }),
6994
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2 text-sm text-muted-foreground", children: [
6995
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
6996
+ /* @__PURE__ */ jsx(Clock, { className: "h-3.5 w-3.5" }),
6997
+ event?.dtstart ? new Date(event.dtstart).toLocaleString() : "\u2014",
6998
+ event?.dtend ? ` \u2013 ${new Date(event.dtend).toLocaleString()}` : ""
6999
+ ] }),
7000
+ event?.location && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
7001
+ /* @__PURE__ */ jsx(MapPin, { className: "h-3.5 w-3.5" }),
7002
+ event.location
7003
+ ] })
6677
7004
  ] }),
6678
- linkLabel || event?.referenceId
6679
- ] }),
6680
- /* @__PURE__ */ jsxs(DialogFooter, { className: "gap-2", children: [
6681
- !isCompleted && onComplete && /* @__PURE__ */ jsxs(
6682
- Button,
7005
+ selectedCategory && /* @__PURE__ */ jsxs(
7006
+ "span",
6683
7007
  {
6684
- variant: "secondary",
6685
- size: "sm",
6686
- onClick: handleComplete,
6687
- disabled: completing,
7008
+ className: "inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium",
7009
+ style: {
7010
+ backgroundColor: selectedCategory.bgColor,
7011
+ color: selectedCategory.iconColor
7012
+ },
6688
7013
  children: [
6689
- completing && /* @__PURE__ */ jsx(Loader2, { className: "mr-1 h-3.5 w-3.5 animate-spin" }),
6690
- labels.markComplete ?? "Mark as completed"
7014
+ /* @__PURE__ */ jsx(CategoryIcon, { className: "h-3.5 w-3.5" }),
7015
+ selectedCategory.name
6691
7016
  ]
6692
7017
  }
6693
7018
  ),
6694
- /* @__PURE__ */ jsx("div", { className: "flex-1" }),
6695
- isExisting && onDelete && !isCompleted && /* @__PURE__ */ jsx(Button, { variant: "destructive", size: "sm", onClick: onDelete, children: labels.delete ?? "Delete" }),
6696
- /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", children: labels.cancel ?? "Close" }) }),
6697
- !isCompleted && /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => setMode("edit"), children: [
6698
- /* @__PURE__ */ jsx(Pencil, { className: "mr-1 h-3.5 w-3.5" }),
6699
- labels.edit ?? "Edit"
6700
- ] })
6701
- ] })
6702
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
6703
- /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-2", children: [
6704
- /* @__PURE__ */ jsxs("div", { children: [
6705
- /* @__PURE__ */ jsx("label", { className: "mb-1 block text-sm font-medium", children: labels.summary ?? "Summary" }),
6706
- /* @__PURE__ */ jsx(
6707
- "input",
7019
+ (event?.referenceId || linkLabel) && /* @__PURE__ */ jsxs("p", { className: "inline-flex items-center gap-1.5 text-sm", children: [
7020
+ /* @__PURE__ */ jsx(Link2, { className: "h-3.5 w-3.5 text-muted-foreground" }),
7021
+ /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
7022
+ linkSearch?.label ?? labels.link ?? "Linked case",
7023
+ ":"
7024
+ ] }),
7025
+ linkLabel || event?.referenceId
7026
+ ] }),
7027
+ /* @__PURE__ */ jsxs(DialogFooter, { className: "gap-2", children: [
7028
+ !isCompleted && onComplete && /* @__PURE__ */ jsxs(
7029
+ Button,
6708
7030
  {
6709
- value: summary,
6710
- onChange: (e) => setSummary(e.target.value),
6711
- className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
6712
- placeholder: "Event name",
6713
- disabled: isCompleted
7031
+ variant: "secondary",
7032
+ size: "sm",
7033
+ onClick: handleComplete,
7034
+ disabled: completing,
7035
+ children: [
7036
+ completing && /* @__PURE__ */ jsx(Loader2, { className: "mr-1 h-3.5 w-3.5 animate-spin" }),
7037
+ labels.markComplete ?? "Mark as completed"
7038
+ ]
6714
7039
  }
6715
- )
6716
- ] }),
6717
- categories.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
6718
- /* @__PURE__ */ jsx("label", { className: "mb-1.5 block text-sm font-medium", children: labels.category ?? "Category" }),
6719
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: categories.map((cat) => {
6720
- const Icon = getCategoryIcon(cat.alias);
6721
- const selected = categoryId === cat.id;
6722
- return /* @__PURE__ */ jsxs(
6723
- "button",
7040
+ ),
7041
+ /* @__PURE__ */ jsx("div", { className: "flex-1" }),
7042
+ isExisting && onDelete && !isCompleted && /* @__PURE__ */ jsx(Button, { variant: "destructive", size: "sm", onClick: onDelete, children: labels.delete ?? "Delete" }),
7043
+ /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", children: labels.cancel ?? "Close" }) }),
7044
+ !isCompleted && /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => setMode("edit"), children: [
7045
+ /* @__PURE__ */ jsx(Pencil, { className: "mr-1 h-3.5 w-3.5" }),
7046
+ labels.edit ?? "Edit"
7047
+ ] })
7048
+ ] })
7049
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
7050
+ /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-2", children: [
7051
+ /* @__PURE__ */ jsxs("div", { children: [
7052
+ /* @__PURE__ */ jsx("label", { className: "mb-1 block text-sm font-medium", children: labels.summary ?? "Summary" }),
7053
+ /* @__PURE__ */ jsx(
7054
+ "input",
6724
7055
  {
6725
- type: "button",
6726
- disabled: isCompleted,
6727
- onClick: () => setCategoryId(selected ? "" : cat.id),
6728
- className: cn(
6729
- "inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-medium transition-all",
6730
- selected ? "ring-2 ring-offset-1 ring-primary" : "opacity-80 hover:opacity-100"
6731
- ),
6732
- style: {
6733
- backgroundColor: cat.bgColor,
6734
- color: cat.iconColor,
6735
- borderColor: cat.iconColor
7056
+ value: summary,
7057
+ onChange: (e) => setSummary(e.target.value),
7058
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
7059
+ placeholder: "Event name",
7060
+ disabled: isCompleted
7061
+ }
7062
+ )
7063
+ ] }),
7064
+ categories.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
7065
+ /* @__PURE__ */ jsx("label", { className: "mb-1.5 block text-sm font-medium", children: labels.category ?? "Category" }),
7066
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: categories.map((cat) => {
7067
+ const Icon = getCategoryIcon(cat.alias);
7068
+ const selected = categoryId === cat.id;
7069
+ return /* @__PURE__ */ jsxs(
7070
+ "button",
7071
+ {
7072
+ type: "button",
7073
+ disabled: isCompleted,
7074
+ onClick: () => setCategoryId(selected ? "" : cat.id),
7075
+ className: cn(
7076
+ "inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-medium transition-all",
7077
+ selected ? "ring-2 ring-offset-1 ring-primary" : "opacity-80 hover:opacity-100"
7078
+ ),
7079
+ style: {
7080
+ backgroundColor: cat.bgColor,
7081
+ color: cat.iconColor,
7082
+ borderColor: cat.iconColor
7083
+ },
7084
+ children: [
7085
+ /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
7086
+ cat.name
7087
+ ]
6736
7088
  },
6737
- children: [
6738
- /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
6739
- cat.name
6740
- ]
6741
- },
6742
- cat.id
6743
- );
6744
- }) })
6745
- ] }),
6746
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
7089
+ cat.id
7090
+ );
7091
+ }) })
7092
+ ] }),
6747
7093
  /* @__PURE__ */ jsxs("div", { children: [
6748
- /* @__PURE__ */ jsxs("label", { className: "mb-1 block text-sm font-medium", children: [
7094
+ /* @__PURE__ */ jsxs("label", { className: "mb-1.5 block text-sm font-medium", children: [
6749
7095
  /* @__PURE__ */ jsx(Clock, { className: "mr-1 inline h-3.5 w-3.5" }),
6750
- labels.startDate ?? "Start"
7096
+ labels.date ?? labels.startDate ?? "Date & time"
6751
7097
  ] }),
7098
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
7099
+ /* @__PURE__ */ jsxs(
7100
+ Popover,
7101
+ {
7102
+ open: datePickerOpen,
7103
+ onOpenChange: setDatePickerOpen,
7104
+ children: [
7105
+ /* @__PURE__ */ jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
7106
+ "button",
7107
+ {
7108
+ type: "button",
7109
+ disabled: isCompleted,
7110
+ className: cn(
7111
+ "inline-flex h-9 items-center rounded-md border border-input bg-background px-3 text-sm",
7112
+ "hover:bg-accent hover:text-accent-foreground",
7113
+ "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
7114
+ "disabled:cursor-not-allowed disabled:opacity-50"
7115
+ ),
7116
+ children: dateLabel
7117
+ }
7118
+ ) }),
7119
+ /* @__PURE__ */ jsx(
7120
+ PopoverContent,
7121
+ {
7122
+ className: "z-[100] w-auto p-2",
7123
+ align: "start",
7124
+ children: /* @__PURE__ */ jsx(
7125
+ DayPicker,
7126
+ {
7127
+ mode: "single",
7128
+ selected: date,
7129
+ onSelect: (d) => {
7130
+ if (d) {
7131
+ setDate(startOfDay(d));
7132
+ setDatePickerOpen(false);
7133
+ }
7134
+ },
7135
+ showOutsideDays: true,
7136
+ classNames: dayPickerClassNames
7137
+ }
7138
+ )
7139
+ }
7140
+ )
7141
+ ]
7142
+ }
7143
+ ),
7144
+ !allDay && /* @__PURE__ */ jsxs(Fragment, { children: [
7145
+ /* @__PURE__ */ jsx(
7146
+ TimeCombobox,
7147
+ {
7148
+ value: startTime,
7149
+ onChange: handleStartTimeChange,
7150
+ disabled: isCompleted,
7151
+ "aria-label": labels.startTime ?? "Start time"
7152
+ }
7153
+ ),
7154
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2013" }),
7155
+ /* @__PURE__ */ jsx(
7156
+ TimeCombobox,
7157
+ {
7158
+ value: endTime,
7159
+ onChange: setEndTime,
7160
+ disabled: isCompleted,
7161
+ durationFrom: startTime,
7162
+ "aria-label": labels.endTime ?? "End time"
7163
+ }
7164
+ )
7165
+ ] })
7166
+ ] })
7167
+ ] }),
7168
+ /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm", children: [
6752
7169
  /* @__PURE__ */ jsx(
6753
7170
  "input",
6754
7171
  {
6755
- type: "datetime-local",
6756
- value: dtstart,
6757
- onChange: (e) => setDtstart(e.target.value),
7172
+ type: "checkbox",
7173
+ checked: allDay,
7174
+ onChange: (e) => setAllDay(e.target.checked),
6758
7175
  disabled: isCompleted,
6759
- className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
7176
+ className: "rounded border-input"
6760
7177
  }
6761
- )
7178
+ ),
7179
+ labels.allDay ?? "All day"
7180
+ ] }),
7181
+ (linkSearch || referenceId) && !isExisting && /* @__PURE__ */ jsxs("div", { children: [
7182
+ /* @__PURE__ */ jsxs("label", { className: "mb-1 block text-sm font-medium", children: [
7183
+ /* @__PURE__ */ jsx(Link2, { className: "mr-1 inline h-3.5 w-3.5" }),
7184
+ linkSearch?.label ?? labels.link ?? "Link to case"
7185
+ ] }),
7186
+ referenceId ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-md border px-3 py-2 text-sm", children: [
7187
+ /* @__PURE__ */ jsx("span", { children: linkLabel || referenceId }),
7188
+ linkSearch && /* @__PURE__ */ jsx(
7189
+ "button",
7190
+ {
7191
+ type: "button",
7192
+ className: "text-xs text-muted-foreground hover:text-foreground",
7193
+ onClick: () => {
7194
+ setReferenceId("");
7195
+ setReferenceType("");
7196
+ setLinkLabel("");
7197
+ },
7198
+ children: "Clear"
7199
+ }
7200
+ )
7201
+ ] }) : linkSearch ? /* @__PURE__ */ jsxs(Fragment, { children: [
7202
+ /* @__PURE__ */ jsx(
7203
+ "input",
7204
+ {
7205
+ value: linkQuery,
7206
+ onChange: (e) => setLinkQuery(e.target.value),
7207
+ placeholder: labels.linkPlaceholder ?? "Search cases\u2026",
7208
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
7209
+ }
7210
+ ),
7211
+ linkOptions.length > 0 && /* @__PURE__ */ jsx("ul", { className: "mt-1 max-h-40 overflow-auto rounded-md border bg-popover text-sm shadow-md", children: linkOptions.map((opt) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
7212
+ "button",
7213
+ {
7214
+ type: "button",
7215
+ className: "flex w-full flex-col px-3 py-2 text-left hover:bg-accent",
7216
+ onClick: () => {
7217
+ setReferenceId(opt.id);
7218
+ setReferenceType("cases-svc");
7219
+ setLinkLabel(opt.label);
7220
+ setLinkQuery("");
7221
+ setLinkOptions([]);
7222
+ },
7223
+ children: [
7224
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: opt.label }),
7225
+ opt.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: opt.description })
7226
+ ]
7227
+ }
7228
+ ) }, opt.id)) })
7229
+ ] }) : null
6762
7230
  ] }),
6763
7231
  /* @__PURE__ */ jsxs("div", { children: [
6764
- /* @__PURE__ */ jsx("label", { className: "mb-1 block text-sm font-medium", children: labels.endDate ?? "End" }),
7232
+ /* @__PURE__ */ jsxs("label", { className: "mb-1 block text-sm font-medium", children: [
7233
+ /* @__PURE__ */ jsx(MapPin, { className: "mr-1 inline h-3.5 w-3.5" }),
7234
+ labels.location ?? "Location"
7235
+ ] }),
6765
7236
  /* @__PURE__ */ jsx(
6766
7237
  "input",
6767
7238
  {
6768
- type: "datetime-local",
6769
- value: dtend,
6770
- onChange: (e) => setDtend(e.target.value),
7239
+ value: location,
7240
+ onChange: (e) => setLocation(e.target.value),
6771
7241
  disabled: isCompleted,
6772
- className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
7242
+ className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
7243
+ placeholder: "Add location"
6773
7244
  }
6774
7245
  )
6775
- ] })
6776
- ] }),
6777
- /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm", children: [
6778
- /* @__PURE__ */ jsx(
6779
- "input",
6780
- {
6781
- type: "checkbox",
6782
- checked: allDay,
6783
- onChange: (e) => setAllDay(e.target.checked),
6784
- disabled: isCompleted,
6785
- className: "rounded border-input"
6786
- }
6787
- ),
6788
- labels.allDay ?? "All day"
6789
- ] }),
6790
- (linkSearch || referenceId) && !isExisting && /* @__PURE__ */ jsxs("div", { children: [
6791
- /* @__PURE__ */ jsxs("label", { className: "mb-1 block text-sm font-medium", children: [
6792
- /* @__PURE__ */ jsx(Link2, { className: "mr-1 inline h-3.5 w-3.5" }),
6793
- linkSearch?.label ?? labels.link ?? "Link to case"
6794
7246
  ] }),
6795
- referenceId ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-md border px-3 py-2 text-sm", children: [
6796
- /* @__PURE__ */ jsx("span", { children: linkLabel || referenceId }),
6797
- linkSearch && /* @__PURE__ */ jsx(
6798
- "button",
6799
- {
6800
- type: "button",
6801
- className: "text-xs text-muted-foreground hover:text-foreground",
6802
- onClick: () => {
6803
- setReferenceId("");
6804
- setReferenceType("");
6805
- setLinkLabel("");
6806
- },
6807
- children: "Clear"
6808
- }
6809
- )
6810
- ] }) : linkSearch ? /* @__PURE__ */ jsxs(Fragment, { children: [
7247
+ /* @__PURE__ */ jsxs("div", { children: [
7248
+ /* @__PURE__ */ jsx("label", { className: "mb-1 block text-sm font-medium", children: labels.description ?? "Description" }),
6811
7249
  /* @__PURE__ */ jsx(
6812
- "input",
6813
- {
6814
- value: linkQuery,
6815
- onChange: (e) => setLinkQuery(e.target.value),
6816
- placeholder: labels.linkPlaceholder ?? "Search cases\u2026",
6817
- className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
6818
- }
6819
- ),
6820
- linkOptions.length > 0 && /* @__PURE__ */ jsx("ul", { className: "mt-1 max-h-40 overflow-auto rounded-md border bg-popover text-sm shadow-md", children: linkOptions.map((opt) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
6821
- "button",
7250
+ "textarea",
6822
7251
  {
6823
- type: "button",
6824
- className: "flex w-full flex-col px-3 py-2 text-left hover:bg-accent",
6825
- onClick: () => {
6826
- setReferenceId(opt.id);
6827
- setReferenceType("cases-svc");
6828
- setLinkLabel(opt.label);
6829
- setLinkQuery("");
6830
- setLinkOptions([]);
6831
- },
6832
- children: [
6833
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: opt.label }),
6834
- opt.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: opt.description })
6835
- ]
7252
+ value: description,
7253
+ onChange: (e) => setDescription(e.target.value),
7254
+ disabled: isCompleted,
7255
+ rows: 3,
7256
+ className: "w-full resize-none rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
7257
+ placeholder: "Add description"
6836
7258
  }
6837
- ) }, opt.id)) })
6838
- ] }) : null
7259
+ )
7260
+ ] })
6839
7261
  ] }),
6840
- /* @__PURE__ */ jsxs("div", { children: [
6841
- /* @__PURE__ */ jsxs("label", { className: "mb-1 block text-sm font-medium", children: [
6842
- /* @__PURE__ */ jsx(MapPin, { className: "mr-1 inline h-3.5 w-3.5" }),
6843
- labels.location ?? "Location"
6844
- ] }),
6845
- /* @__PURE__ */ jsx(
6846
- "input",
7262
+ /* @__PURE__ */ jsxs(DialogFooter, { className: "gap-2", children: [
7263
+ isExisting && onDelete && !isCompleted && /* @__PURE__ */ jsx(Button, { variant: "destructive", size: "sm", onClick: onDelete, children: labels.delete ?? "Delete" }),
7264
+ /* @__PURE__ */ jsx("div", { className: "flex-1" }),
7265
+ isExisting && /* @__PURE__ */ jsx(
7266
+ Button,
6847
7267
  {
6848
- value: location,
6849
- onChange: (e) => setLocation(e.target.value),
6850
- disabled: isCompleted,
6851
- className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
6852
- placeholder: "Add location"
7268
+ variant: "outline",
7269
+ size: "sm",
7270
+ onClick: () => {
7271
+ if (isDirty) {
7272
+ requestDiscard("preview");
7273
+ } else {
7274
+ setMode("preview");
7275
+ }
7276
+ },
7277
+ children: labels.cancel ?? "Cancel"
6853
7278
  }
6854
- )
6855
- ] }),
6856
- /* @__PURE__ */ jsxs("div", { children: [
6857
- /* @__PURE__ */ jsx("label", { className: "mb-1 block text-sm font-medium", children: labels.description ?? "Description" }),
6858
- /* @__PURE__ */ jsx(
6859
- "textarea",
7279
+ ),
7280
+ !isExisting && /* @__PURE__ */ jsx(
7281
+ Button,
6860
7282
  {
6861
- value: description,
6862
- onChange: (e) => setDescription(e.target.value),
6863
- disabled: isCompleted,
6864
- rows: 3,
6865
- className: "w-full resize-none rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
6866
- placeholder: "Add description"
7283
+ variant: "outline",
7284
+ size: "sm",
7285
+ onClick: () => handleOpenChange(false),
7286
+ children: labels.cancel ?? "Cancel"
7287
+ }
7288
+ ),
7289
+ !isCompleted && /* @__PURE__ */ jsxs(
7290
+ Button,
7291
+ {
7292
+ size: "sm",
7293
+ onClick: handleSave,
7294
+ disabled: saving || !summary.trim() || !date || !startTime || !endTime,
7295
+ children: [
7296
+ saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1 h-3.5 w-3.5 animate-spin" }),
7297
+ labels.save ?? "Save"
7298
+ ]
6867
7299
  }
6868
7300
  )
6869
7301
  ] })
7302
+ ] })
7303
+ ] }) }),
7304
+ /* @__PURE__ */ jsx(Dialog, { open: confirmDiscard, onOpenChange: setConfirmDiscard, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-[400px]", showCloseButton: false, children: [
7305
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
7306
+ /* @__PURE__ */ jsx(DialogTitle, { children: labels.discardTitle ?? "Discard changes?" }),
7307
+ /* @__PURE__ */ jsx(DialogDescription, { children: labels.discardMessage ?? "You have unsaved changes. Are you sure you want to discard them?" })
6870
7308
  ] }),
6871
7309
  /* @__PURE__ */ jsxs(DialogFooter, { className: "gap-2", children: [
6872
- isExisting && onDelete && !isCompleted && /* @__PURE__ */ jsx(Button, { variant: "destructive", size: "sm", onClick: onDelete, children: labels.delete ?? "Delete" }),
6873
- /* @__PURE__ */ jsx("div", { className: "flex-1" }),
6874
- isExisting && /* @__PURE__ */ jsx(
7310
+ /* @__PURE__ */ jsx(
6875
7311
  Button,
6876
7312
  {
6877
7313
  variant: "outline",
6878
7314
  size: "sm",
6879
- onClick: () => setMode("preview"),
6880
- children: labels.cancel ?? "Cancel"
7315
+ onClick: () => setConfirmDiscard(false),
7316
+ children: labels.discardCancel ?? "Keep editing"
6881
7317
  }
6882
7318
  ),
6883
- !isExisting && /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", children: labels.cancel ?? "Cancel" }) }),
6884
- !isCompleted && /* @__PURE__ */ jsxs(
7319
+ /* @__PURE__ */ jsx(
6885
7320
  Button,
6886
7321
  {
7322
+ variant: "destructive",
6887
7323
  size: "sm",
6888
- onClick: handleSave,
6889
- disabled: saving || !summary.trim() || !dtstart || !dtend,
6890
- children: [
6891
- saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1 h-3.5 w-3.5 animate-spin" }),
6892
- labels.save ?? "Save"
6893
- ]
7324
+ onClick: () => {
7325
+ if (discardIntent === "preview") {
7326
+ revertToSnapshot();
7327
+ setConfirmDiscard(false);
7328
+ setMode("preview");
7329
+ } else {
7330
+ forceClose();
7331
+ }
7332
+ },
7333
+ children: labels.discardConfirm ?? "Discard"
6894
7334
  }
6895
7335
  )
6896
7336
  ] })
6897
- ] })
6898
- ] }) });
7337
+ ] }) })
7338
+ ] });
6899
7339
  }
6900
7340
  function MiniCalendar({
6901
7341
  selected,