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