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