@algenium/blocks 1.19.1 → 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,67 @@ 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();
6621
6955
  } catch (err) {
6622
6956
  console.error("[EventDialog] Failed to save event:", err);
6623
6957
  } finally {
@@ -6629,275 +6963,379 @@ function EventDialog({
6629
6963
  setCompleting(true);
6630
6964
  try {
6631
6965
  await onComplete();
6632
- onOpenChange(false);
6966
+ forceClose();
6633
6967
  } finally {
6634
6968
  setCompleting(false);
6635
6969
  }
6636
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
+ };
6637
6980
  const CategoryIcon = getCategoryIcon(selectedCategory?.alias);
6638
- return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-[520px]", children: [
6639
- /* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: mode === "preview" ? labels.previewTitle ?? "Event details" : isExisting ? labels.editTitle ?? "Edit event" : labels.createTitle ?? "New event" }) }),
6640
- mode === "preview" ? /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-2", children: [
6641
- /* @__PURE__ */ jsxs("div", { children: [
6642
- /* @__PURE__ */ jsx("h3", { className: "text-lg font-semibold", children: event?.summary }),
6643
- isCompleted && /* @__PURE__ */ jsxs("p", { className: "mt-1 inline-flex items-center gap-1 text-sm text-emerald-600", children: [
6644
- /* @__PURE__ */ jsx(CheckCircle2, { className: "h-3.5 w-3.5" }),
6645
- labels.completed ?? "Completed"
6646
- ] })
6647
- ] }),
6648
- event?.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground whitespace-pre-wrap", children: event.description }),
6649
- /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2 text-sm text-muted-foreground", children: [
6650
- /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
6651
- /* @__PURE__ */ jsx(Clock, { className: "h-3.5 w-3.5" }),
6652
- event?.dtstart ? new Date(event.dtstart).toLocaleString() : "\u2014",
6653
- 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
+ ] })
6654
6992
  ] }),
6655
- event?.location && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
6656
- /* @__PURE__ */ jsx(MapPin, { className: "h-3.5 w-3.5" }),
6657
- event.location
6658
- ] })
6659
- ] }),
6660
- selectedCategory && /* @__PURE__ */ jsxs(
6661
- "span",
6662
- {
6663
- className: "inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 text-xs font-medium",
6664
- style: {
6665
- backgroundColor: selectedCategory.bgColor,
6666
- color: selectedCategory.iconColor
6667
- },
6668
- children: [
6669
- /* @__PURE__ */ jsx(CategoryIcon, { className: "h-3.5 w-3.5" }),
6670
- selectedCategory.name
6671
- ]
6672
- }
6673
- ),
6674
- (event?.referenceId || linkLabel) && /* @__PURE__ */ jsxs("p", { className: "inline-flex items-center gap-1.5 text-sm", children: [
6675
- /* @__PURE__ */ jsx(Link2, { className: "h-3.5 w-3.5 text-muted-foreground" }),
6676
- /* @__PURE__ */ jsxs("span", { className: "font-medium", children: [
6677
- linkSearch?.label ?? labels.link ?? "Linked case",
6678
- ":"
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
+ ] })
6679
7004
  ] }),
6680
- linkLabel || event?.referenceId
6681
- ] }),
6682
- /* @__PURE__ */ jsxs(DialogFooter, { className: "gap-2", children: [
6683
- !isCompleted && onComplete && /* @__PURE__ */ jsxs(
6684
- Button,
7005
+ selectedCategory && /* @__PURE__ */ jsxs(
7006
+ "span",
6685
7007
  {
6686
- variant: "secondary",
6687
- size: "sm",
6688
- onClick: handleComplete,
6689
- 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
+ },
6690
7013
  children: [
6691
- completing && /* @__PURE__ */ jsx(Loader2, { className: "mr-1 h-3.5 w-3.5 animate-spin" }),
6692
- labels.markComplete ?? "Mark as completed"
7014
+ /* @__PURE__ */ jsx(CategoryIcon, { className: "h-3.5 w-3.5" }),
7015
+ selectedCategory.name
6693
7016
  ]
6694
7017
  }
6695
7018
  ),
6696
- /* @__PURE__ */ jsx("div", { className: "flex-1" }),
6697
- isExisting && onDelete && !isCompleted && /* @__PURE__ */ jsx(Button, { variant: "destructive", size: "sm", onClick: onDelete, children: labels.delete ?? "Delete" }),
6698
- /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", children: labels.cancel ?? "Close" }) }),
6699
- !isCompleted && /* @__PURE__ */ jsxs(Button, { size: "sm", onClick: () => setMode("edit"), children: [
6700
- /* @__PURE__ */ jsx(Pencil, { className: "mr-1 h-3.5 w-3.5" }),
6701
- labels.edit ?? "Edit"
6702
- ] })
6703
- ] })
6704
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
6705
- /* @__PURE__ */ jsxs("div", { className: "space-y-4 py-2", children: [
6706
- /* @__PURE__ */ jsxs("div", { children: [
6707
- /* @__PURE__ */ jsx("label", { className: "mb-1 block text-sm font-medium", children: labels.summary ?? "Summary" }),
6708
- /* @__PURE__ */ jsx(
6709
- "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,
6710
7030
  {
6711
- value: summary,
6712
- onChange: (e) => setSummary(e.target.value),
6713
- 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",
6714
- placeholder: "Event name",
6715
- 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
+ ]
6716
7039
  }
6717
- )
6718
- ] }),
6719
- categories.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
6720
- /* @__PURE__ */ jsx("label", { className: "mb-1.5 block text-sm font-medium", children: labels.category ?? "Category" }),
6721
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2", children: categories.map((cat) => {
6722
- const Icon = getCategoryIcon(cat.alias);
6723
- const selected = categoryId === cat.id;
6724
- return /* @__PURE__ */ jsxs(
6725
- "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",
6726
7055
  {
6727
- type: "button",
6728
- disabled: isCompleted,
6729
- onClick: () => setCategoryId(selected ? "" : cat.id),
6730
- className: cn(
6731
- "inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs font-medium transition-all",
6732
- selected ? "ring-2 ring-offset-1 ring-primary" : "opacity-80 hover:opacity-100"
6733
- ),
6734
- style: {
6735
- backgroundColor: cat.bgColor,
6736
- color: cat.iconColor,
6737
- 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
+ ]
6738
7088
  },
6739
- children: [
6740
- /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5" }),
6741
- cat.name
6742
- ]
6743
- },
6744
- cat.id
6745
- );
6746
- }) })
6747
- ] }),
6748
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
7089
+ cat.id
7090
+ );
7091
+ }) })
7092
+ ] }),
6749
7093
  /* @__PURE__ */ jsxs("div", { children: [
6750
- /* @__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: [
6751
7095
  /* @__PURE__ */ jsx(Clock, { className: "mr-1 inline h-3.5 w-3.5" }),
6752
- labels.startDate ?? "Start"
7096
+ labels.date ?? labels.startDate ?? "Date & time"
6753
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: [
6754
7169
  /* @__PURE__ */ jsx(
6755
7170
  "input",
6756
7171
  {
6757
- type: "datetime-local",
6758
- value: dtstart,
6759
- onChange: (e) => setDtstart(e.target.value),
7172
+ type: "checkbox",
7173
+ checked: allDay,
7174
+ onChange: (e) => setAllDay(e.target.checked),
6760
7175
  disabled: isCompleted,
6761
- 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"
6762
7177
  }
6763
- )
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
6764
7230
  ] }),
6765
7231
  /* @__PURE__ */ jsxs("div", { children: [
6766
- /* @__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
+ ] }),
6767
7236
  /* @__PURE__ */ jsx(
6768
7237
  "input",
6769
7238
  {
6770
- type: "datetime-local",
6771
- value: dtend,
6772
- onChange: (e) => setDtend(e.target.value),
7239
+ value: location,
7240
+ onChange: (e) => setLocation(e.target.value),
6773
7241
  disabled: isCompleted,
6774
- 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"
6775
7244
  }
6776
7245
  )
6777
- ] })
6778
- ] }),
6779
- /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2 text-sm", children: [
6780
- /* @__PURE__ */ jsx(
6781
- "input",
6782
- {
6783
- type: "checkbox",
6784
- checked: allDay,
6785
- onChange: (e) => setAllDay(e.target.checked),
6786
- disabled: isCompleted,
6787
- className: "rounded border-input"
6788
- }
6789
- ),
6790
- labels.allDay ?? "All day"
6791
- ] }),
6792
- (linkSearch || referenceId) && !isExisting && /* @__PURE__ */ jsxs("div", { children: [
6793
- /* @__PURE__ */ jsxs("label", { className: "mb-1 block text-sm font-medium", children: [
6794
- /* @__PURE__ */ jsx(Link2, { className: "mr-1 inline h-3.5 w-3.5" }),
6795
- linkSearch?.label ?? labels.link ?? "Link to case"
6796
7246
  ] }),
6797
- referenceId ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between rounded-md border px-3 py-2 text-sm", children: [
6798
- /* @__PURE__ */ jsx("span", { children: linkLabel || referenceId }),
6799
- linkSearch && /* @__PURE__ */ jsx(
6800
- "button",
6801
- {
6802
- type: "button",
6803
- className: "text-xs text-muted-foreground hover:text-foreground",
6804
- onClick: () => {
6805
- setReferenceId("");
6806
- setReferenceType("");
6807
- setLinkLabel("");
6808
- },
6809
- children: "Clear"
6810
- }
6811
- )
6812
- ] }) : 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" }),
6813
7249
  /* @__PURE__ */ jsx(
6814
- "input",
6815
- {
6816
- value: linkQuery,
6817
- onChange: (e) => setLinkQuery(e.target.value),
6818
- placeholder: labels.linkPlaceholder ?? "Search cases\u2026",
6819
- 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"
6820
- }
6821
- ),
6822
- 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(
6823
- "button",
7250
+ "textarea",
6824
7251
  {
6825
- type: "button",
6826
- className: "flex w-full flex-col px-3 py-2 text-left hover:bg-accent",
6827
- onClick: () => {
6828
- setReferenceId(opt.id);
6829
- setReferenceType("cases-svc");
6830
- setLinkLabel(opt.label);
6831
- setLinkQuery("");
6832
- setLinkOptions([]);
6833
- },
6834
- children: [
6835
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: opt.label }),
6836
- opt.description && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: opt.description })
6837
- ]
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"
6838
7258
  }
6839
- ) }, opt.id)) })
6840
- ] }) : null
7259
+ )
7260
+ ] })
6841
7261
  ] }),
6842
- /* @__PURE__ */ jsxs("div", { children: [
6843
- /* @__PURE__ */ jsxs("label", { className: "mb-1 block text-sm font-medium", children: [
6844
- /* @__PURE__ */ jsx(MapPin, { className: "mr-1 inline h-3.5 w-3.5" }),
6845
- labels.location ?? "Location"
6846
- ] }),
6847
- /* @__PURE__ */ jsx(
6848
- "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,
6849
7267
  {
6850
- value: location,
6851
- onChange: (e) => setLocation(e.target.value),
6852
- disabled: isCompleted,
6853
- 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",
6854
- 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"
6855
7278
  }
6856
- )
6857
- ] }),
6858
- /* @__PURE__ */ jsxs("div", { children: [
6859
- /* @__PURE__ */ jsx("label", { className: "mb-1 block text-sm font-medium", children: labels.description ?? "Description" }),
6860
- /* @__PURE__ */ jsx(
6861
- "textarea",
7279
+ ),
7280
+ !isExisting && /* @__PURE__ */ jsx(
7281
+ Button,
6862
7282
  {
6863
- value: description,
6864
- onChange: (e) => setDescription(e.target.value),
6865
- disabled: isCompleted,
6866
- rows: 3,
6867
- 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",
6868
- 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
+ ]
6869
7299
  }
6870
7300
  )
6871
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?" })
6872
7308
  ] }),
6873
7309
  /* @__PURE__ */ jsxs(DialogFooter, { className: "gap-2", children: [
6874
- isExisting && onDelete && !isCompleted && /* @__PURE__ */ jsx(Button, { variant: "destructive", size: "sm", onClick: onDelete, children: labels.delete ?? "Delete" }),
6875
- /* @__PURE__ */ jsx("div", { className: "flex-1" }),
6876
- isExisting && /* @__PURE__ */ jsx(
7310
+ /* @__PURE__ */ jsx(
6877
7311
  Button,
6878
7312
  {
6879
7313
  variant: "outline",
6880
7314
  size: "sm",
6881
- onClick: () => setMode("preview"),
6882
- children: labels.cancel ?? "Cancel"
7315
+ onClick: () => setConfirmDiscard(false),
7316
+ children: labels.discardCancel ?? "Keep editing"
6883
7317
  }
6884
7318
  ),
6885
- !isExisting && /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", children: labels.cancel ?? "Cancel" }) }),
6886
- !isCompleted && /* @__PURE__ */ jsxs(
7319
+ /* @__PURE__ */ jsx(
6887
7320
  Button,
6888
7321
  {
7322
+ variant: "destructive",
6889
7323
  size: "sm",
6890
- onClick: handleSave,
6891
- disabled: saving || !summary.trim() || !dtstart || !dtend,
6892
- children: [
6893
- saving && /* @__PURE__ */ jsx(Loader2, { className: "mr-1 h-3.5 w-3.5 animate-spin" }),
6894
- labels.save ?? "Save"
6895
- ]
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"
6896
7334
  }
6897
7335
  )
6898
7336
  ] })
6899
- ] })
6900
- ] }) });
7337
+ ] }) })
7338
+ ] });
6901
7339
  }
6902
7340
  function MiniCalendar({
6903
7341
  selected,