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