@adatechnology/scheduling-ui 0.1.0-rc.5 → 0.1.0-rc.7
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.d.ts +3 -1
- package/dist/index.js +607 -342
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -313,11 +313,11 @@ var DEFAULT_SCHEDULING_WORKSPACE_LABELS = {
|
|
|
313
313
|
};
|
|
314
314
|
|
|
315
315
|
// src/workspace/SchedulingWorkspace.tsx
|
|
316
|
-
import { useState as
|
|
316
|
+
import { useState as useState14 } from "react";
|
|
317
317
|
|
|
318
318
|
// src/workspace/AgendaArea.tsx
|
|
319
319
|
import { CalendarDays, ChevronLeft, ChevronRight } from "lucide-react";
|
|
320
|
-
import { useEffect as
|
|
320
|
+
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
321
321
|
import { MAX_PAGE_SIZE } from "@adatechnology/scheduling-contracts";
|
|
322
322
|
|
|
323
323
|
// src/locales/pt-BR.json
|
|
@@ -424,7 +424,9 @@ var pt_BR_default = {
|
|
|
424
424
|
"booking.emptyTitle": "Nenhuma reserva",
|
|
425
425
|
"booking.emptyHint": "Ajuste os filtros de status ou aguarde novas solicita\xE7\xF5es.",
|
|
426
426
|
"availability.selectResourceTitle": "Escolha um recurso",
|
|
427
|
-
"availability.selectResourceHint": "As regras de disponibilidade s\xE3o definidas por recurso."
|
|
427
|
+
"availability.selectResourceHint": "As regras de disponibilidade s\xE3o definidas por recurso.",
|
|
428
|
+
"select.search": "Buscar\u2026",
|
|
429
|
+
"select.noResults": "Nenhum resultado"
|
|
428
430
|
};
|
|
429
431
|
|
|
430
432
|
// src/locales/en.json
|
|
@@ -531,7 +533,9 @@ var en_default = {
|
|
|
531
533
|
"booking.emptyTitle": "No bookings",
|
|
532
534
|
"booking.emptyHint": "Adjust the status filters or wait for new requests.",
|
|
533
535
|
"availability.selectResourceTitle": "Pick a resource",
|
|
534
|
-
"availability.selectResourceHint": "Availability rules are defined per resource."
|
|
536
|
+
"availability.selectResourceHint": "Availability rules are defined per resource.",
|
|
537
|
+
"select.search": "Search\u2026",
|
|
538
|
+
"select.noResults": "No results"
|
|
535
539
|
};
|
|
536
540
|
|
|
537
541
|
// src/locales/index.ts
|
|
@@ -680,7 +684,7 @@ function AgendaGrid({ bookings, days, now, onSelect }) {
|
|
|
680
684
|
/* @__PURE__ */ jsxs(
|
|
681
685
|
"div",
|
|
682
686
|
{
|
|
683
|
-
className: `${HEADER_CLASS} flex items-center gap-1 border-b border-gray-200 dark:border-gray-800 ${isToday ? "text-brand-700 dark:text-brand-
|
|
687
|
+
className: `${HEADER_CLASS} flex items-center gap-1 border-b border-gray-200 dark:border-gray-800 ${isToday ? "text-brand-700 dark:text-brand-400" : ""}`,
|
|
684
688
|
children: [
|
|
685
689
|
day.toLocaleDateString(locale, { weekday: "short", day: "2-digit", month: "2-digit" }),
|
|
686
690
|
isToday && /* @__PURE__ */ jsx3("span", { className: "h-1.5 w-1.5 rounded-full bg-brand-600", "aria-hidden": "true" })
|
|
@@ -755,11 +759,256 @@ function AgendaGrid({ bookings, days, now, onSelect }) {
|
|
|
755
759
|
|
|
756
760
|
// src/components/BookingDrawer.tsx
|
|
757
761
|
import { XCircle } from "lucide-react";
|
|
758
|
-
import { useState } from "react";
|
|
762
|
+
import { useState as useState2 } from "react";
|
|
759
763
|
import { BOOKING_STATUS as BOOKING_STATUS2 } from "@adatechnology/scheduling-contracts";
|
|
760
764
|
|
|
761
765
|
// src/components/DateTimeField.tsx
|
|
762
|
-
import { useId } from "react";
|
|
766
|
+
import { useId as useId2, useMemo as useMemo3 } from "react";
|
|
767
|
+
|
|
768
|
+
// src/components/SelectField.tsx
|
|
769
|
+
import { Check, ChevronDown, Search } from "lucide-react";
|
|
770
|
+
import { useEffect, useId, useMemo as useMemo2, useRef, useState } from "react";
|
|
771
|
+
|
|
772
|
+
// src/components/selectFilter.util.ts
|
|
773
|
+
var COMBINING_MARKS = /[̀-ͯ]/g;
|
|
774
|
+
function normalizeForSearch(text) {
|
|
775
|
+
return text.normalize("NFD").replace(COMBINING_MARKS, "").toLowerCase().trim();
|
|
776
|
+
}
|
|
777
|
+
function filterSelectOptions(options, query) {
|
|
778
|
+
const needle = normalizeForSearch(query);
|
|
779
|
+
if (needle === "") return options;
|
|
780
|
+
return options.filter((option) => normalizeForSearch(option.label).includes(needle));
|
|
781
|
+
}
|
|
782
|
+
function findByPrefix(options, prefix) {
|
|
783
|
+
const needle = normalizeForSearch(prefix);
|
|
784
|
+
if (needle === "") return -1;
|
|
785
|
+
return options.findIndex((option) => normalizeForSearch(option.label).startsWith(needle));
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// src/components/ui.constant.ts
|
|
789
|
+
var BUTTON_BASE = "inline-flex items-center justify-center gap-2 rounded-lg px-3 text-sm font-medium min-h-11 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-900 disabled:opacity-50 disabled:pointer-events-none";
|
|
790
|
+
var BUTTON_PRIMARY = `${BUTTON_BASE} bg-brand-600 text-white hover:bg-brand-700`;
|
|
791
|
+
var BUTTON_SECONDARY = `${BUTTON_BASE} border border-gray-300 bg-white text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200 dark:hover:bg-gray-800`;
|
|
792
|
+
var BUTTON_GHOST = `${BUTTON_BASE} text-gray-600 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-800`;
|
|
793
|
+
var BUTTON_DANGER = `${BUTTON_BASE} text-red-700 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950/40`;
|
|
794
|
+
var ICON_BUTTON = `${BUTTON_SECONDARY} min-w-11 px-0`;
|
|
795
|
+
var FIELD_CONTROL = "min-h-11 rounded-lg border border-gray-300 bg-white px-3 text-sm text-gray-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100";
|
|
796
|
+
var FIELD_LABEL = "text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400";
|
|
797
|
+
var SURFACE_BORDER = "rounded-xl border border-gray-200 dark:border-gray-800";
|
|
798
|
+
var ROW_STRIPE = "bg-gray-50 dark:bg-gray-800/40";
|
|
799
|
+
|
|
800
|
+
// src/components/SelectField.tsx
|
|
801
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
802
|
+
var SEARCHABLE_THRESHOLD = 8;
|
|
803
|
+
var POPUP_MAX_HEIGHT = 288;
|
|
804
|
+
var TYPEAHEAD_RESET_MS = 700;
|
|
805
|
+
var OPTION_BASE = "flex w-full cursor-pointer items-center gap-2 px-3 py-2 text-left text-sm";
|
|
806
|
+
function SelectField({
|
|
807
|
+
label,
|
|
808
|
+
value,
|
|
809
|
+
options,
|
|
810
|
+
onChange,
|
|
811
|
+
emptyOptionLabel,
|
|
812
|
+
searchable,
|
|
813
|
+
hideLabel,
|
|
814
|
+
className
|
|
815
|
+
}) {
|
|
816
|
+
const { locale } = useSchedulingConfig();
|
|
817
|
+
const messages = resolveSchedulingMessages(locale);
|
|
818
|
+
const fieldId = useId();
|
|
819
|
+
const listboxId = `${fieldId}-listbox`;
|
|
820
|
+
const containerRef = useRef(null);
|
|
821
|
+
const triggerRef = useRef(null);
|
|
822
|
+
const searchRef = useRef(null);
|
|
823
|
+
const listRef = useRef(null);
|
|
824
|
+
const typeahead = useRef({ text: "", at: 0 });
|
|
825
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
826
|
+
const [openUpward, setOpenUpward] = useState(false);
|
|
827
|
+
const [query, setQuery] = useState("");
|
|
828
|
+
const [highlighted, setHighlighted] = useState(0);
|
|
829
|
+
const allOptions = useMemo2(
|
|
830
|
+
() => emptyOptionLabel === void 0 ? options : [{ value: "", label: emptyOptionLabel }, ...options],
|
|
831
|
+
[emptyOptionLabel, options]
|
|
832
|
+
);
|
|
833
|
+
const withSearch = searchable ?? allOptions.length >= SEARCHABLE_THRESHOLD;
|
|
834
|
+
const visibleOptions = useMemo2(
|
|
835
|
+
() => withSearch ? filterSelectOptions(allOptions, query) : allOptions,
|
|
836
|
+
[allOptions, query, withSearch]
|
|
837
|
+
);
|
|
838
|
+
const selected = allOptions.find((option) => option.value === value);
|
|
839
|
+
function open() {
|
|
840
|
+
const rect = triggerRef.current?.getBoundingClientRect();
|
|
841
|
+
if (rect) {
|
|
842
|
+
const spaceBelow = window.innerHeight - rect.bottom;
|
|
843
|
+
setOpenUpward(spaceBelow < POPUP_MAX_HEIGHT && rect.top > spaceBelow);
|
|
844
|
+
}
|
|
845
|
+
setQuery("");
|
|
846
|
+
setHighlighted(Math.max(allOptions.findIndex((option) => option.value === value), 0));
|
|
847
|
+
setIsOpen(true);
|
|
848
|
+
}
|
|
849
|
+
function close(returnFocus = true) {
|
|
850
|
+
setIsOpen(false);
|
|
851
|
+
if (returnFocus) triggerRef.current?.focus();
|
|
852
|
+
}
|
|
853
|
+
function commit(option) {
|
|
854
|
+
onChange(option.value);
|
|
855
|
+
close();
|
|
856
|
+
}
|
|
857
|
+
useEffect(() => {
|
|
858
|
+
if (!isOpen) return;
|
|
859
|
+
function handlePointerDown(event) {
|
|
860
|
+
if (!containerRef.current?.contains(event.target)) setIsOpen(false);
|
|
861
|
+
}
|
|
862
|
+
document.addEventListener("pointerdown", handlePointerDown);
|
|
863
|
+
return () => document.removeEventListener("pointerdown", handlePointerDown);
|
|
864
|
+
}, [isOpen]);
|
|
865
|
+
useEffect(() => {
|
|
866
|
+
if (isOpen && withSearch) searchRef.current?.focus();
|
|
867
|
+
}, [isOpen, withSearch]);
|
|
868
|
+
useEffect(() => {
|
|
869
|
+
if (!isOpen) return;
|
|
870
|
+
listRef.current?.children[highlighted]?.scrollIntoView({ block: "nearest" });
|
|
871
|
+
}, [highlighted, isOpen]);
|
|
872
|
+
function jumpByTypeahead(key) {
|
|
873
|
+
if (withSearch || key.length !== 1 || !/\S/.test(key)) return false;
|
|
874
|
+
const now = Date.now();
|
|
875
|
+
const text = now - typeahead.current.at > TYPEAHEAD_RESET_MS ? key : typeahead.current.text + key;
|
|
876
|
+
typeahead.current = { text, at: now };
|
|
877
|
+
const index = findByPrefix(visibleOptions, text);
|
|
878
|
+
if (index >= 0) setHighlighted(index);
|
|
879
|
+
return index >= 0;
|
|
880
|
+
}
|
|
881
|
+
function handleKeyDown(event) {
|
|
882
|
+
if (!isOpen) {
|
|
883
|
+
if (["ArrowDown", "ArrowUp", "Enter", " "].includes(event.key)) {
|
|
884
|
+
event.preventDefault();
|
|
885
|
+
open();
|
|
886
|
+
}
|
|
887
|
+
return;
|
|
888
|
+
}
|
|
889
|
+
switch (event.key) {
|
|
890
|
+
case "Escape":
|
|
891
|
+
event.preventDefault();
|
|
892
|
+
close();
|
|
893
|
+
return;
|
|
894
|
+
case "Tab":
|
|
895
|
+
close(false);
|
|
896
|
+
return;
|
|
897
|
+
case "ArrowDown":
|
|
898
|
+
event.preventDefault();
|
|
899
|
+
setHighlighted((current) => Math.min(current + 1, visibleOptions.length - 1));
|
|
900
|
+
return;
|
|
901
|
+
case "ArrowUp":
|
|
902
|
+
event.preventDefault();
|
|
903
|
+
setHighlighted((current) => Math.max(current - 1, 0));
|
|
904
|
+
return;
|
|
905
|
+
case "Home":
|
|
906
|
+
event.preventDefault();
|
|
907
|
+
setHighlighted(0);
|
|
908
|
+
return;
|
|
909
|
+
case "End":
|
|
910
|
+
event.preventDefault();
|
|
911
|
+
setHighlighted(visibleOptions.length - 1);
|
|
912
|
+
return;
|
|
913
|
+
case "Enter": {
|
|
914
|
+
event.preventDefault();
|
|
915
|
+
const option = visibleOptions[highlighted];
|
|
916
|
+
if (option) commit(option);
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
default:
|
|
920
|
+
if (jumpByTypeahead(event.key)) event.preventDefault();
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
return /* @__PURE__ */ jsxs2("div", { className: `flex flex-col gap-1 ${className ?? ""}`, children: [
|
|
924
|
+
/* @__PURE__ */ jsx4("span", { id: fieldId, className: hideLabel ? "sr-only" : FIELD_LABEL, children: label }),
|
|
925
|
+
/* @__PURE__ */ jsxs2("div", { ref: containerRef, className: "relative", children: [
|
|
926
|
+
/* @__PURE__ */ jsxs2(
|
|
927
|
+
"button",
|
|
928
|
+
{
|
|
929
|
+
ref: triggerRef,
|
|
930
|
+
type: "button",
|
|
931
|
+
role: "combobox",
|
|
932
|
+
"aria-controls": listboxId,
|
|
933
|
+
"aria-expanded": isOpen,
|
|
934
|
+
"aria-haspopup": "listbox",
|
|
935
|
+
"aria-labelledby": fieldId,
|
|
936
|
+
onClick: () => isOpen ? close(false) : open(),
|
|
937
|
+
onKeyDown: handleKeyDown,
|
|
938
|
+
className: `${FIELD_CONTROL} flex w-full items-center gap-2 text-left`,
|
|
939
|
+
children: [
|
|
940
|
+
/* @__PURE__ */ jsx4("span", { className: `flex-1 truncate ${selected ? "" : "text-gray-500 dark:text-gray-400"}`, children: selected?.label ?? emptyOptionLabel ?? "" }),
|
|
941
|
+
/* @__PURE__ */ jsx4(
|
|
942
|
+
ChevronDown,
|
|
943
|
+
{
|
|
944
|
+
"aria-hidden": "true",
|
|
945
|
+
className: `h-4 w-4 shrink-0 text-gray-400 transition-transform ${isOpen ? "rotate-180" : ""}`
|
|
946
|
+
}
|
|
947
|
+
)
|
|
948
|
+
]
|
|
949
|
+
}
|
|
950
|
+
),
|
|
951
|
+
isOpen && /* @__PURE__ */ jsxs2(
|
|
952
|
+
"div",
|
|
953
|
+
{
|
|
954
|
+
className: `absolute z-30 w-full min-w-max overflow-hidden rounded-lg border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-900 ${openUpward ? "bottom-full mb-1" : "top-full mt-1"}`,
|
|
955
|
+
children: [
|
|
956
|
+
withSearch && /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 border-b border-gray-200 px-3 dark:border-gray-700", children: [
|
|
957
|
+
/* @__PURE__ */ jsx4(Search, { "aria-hidden": "true", className: "h-4 w-4 shrink-0 text-gray-400" }),
|
|
958
|
+
/* @__PURE__ */ jsx4(
|
|
959
|
+
"input",
|
|
960
|
+
{
|
|
961
|
+
ref: searchRef,
|
|
962
|
+
type: "text",
|
|
963
|
+
value: query,
|
|
964
|
+
onChange: (event) => {
|
|
965
|
+
setQuery(event.target.value);
|
|
966
|
+
setHighlighted(0);
|
|
967
|
+
},
|
|
968
|
+
onKeyDown: handleKeyDown,
|
|
969
|
+
"aria-label": messages["select.search"],
|
|
970
|
+
placeholder: messages["select.search"],
|
|
971
|
+
className: "min-h-11 w-full bg-transparent text-sm text-gray-900 outline-none placeholder:text-gray-400 dark:text-gray-100"
|
|
972
|
+
}
|
|
973
|
+
)
|
|
974
|
+
] }),
|
|
975
|
+
/* @__PURE__ */ jsxs2(
|
|
976
|
+
"ul",
|
|
977
|
+
{
|
|
978
|
+
ref: listRef,
|
|
979
|
+
id: listboxId,
|
|
980
|
+
role: "listbox",
|
|
981
|
+
"aria-labelledby": fieldId,
|
|
982
|
+
className: "max-h-72 overflow-y-auto py-1",
|
|
983
|
+
children: [
|
|
984
|
+
visibleOptions.map((option, index) => {
|
|
985
|
+
const isSelected = option.value === value;
|
|
986
|
+
return /* @__PURE__ */ jsxs2(
|
|
987
|
+
"li",
|
|
988
|
+
{
|
|
989
|
+
role: "option",
|
|
990
|
+
"aria-selected": isSelected,
|
|
991
|
+
onPointerEnter: () => setHighlighted(index),
|
|
992
|
+
onClick: () => commit(option),
|
|
993
|
+
className: `${OPTION_BASE} ${index === highlighted ? "bg-brand-50 dark:bg-brand-900/30" : ""} ${isSelected ? "font-medium text-brand-800 dark:text-brand-200" : "text-gray-800 dark:text-gray-200"}`,
|
|
994
|
+
children: [
|
|
995
|
+
/* @__PURE__ */ jsx4("span", { className: "flex-1 truncate", children: option.label }),
|
|
996
|
+
isSelected && /* @__PURE__ */ jsx4(Check, { "aria-hidden": "true", className: "h-4 w-4 shrink-0" })
|
|
997
|
+
]
|
|
998
|
+
},
|
|
999
|
+
option.value
|
|
1000
|
+
);
|
|
1001
|
+
}),
|
|
1002
|
+
visibleOptions.length === 0 && /* @__PURE__ */ jsx4("li", { className: "px-3 py-3 text-sm text-gray-500 dark:text-gray-400", children: messages["select.noResults"] })
|
|
1003
|
+
]
|
|
1004
|
+
}
|
|
1005
|
+
)
|
|
1006
|
+
]
|
|
1007
|
+
}
|
|
1008
|
+
)
|
|
1009
|
+
] })
|
|
1010
|
+
] });
|
|
1011
|
+
}
|
|
763
1012
|
|
|
764
1013
|
// src/components/dateTimeParts.util.ts
|
|
765
1014
|
var DATE_TIME_LOCAL_PATTERN = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})$/;
|
|
@@ -789,7 +1038,7 @@ function buildYearOptions(params) {
|
|
|
789
1038
|
}
|
|
790
1039
|
|
|
791
1040
|
// src/components/DateTimeField.tsx
|
|
792
|
-
import { jsx as
|
|
1041
|
+
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
793
1042
|
var FIELD_CLASS = "min-h-11 rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-2 text-sm";
|
|
794
1043
|
var YEAR_WINDOW = { PAST: 1, FUTURE: 2 };
|
|
795
1044
|
function partsOf(value, fallback) {
|
|
@@ -810,7 +1059,7 @@ function monthLabels(locale) {
|
|
|
810
1059
|
function DateTimeField({ label, value, onChange, emptyDefault }) {
|
|
811
1060
|
const { locale } = useSchedulingConfig();
|
|
812
1061
|
const messages = resolveSchedulingMessages(locale);
|
|
813
|
-
const groupId =
|
|
1062
|
+
const groupId = useId2();
|
|
814
1063
|
const today = emptyDefault ?? /* @__PURE__ */ new Date();
|
|
815
1064
|
const parts = partsOf(value, today);
|
|
816
1065
|
const years = buildYearOptions({
|
|
@@ -821,42 +1070,59 @@ function DateTimeField({ label, value, onChange, emptyDefault }) {
|
|
|
821
1070
|
});
|
|
822
1071
|
const days = daysInMonth({ year: parts.year, month: parts.month });
|
|
823
1072
|
const months = monthLabels(locale);
|
|
1073
|
+
const yearOptions = useMemo3(
|
|
1074
|
+
() => years.map((year) => ({ value: String(year), label: String(year) })),
|
|
1075
|
+
[years]
|
|
1076
|
+
);
|
|
1077
|
+
const monthOptions = useMemo3(
|
|
1078
|
+
() => months.map((name, index) => ({ value: String(index + 1), label: name })),
|
|
1079
|
+
[months]
|
|
1080
|
+
);
|
|
1081
|
+
const dayOptions = useMemo3(
|
|
1082
|
+
() => Array.from({ length: days }, (_, index) => ({ value: String(index + 1), label: String(index + 1) })),
|
|
1083
|
+
[days]
|
|
1084
|
+
);
|
|
824
1085
|
function change(patch) {
|
|
825
1086
|
onChange(formatDateTimeParts({ ...parts, ...patch }));
|
|
826
1087
|
}
|
|
827
|
-
return /* @__PURE__ */
|
|
828
|
-
/* @__PURE__ */
|
|
829
|
-
/* @__PURE__ */
|
|
830
|
-
|
|
1088
|
+
return /* @__PURE__ */ jsxs3("div", { role: "group", "aria-labelledby": groupId, className: "flex flex-wrap items-center gap-2", children: [
|
|
1089
|
+
/* @__PURE__ */ jsx5("span", { id: groupId, className: "sr-only", children: label }),
|
|
1090
|
+
/* @__PURE__ */ jsx5(
|
|
1091
|
+
SelectField,
|
|
831
1092
|
{
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
1093
|
+
hideLabel: true,
|
|
1094
|
+
label: `${label} \u2014 ${messages["datetime.year"]}`,
|
|
1095
|
+
value: String(parts.year),
|
|
1096
|
+
options: yearOptions,
|
|
1097
|
+
onChange: (next) => change({ year: Number(next) }),
|
|
1098
|
+
className: "min-w-24"
|
|
837
1099
|
}
|
|
838
1100
|
),
|
|
839
|
-
/* @__PURE__ */
|
|
840
|
-
|
|
1101
|
+
/* @__PURE__ */ jsx5(
|
|
1102
|
+
SelectField,
|
|
841
1103
|
{
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
1104
|
+
hideLabel: true,
|
|
1105
|
+
label: `${label} \u2014 ${messages["datetime.month"]}`,
|
|
1106
|
+
value: String(parts.month),
|
|
1107
|
+
options: monthOptions,
|
|
1108
|
+
onChange: (next) => change({ month: Number(next) }),
|
|
1109
|
+
searchable: false,
|
|
1110
|
+
className: "min-w-36"
|
|
847
1111
|
}
|
|
848
1112
|
),
|
|
849
|
-
/* @__PURE__ */
|
|
850
|
-
|
|
1113
|
+
/* @__PURE__ */ jsx5(
|
|
1114
|
+
SelectField,
|
|
851
1115
|
{
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
1116
|
+
hideLabel: true,
|
|
1117
|
+
label: `${label} \u2014 ${messages["datetime.day"]}`,
|
|
1118
|
+
value: String(Math.min(parts.day, days)),
|
|
1119
|
+
options: dayOptions,
|
|
1120
|
+
onChange: (next) => change({ day: Number(next) }),
|
|
1121
|
+
searchable: false,
|
|
1122
|
+
className: "min-w-20"
|
|
857
1123
|
}
|
|
858
1124
|
),
|
|
859
|
-
/* @__PURE__ */
|
|
1125
|
+
/* @__PURE__ */ jsx5(
|
|
860
1126
|
"input",
|
|
861
1127
|
{
|
|
862
1128
|
type: "time",
|
|
@@ -916,32 +1182,18 @@ function formatDateTimeLocalInTimeZone(instant, timeZone) {
|
|
|
916
1182
|
|
|
917
1183
|
// src/components/SidePanel.tsx
|
|
918
1184
|
import { X } from "lucide-react";
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
var BUTTON_BASE = "inline-flex items-center justify-center gap-2 rounded-lg px-3 text-sm font-medium min-h-11 transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 focus-visible:ring-offset-2 dark:focus-visible:ring-offset-gray-900 disabled:opacity-50 disabled:pointer-events-none";
|
|
922
|
-
var BUTTON_PRIMARY = `${BUTTON_BASE} bg-brand-600 text-white hover:bg-brand-700`;
|
|
923
|
-
var BUTTON_SECONDARY = `${BUTTON_BASE} border border-gray-300 bg-white text-gray-700 hover:bg-gray-50 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-200 dark:hover:bg-gray-800`;
|
|
924
|
-
var BUTTON_GHOST = `${BUTTON_BASE} text-gray-600 hover:bg-gray-100 dark:text-gray-400 dark:hover:bg-gray-800`;
|
|
925
|
-
var BUTTON_DANGER = `${BUTTON_BASE} text-red-700 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-950/40`;
|
|
926
|
-
var ICON_BUTTON = `${BUTTON_SECONDARY} min-w-11 px-0`;
|
|
927
|
-
var FIELD_CONTROL = "min-h-11 rounded-lg border border-gray-300 bg-white px-3 text-sm text-gray-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-500 dark:border-gray-700 dark:bg-gray-900 dark:text-gray-100";
|
|
928
|
-
var FIELD_LABEL = "text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400";
|
|
929
|
-
var SURFACE_BORDER = "rounded-xl border border-gray-200 dark:border-gray-800";
|
|
930
|
-
var ROW_STRIPE = "bg-gray-50 dark:bg-gray-800/40";
|
|
931
|
-
|
|
932
|
-
// src/components/SidePanel.tsx
|
|
933
|
-
import { useEffect } from "react";
|
|
934
|
-
import { Fragment, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1185
|
+
import { useEffect as useEffect2 } from "react";
|
|
1186
|
+
import { Fragment, jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
935
1187
|
function SidePanel({ title, closeLabel, onClose, headerActions, children }) {
|
|
936
|
-
|
|
1188
|
+
useEffect2(() => {
|
|
937
1189
|
function handleKeyDown(event) {
|
|
938
1190
|
if (event.key === "Escape") onClose();
|
|
939
1191
|
}
|
|
940
1192
|
window.addEventListener("keydown", handleKeyDown);
|
|
941
1193
|
return () => window.removeEventListener("keydown", handleKeyDown);
|
|
942
1194
|
}, [onClose]);
|
|
943
|
-
return /* @__PURE__ */
|
|
944
|
-
/* @__PURE__ */
|
|
1195
|
+
return /* @__PURE__ */ jsxs4(Fragment, { children: [
|
|
1196
|
+
/* @__PURE__ */ jsx6(
|
|
945
1197
|
"button",
|
|
946
1198
|
{
|
|
947
1199
|
type: "button",
|
|
@@ -951,29 +1203,29 @@ function SidePanel({ title, closeLabel, onClose, headerActions, children }) {
|
|
|
951
1203
|
className: "absolute inset-0 z-10 bg-gray-900/30 backdrop-blur-[1px] wide:hidden"
|
|
952
1204
|
}
|
|
953
1205
|
),
|
|
954
|
-
/* @__PURE__ */
|
|
1206
|
+
/* @__PURE__ */ jsxs4(
|
|
955
1207
|
"section",
|
|
956
1208
|
{
|
|
957
1209
|
"aria-label": title,
|
|
958
1210
|
className: "absolute inset-y-0 right-0 z-20 flex w-full max-w-full flex-col bg-white shadow-2xl desktop:w-[28rem] dark:bg-gray-900 wide:static wide:z-auto wide:shrink-0 wide:border-l wide:border-gray-200 wide:shadow-none wide:dark:border-gray-700",
|
|
959
1211
|
children: [
|
|
960
|
-
/* @__PURE__ */
|
|
961
|
-
/* @__PURE__ */
|
|
1212
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2 border-b border-gray-200 px-4 py-3 dark:border-gray-700", children: [
|
|
1213
|
+
/* @__PURE__ */ jsx6("h2", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100 mr-auto", children: title }),
|
|
962
1214
|
headerActions,
|
|
963
|
-
/* @__PURE__ */
|
|
1215
|
+
/* @__PURE__ */ jsxs4(
|
|
964
1216
|
"button",
|
|
965
1217
|
{
|
|
966
1218
|
type: "button",
|
|
967
1219
|
onClick: onClose,
|
|
968
1220
|
className: BUTTON_GHOST,
|
|
969
1221
|
children: [
|
|
970
|
-
/* @__PURE__ */
|
|
1222
|
+
/* @__PURE__ */ jsx6(X, { "aria-hidden": "true", className: "w-4 h-4" }),
|
|
971
1223
|
closeLabel
|
|
972
1224
|
]
|
|
973
1225
|
}
|
|
974
1226
|
)
|
|
975
1227
|
] }),
|
|
976
|
-
/* @__PURE__ */
|
|
1228
|
+
/* @__PURE__ */ jsx6("div", { className: "min-h-0 flex-1 overflow-y-auto p-4", children })
|
|
977
1229
|
]
|
|
978
1230
|
}
|
|
979
1231
|
)
|
|
@@ -981,7 +1233,7 @@ function SidePanel({ title, closeLabel, onClose, headerActions, children }) {
|
|
|
981
1233
|
}
|
|
982
1234
|
|
|
983
1235
|
// src/components/BookingDrawer.tsx
|
|
984
|
-
import { Fragment as Fragment2, jsx as
|
|
1236
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
985
1237
|
var BROWSER_TIME_ZONE = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
986
1238
|
var ACTION_BUTTON_CLASS = "min-h-11 px-3 rounded-lg text-sm font-medium border border-gray-300 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800";
|
|
987
1239
|
var PRIMARY_BUTTON_CLASS = "min-h-11 px-3 rounded-lg text-sm font-medium bg-brand-600 text-white hover:bg-brand-700";
|
|
@@ -996,12 +1248,12 @@ function BookingDrawer({ booking, resourceTimezone, onClose }) {
|
|
|
996
1248
|
const cancelBooking = useCancelBooking();
|
|
997
1249
|
const rescheduleBooking = useRescheduleBooking();
|
|
998
1250
|
const timezone = resourceTimezone ?? BROWSER_TIME_ZONE;
|
|
999
|
-
const [isCancelling, setIsCancelling] =
|
|
1000
|
-
const [cancelledBy, setCancelledBy] =
|
|
1001
|
-
const [cancellationReason, setCancellationReason] =
|
|
1002
|
-
const [isRescheduling, setIsRescheduling] =
|
|
1003
|
-
const [start, setStart] =
|
|
1004
|
-
const [end, setEnd] =
|
|
1251
|
+
const [isCancelling, setIsCancelling] = useState2(false);
|
|
1252
|
+
const [cancelledBy, setCancelledBy] = useState2("");
|
|
1253
|
+
const [cancellationReason, setCancellationReason] = useState2("");
|
|
1254
|
+
const [isRescheduling, setIsRescheduling] = useState2(false);
|
|
1255
|
+
const [start, setStart] = useState2(() => formatDateTimeLocalInTimeZone(booking.startsAt, timezone));
|
|
1256
|
+
const [end, setEnd] = useState2(() => formatDateTimeLocalInTimeZone(booking.endsAt, timezone));
|
|
1005
1257
|
async function handleCancel() {
|
|
1006
1258
|
if (!cancelledBy) return;
|
|
1007
1259
|
try {
|
|
@@ -1029,31 +1281,31 @@ function BookingDrawer({ booking, resourceTimezone, onClose }) {
|
|
|
1029
1281
|
}
|
|
1030
1282
|
}
|
|
1031
1283
|
const isTerminal = booking.status === BOOKING_STATUS2.CANCELLED || booking.status === BOOKING_STATUS2.COMPLETED || booking.status === BOOKING_STATUS2.NO_SHOW;
|
|
1032
|
-
return /* @__PURE__ */
|
|
1033
|
-
/* @__PURE__ */
|
|
1034
|
-
/* @__PURE__ */
|
|
1035
|
-
/* @__PURE__ */
|
|
1036
|
-
/* @__PURE__ */
|
|
1284
|
+
return /* @__PURE__ */ jsx7(SidePanel, { title: messages["booking.detailTitle"], closeLabel: messages["common.close"], onClose, children: /* @__PURE__ */ jsxs5("div", { className: "space-y-4", children: [
|
|
1285
|
+
/* @__PURE__ */ jsxs5("div", { children: [
|
|
1286
|
+
/* @__PURE__ */ jsx7("p", { className: "text-base font-semibold text-gray-900 dark:text-gray-100", children: booking.title }),
|
|
1287
|
+
/* @__PURE__ */ jsx7("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: messages[`booking.status.${booking.status === "no_show" ? "noShow" : booking.status}`] }),
|
|
1288
|
+
/* @__PURE__ */ jsxs5("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: [
|
|
1037
1289
|
booking.startsAt.toLocaleString(locale),
|
|
1038
1290
|
" \u2192 ",
|
|
1039
1291
|
booking.endsAt.toLocaleString(locale)
|
|
1040
1292
|
] })
|
|
1041
1293
|
] }),
|
|
1042
|
-
(confirmBooking.isError || completeBooking.isError || markNoShow.isError || cancelBooking.isError || rescheduleBooking.isError) && /* @__PURE__ */
|
|
1043
|
-
!isTerminal && /* @__PURE__ */
|
|
1044
|
-
booking.status === BOOKING_STATUS2.REQUESTED && /* @__PURE__ */
|
|
1045
|
-
booking.status === BOOKING_STATUS2.CONFIRMED && /* @__PURE__ */
|
|
1046
|
-
/* @__PURE__ */
|
|
1047
|
-
/* @__PURE__ */
|
|
1048
|
-
/* @__PURE__ */
|
|
1294
|
+
(confirmBooking.isError || completeBooking.isError || markNoShow.isError || cancelBooking.isError || rescheduleBooking.isError) && /* @__PURE__ */ jsx7("p", { role: "alert", className: "text-sm text-red-700 bg-red-50 rounded-lg px-3 py-2", children: messages["common.actionFailure"] }),
|
|
1295
|
+
!isTerminal && /* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap gap-2", children: [
|
|
1296
|
+
booking.status === BOOKING_STATUS2.REQUESTED && /* @__PURE__ */ jsx7("button", { type: "button", onClick: () => confirmBooking.mutate(booking.id), className: PRIMARY_BUTTON_CLASS, children: messages["booking.confirm"] }),
|
|
1297
|
+
booking.status === BOOKING_STATUS2.CONFIRMED && /* @__PURE__ */ jsxs5(Fragment2, { children: [
|
|
1298
|
+
/* @__PURE__ */ jsx7("button", { type: "button", onClick: () => completeBooking.mutate(booking.id), className: ACTION_BUTTON_CLASS, children: messages["booking.complete"] }),
|
|
1299
|
+
/* @__PURE__ */ jsx7("button", { type: "button", onClick: () => markNoShow.mutate(booking.id), className: ACTION_BUTTON_CLASS, children: messages["booking.markNoShow"] }),
|
|
1300
|
+
/* @__PURE__ */ jsx7("button", { type: "button", onClick: () => setIsRescheduling(true), className: ACTION_BUTTON_CLASS, children: messages["booking.reschedule"] })
|
|
1049
1301
|
] }),
|
|
1050
|
-
/* @__PURE__ */
|
|
1051
|
-
/* @__PURE__ */
|
|
1302
|
+
/* @__PURE__ */ jsxs5("button", { type: "button", onClick: () => setIsCancelling(true), className: DANGER_BUTTON_CLASS, children: [
|
|
1303
|
+
/* @__PURE__ */ jsx7(XCircle, { "aria-hidden": "true", className: "w-4 h-4" }),
|
|
1052
1304
|
messages["booking.cancel"]
|
|
1053
1305
|
] })
|
|
1054
1306
|
] }),
|
|
1055
|
-
isRescheduling && /* @__PURE__ */
|
|
1056
|
-
/* @__PURE__ */
|
|
1307
|
+
isRescheduling && /* @__PURE__ */ jsxs5("div", { className: "space-y-2 rounded-lg border border-gray-200 dark:border-gray-700 p-3", children: [
|
|
1308
|
+
/* @__PURE__ */ jsx7(
|
|
1057
1309
|
DateTimeField,
|
|
1058
1310
|
{
|
|
1059
1311
|
label: messages["booking.rescheduleStart"],
|
|
@@ -1061,11 +1313,11 @@ function BookingDrawer({ booking, resourceTimezone, onClose }) {
|
|
|
1061
1313
|
onChange: setStart
|
|
1062
1314
|
}
|
|
1063
1315
|
),
|
|
1064
|
-
/* @__PURE__ */
|
|
1065
|
-
/* @__PURE__ */
|
|
1316
|
+
/* @__PURE__ */ jsx7(DateTimeField, { label: messages["booking.rescheduleEnd"], value: end, onChange: setEnd }),
|
|
1317
|
+
/* @__PURE__ */ jsx7("button", { type: "button", onClick: handleReschedule, disabled: rescheduleBooking.isPending, className: PRIMARY_BUTTON_CLASS, children: messages["common.save"] })
|
|
1066
1318
|
] }),
|
|
1067
|
-
isCancelling && /* @__PURE__ */
|
|
1068
|
-
/* @__PURE__ */
|
|
1319
|
+
isCancelling && /* @__PURE__ */ jsxs5("div", { className: "space-y-2 rounded-lg border border-gray-200 dark:border-gray-700 p-3", children: [
|
|
1320
|
+
/* @__PURE__ */ jsx7(
|
|
1069
1321
|
"input",
|
|
1070
1322
|
{
|
|
1071
1323
|
type: "text",
|
|
@@ -1075,7 +1327,7 @@ function BookingDrawer({ booking, resourceTimezone, onClose }) {
|
|
|
1075
1327
|
className: INPUT_CLASS
|
|
1076
1328
|
}
|
|
1077
1329
|
),
|
|
1078
|
-
/* @__PURE__ */
|
|
1330
|
+
/* @__PURE__ */ jsx7(
|
|
1079
1331
|
"input",
|
|
1080
1332
|
{
|
|
1081
1333
|
type: "text",
|
|
@@ -1085,8 +1337,8 @@ function BookingDrawer({ booking, resourceTimezone, onClose }) {
|
|
|
1085
1337
|
className: INPUT_CLASS
|
|
1086
1338
|
}
|
|
1087
1339
|
),
|
|
1088
|
-
/* @__PURE__ */
|
|
1089
|
-
/* @__PURE__ */
|
|
1340
|
+
/* @__PURE__ */ jsxs5("button", { type: "button", onClick: handleCancel, disabled: cancelBooking.isPending, className: DANGER_BUTTON_CLASS, children: [
|
|
1341
|
+
/* @__PURE__ */ jsx7(XCircle, { "aria-hidden": "true", className: "w-4 h-4" }),
|
|
1090
1342
|
messages["booking.cancel"]
|
|
1091
1343
|
] })
|
|
1092
1344
|
] })
|
|
@@ -1094,28 +1346,30 @@ function BookingDrawer({ booking, resourceTimezone, onClose }) {
|
|
|
1094
1346
|
}
|
|
1095
1347
|
|
|
1096
1348
|
// src/components/ResourceSelect.tsx
|
|
1097
|
-
import {
|
|
1349
|
+
import { useMemo as useMemo4 } from "react";
|
|
1350
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
1098
1351
|
function ResourceSelect({ label, emptyOptionLabel, resources, value, onChange }) {
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1352
|
+
const options = useMemo4(
|
|
1353
|
+
() => resources.map((resource) => ({ value: resource.id, label: resource.name })),
|
|
1354
|
+
[resources]
|
|
1355
|
+
);
|
|
1356
|
+
return /* @__PURE__ */ jsx8(
|
|
1357
|
+
SelectField,
|
|
1358
|
+
{
|
|
1359
|
+
label,
|
|
1360
|
+
emptyOptionLabel,
|
|
1361
|
+
options,
|
|
1362
|
+
value,
|
|
1363
|
+
onChange,
|
|
1364
|
+
searchable: true,
|
|
1365
|
+
className: "min-w-52"
|
|
1366
|
+
}
|
|
1367
|
+
);
|
|
1114
1368
|
}
|
|
1115
1369
|
|
|
1116
1370
|
// src/components/StateFeedback.tsx
|
|
1117
1371
|
import { AlertTriangle } from "lucide-react";
|
|
1118
|
-
import { jsx as
|
|
1372
|
+
import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1119
1373
|
function ErrorBanner({ message }) {
|
|
1120
1374
|
return /* @__PURE__ */ jsxs6(
|
|
1121
1375
|
"p",
|
|
@@ -1123,7 +1377,7 @@ function ErrorBanner({ message }) {
|
|
|
1123
1377
|
role: "alert",
|
|
1124
1378
|
className: "flex items-start gap-2 rounded-lg border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-800 dark:border-red-900 dark:bg-red-950/50 dark:text-red-200",
|
|
1125
1379
|
children: [
|
|
1126
|
-
/* @__PURE__ */
|
|
1380
|
+
/* @__PURE__ */ jsx9(AlertTriangle, { "aria-hidden": "true", className: "mt-0.5 h-4 w-4 shrink-0" }),
|
|
1127
1381
|
message
|
|
1128
1382
|
]
|
|
1129
1383
|
}
|
|
@@ -1131,18 +1385,18 @@ function ErrorBanner({ message }) {
|
|
|
1131
1385
|
}
|
|
1132
1386
|
function EmptyState({ icon: Icon, title, hint, action }) {
|
|
1133
1387
|
return /* @__PURE__ */ jsxs6("div", { className: `${SURFACE_BORDER} flex flex-col items-center gap-2 px-6 py-12 text-center`, children: [
|
|
1134
|
-
/* @__PURE__ */
|
|
1135
|
-
/* @__PURE__ */
|
|
1136
|
-
hint && /* @__PURE__ */
|
|
1137
|
-
action && /* @__PURE__ */
|
|
1388
|
+
/* @__PURE__ */ jsx9("span", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-gray-100 text-gray-400 dark:bg-gray-800 dark:text-gray-500", children: /* @__PURE__ */ jsx9(Icon, { "aria-hidden": "true", className: "h-6 w-6" }) }),
|
|
1389
|
+
/* @__PURE__ */ jsx9("p", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: title }),
|
|
1390
|
+
hint && /* @__PURE__ */ jsx9("p", { className: "max-w-sm text-sm text-gray-500 dark:text-gray-400", children: hint }),
|
|
1391
|
+
action && /* @__PURE__ */ jsx9("div", { className: "mt-2", children: action })
|
|
1138
1392
|
] });
|
|
1139
1393
|
}
|
|
1140
1394
|
var SKELETON_ROW = "h-11 animate-pulse rounded-lg bg-gray-100 dark:bg-gray-800";
|
|
1141
1395
|
function ListSkeleton({ label, rows = 5 }) {
|
|
1142
|
-
return /* @__PURE__ */
|
|
1396
|
+
return /* @__PURE__ */ jsx9("div", { "aria-busy": "true", "aria-label": label, role: "status", className: "space-y-2", children: Array.from({ length: rows }, (_row, index) => /* @__PURE__ */ jsx9("div", { className: SKELETON_ROW }, index)) });
|
|
1143
1397
|
}
|
|
1144
1398
|
function BlockSkeleton({ label }) {
|
|
1145
|
-
return /* @__PURE__ */
|
|
1399
|
+
return /* @__PURE__ */ jsx9(
|
|
1146
1400
|
"div",
|
|
1147
1401
|
{
|
|
1148
1402
|
"aria-busy": "true",
|
|
@@ -1154,7 +1408,7 @@ function BlockSkeleton({ label }) {
|
|
|
1154
1408
|
}
|
|
1155
1409
|
|
|
1156
1410
|
// src/workspace/AgendaArea.tsx
|
|
1157
|
-
import { jsx as
|
|
1411
|
+
import { jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1158
1412
|
var DAY_IN_MS = 24 * 60 * 6e4;
|
|
1159
1413
|
var NOW_TICK_MS = 6e4;
|
|
1160
1414
|
function startOfWeek(date, weekStartsOn) {
|
|
@@ -1177,8 +1431,8 @@ function formatPeriod(days, locale) {
|
|
|
1177
1431
|
return `${first.toLocaleDateString(locale, short)} \u2013 ${last.toLocaleDateString(locale, { ...short, year: "numeric" })}`;
|
|
1178
1432
|
}
|
|
1179
1433
|
function useNow() {
|
|
1180
|
-
const [now, setNow] =
|
|
1181
|
-
|
|
1434
|
+
const [now, setNow] = useState3(() => /* @__PURE__ */ new Date());
|
|
1435
|
+
useEffect3(() => {
|
|
1182
1436
|
const timer = window.setInterval(() => setNow(/* @__PURE__ */ new Date()), NOW_TICK_MS);
|
|
1183
1437
|
return () => window.clearInterval(timer);
|
|
1184
1438
|
}, []);
|
|
@@ -1189,10 +1443,10 @@ function AgendaArea() {
|
|
|
1189
1443
|
const messages = resolveSchedulingMessages(locale);
|
|
1190
1444
|
const now = useNow();
|
|
1191
1445
|
const { data: resourcesPage } = useResources({ active: true, pageSize: MAX_PAGE_SIZE });
|
|
1192
|
-
const [resourceId, setResourceId] =
|
|
1193
|
-
const [view, setView] =
|
|
1194
|
-
const [anchorDate, setAnchorDate] =
|
|
1195
|
-
const [selectedBookingId, setSelectedBookingId] =
|
|
1446
|
+
const [resourceId, setResourceId] = useState3("");
|
|
1447
|
+
const [view, setView] = useState3("day");
|
|
1448
|
+
const [anchorDate, setAnchorDate] = useState3(() => /* @__PURE__ */ new Date());
|
|
1449
|
+
const [selectedBookingId, setSelectedBookingId] = useState3(void 0);
|
|
1196
1450
|
const resources = resourcesPage?.data ?? [];
|
|
1197
1451
|
const days = buildVisibleDays(anchorDate, view, weekStartsOn);
|
|
1198
1452
|
const from = days[0];
|
|
@@ -1211,13 +1465,13 @@ function AgendaArea() {
|
|
|
1211
1465
|
}
|
|
1212
1466
|
function renderViewOption(value, label) {
|
|
1213
1467
|
const isActive = view === value;
|
|
1214
|
-
return /* @__PURE__ */
|
|
1468
|
+
return /* @__PURE__ */ jsx10(
|
|
1215
1469
|
"button",
|
|
1216
1470
|
{
|
|
1217
1471
|
type: "button",
|
|
1218
1472
|
onClick: () => setView(value),
|
|
1219
1473
|
"aria-pressed": isActive,
|
|
1220
|
-
className: `${BUTTON_BASE} flex-1 ${isActive ? "bg-white text-brand-700 shadow-sm dark:bg-gray-900 dark:text-brand-
|
|
1474
|
+
className: `${BUTTON_BASE} flex-1 ${isActive ? "bg-white text-brand-700 shadow-sm dark:bg-gray-900 dark:text-brand-400" : "text-gray-600 dark:text-gray-400"}`,
|
|
1221
1475
|
children: label
|
|
1222
1476
|
}
|
|
1223
1477
|
);
|
|
@@ -1225,7 +1479,7 @@ function AgendaArea() {
|
|
|
1225
1479
|
return /* @__PURE__ */ jsxs7("div", { className: "flex flex-1 min-h-0 min-w-0", children: [
|
|
1226
1480
|
/* @__PURE__ */ jsxs7("div", { className: "flex flex-1 min-h-0 min-w-0 flex-col gap-4 overflow-y-auto p-4", children: [
|
|
1227
1481
|
/* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap items-end gap-3", children: [
|
|
1228
|
-
/* @__PURE__ */
|
|
1482
|
+
/* @__PURE__ */ jsx10(
|
|
1229
1483
|
ResourceSelect,
|
|
1230
1484
|
{
|
|
1231
1485
|
label: messages["agenda.resourceLabel"],
|
|
@@ -1236,29 +1490,29 @@ function AgendaArea() {
|
|
|
1236
1490
|
}
|
|
1237
1491
|
),
|
|
1238
1492
|
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1", children: [
|
|
1239
|
-
/* @__PURE__ */
|
|
1493
|
+
/* @__PURE__ */ jsx10(
|
|
1240
1494
|
"button",
|
|
1241
1495
|
{
|
|
1242
1496
|
type: "button",
|
|
1243
1497
|
"aria-label": messages["agenda.previous"],
|
|
1244
1498
|
onClick: () => shiftAnchor(-1),
|
|
1245
1499
|
className: ICON_BUTTON,
|
|
1246
|
-
children: /* @__PURE__ */
|
|
1500
|
+
children: /* @__PURE__ */ jsx10(ChevronLeft, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
1247
1501
|
}
|
|
1248
1502
|
),
|
|
1249
|
-
/* @__PURE__ */
|
|
1250
|
-
/* @__PURE__ */
|
|
1503
|
+
/* @__PURE__ */ jsx10("button", { type: "button", onClick: () => setAnchorDate(/* @__PURE__ */ new Date()), className: BUTTON_SECONDARY, children: messages["agenda.today"] }),
|
|
1504
|
+
/* @__PURE__ */ jsx10(
|
|
1251
1505
|
"button",
|
|
1252
1506
|
{
|
|
1253
1507
|
type: "button",
|
|
1254
1508
|
"aria-label": messages["agenda.next"],
|
|
1255
1509
|
onClick: () => shiftAnchor(1),
|
|
1256
1510
|
className: ICON_BUTTON,
|
|
1257
|
-
children: /* @__PURE__ */
|
|
1511
|
+
children: /* @__PURE__ */ jsx10(ChevronRight, { "aria-hidden": "true", className: "h-4 w-4" })
|
|
1258
1512
|
}
|
|
1259
1513
|
)
|
|
1260
1514
|
] }),
|
|
1261
|
-
/* @__PURE__ */
|
|
1515
|
+
/* @__PURE__ */ jsx10(
|
|
1262
1516
|
"p",
|
|
1263
1517
|
{
|
|
1264
1518
|
"aria-live": "polite",
|
|
@@ -1267,18 +1521,18 @@ function AgendaArea() {
|
|
|
1267
1521
|
}
|
|
1268
1522
|
),
|
|
1269
1523
|
/* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-1", children: [
|
|
1270
|
-
/* @__PURE__ */
|
|
1524
|
+
/* @__PURE__ */ jsx10("span", { className: FIELD_LABEL, children: messages["agenda.viewLabel"] }),
|
|
1271
1525
|
/* @__PURE__ */ jsxs7("div", { className: "flex gap-1 rounded-lg bg-gray-100 p-1 dark:bg-gray-800", children: [
|
|
1272
1526
|
renderViewOption("day", messages["agenda.viewDay"]),
|
|
1273
1527
|
renderViewOption("week", messages["agenda.viewWeek"])
|
|
1274
1528
|
] })
|
|
1275
1529
|
] })
|
|
1276
1530
|
] }),
|
|
1277
|
-
isError && /* @__PURE__ */
|
|
1278
|
-
bookingsPage && bookingsPage.total > bookingsPage.data.length && /* @__PURE__ */
|
|
1279
|
-
isLoading && /* @__PURE__ */
|
|
1280
|
-
!isLoading && !isError && visibleBookings.length === 0 && /* @__PURE__ */
|
|
1281
|
-
!isLoading && visibleBookings.length > 0 && /* @__PURE__ */
|
|
1531
|
+
isError && /* @__PURE__ */ jsx10(ErrorBanner, { message: messages["common.loadFailure"] }),
|
|
1532
|
+
bookingsPage && bookingsPage.total > bookingsPage.data.length && /* @__PURE__ */ jsx10("p", { className: "rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-800 dark:border-amber-900 dark:bg-amber-950/50 dark:text-amber-200", children: messages["agenda.moreResults"] }),
|
|
1533
|
+
isLoading && /* @__PURE__ */ jsx10(BlockSkeleton, { label: messages["common.loading"] }),
|
|
1534
|
+
!isLoading && !isError && visibleBookings.length === 0 && /* @__PURE__ */ jsx10(EmptyState, { icon: CalendarDays, title: messages["agenda.emptyTitle"], hint: messages["agenda.empty"] }),
|
|
1535
|
+
!isLoading && visibleBookings.length > 0 && /* @__PURE__ */ jsx10(
|
|
1282
1536
|
AgendaGrid,
|
|
1283
1537
|
{
|
|
1284
1538
|
bookings: visibleBookings,
|
|
@@ -1288,7 +1542,7 @@ function AgendaArea() {
|
|
|
1288
1542
|
}
|
|
1289
1543
|
)
|
|
1290
1544
|
] }),
|
|
1291
|
-
selectedBooking && /* @__PURE__ */
|
|
1545
|
+
selectedBooking && /* @__PURE__ */ jsx10(
|
|
1292
1546
|
BookingDrawer,
|
|
1293
1547
|
{
|
|
1294
1548
|
booking: selectedBooking,
|
|
@@ -1302,13 +1556,13 @@ function AgendaArea() {
|
|
|
1302
1556
|
|
|
1303
1557
|
// src/workspace/AvailabilityArea.tsx
|
|
1304
1558
|
import { Clock } from "lucide-react";
|
|
1305
|
-
import { useState as
|
|
1559
|
+
import { useState as useState6 } from "react";
|
|
1306
1560
|
import { MAX_PAGE_SIZE as MAX_PAGE_SIZE2 } from "@adatechnology/scheduling-contracts";
|
|
1307
1561
|
|
|
1308
1562
|
// src/components/WeeklyRulesEditor.tsx
|
|
1309
1563
|
import { Plus, Trash2 } from "lucide-react";
|
|
1310
|
-
import { useState as
|
|
1311
|
-
import { jsx as
|
|
1564
|
+
import { useState as useState4, useMemo as useMemo5 } from "react";
|
|
1565
|
+
import { jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1312
1566
|
var WEEKDAYS = [0, 1, 2, 3, 4, 5, 6];
|
|
1313
1567
|
function toDraft(rule) {
|
|
1314
1568
|
return { weekday: rule.weekday, startsAtLocal: rule.startsAtLocal, endsAtLocal: rule.endsAtLocal };
|
|
@@ -1319,9 +1573,16 @@ function createEmptyDraft() {
|
|
|
1319
1573
|
function WeeklyRulesEditor({ resourceId }) {
|
|
1320
1574
|
const { locale } = useSchedulingConfig();
|
|
1321
1575
|
const messages = resolveSchedulingMessages(locale);
|
|
1576
|
+
const weekdayOptions = useMemo5(
|
|
1577
|
+
() => WEEKDAYS.map((weekday) => ({
|
|
1578
|
+
value: String(weekday),
|
|
1579
|
+
label: messages[`availability.weekday.${weekday}`]
|
|
1580
|
+
})),
|
|
1581
|
+
[messages]
|
|
1582
|
+
);
|
|
1322
1583
|
const { data, isSuccess: isRulesLoaded, isError: isRulesLoadError } = useAvailabilityRules(resourceId);
|
|
1323
1584
|
const setAvailabilityRules = useSetAvailabilityRules();
|
|
1324
|
-
const [draft, setDraft] =
|
|
1585
|
+
const [draft, setDraft] = useState4(void 0);
|
|
1325
1586
|
const rules = draft ?? (data ? data.map(toDraft) : []);
|
|
1326
1587
|
function updateRule(index, patch) {
|
|
1327
1588
|
setDraft(rules.map((rule, ruleIndex) => ruleIndex === index ? { ...rule, ...patch } : rule));
|
|
@@ -1344,31 +1605,31 @@ function WeeklyRulesEditor({ resourceId }) {
|
|
|
1344
1605
|
}
|
|
1345
1606
|
if (isRulesLoadError) {
|
|
1346
1607
|
return /* @__PURE__ */ jsxs8("section", { className: "space-y-3", children: [
|
|
1347
|
-
/* @__PURE__ */
|
|
1348
|
-
/* @__PURE__ */
|
|
1608
|
+
/* @__PURE__ */ jsx11("h3", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: messages["availability.weeklyRulesTitle"] }),
|
|
1609
|
+
/* @__PURE__ */ jsx11("p", { role: "alert", className: "text-sm text-red-700 bg-red-50 rounded-lg px-3 py-2", children: messages["common.loadFailure"] })
|
|
1349
1610
|
] });
|
|
1350
1611
|
}
|
|
1351
1612
|
if (!isRulesLoaded) {
|
|
1352
1613
|
return /* @__PURE__ */ jsxs8("section", { className: "space-y-3", children: [
|
|
1353
|
-
/* @__PURE__ */
|
|
1354
|
-
/* @__PURE__ */
|
|
1614
|
+
/* @__PURE__ */ jsx11("h3", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: messages["availability.weeklyRulesTitle"] }),
|
|
1615
|
+
/* @__PURE__ */ jsx11("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: messages["common.loading"] })
|
|
1355
1616
|
] });
|
|
1356
1617
|
}
|
|
1357
1618
|
return /* @__PURE__ */ jsxs8("section", { className: "space-y-3", children: [
|
|
1358
|
-
/* @__PURE__ */
|
|
1359
|
-
setAvailabilityRules.isError && /* @__PURE__ */
|
|
1360
|
-
/* @__PURE__ */
|
|
1361
|
-
/* @__PURE__ */
|
|
1362
|
-
|
|
1619
|
+
/* @__PURE__ */ jsx11("h3", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: messages["availability.weeklyRulesTitle"] }),
|
|
1620
|
+
setAvailabilityRules.isError && /* @__PURE__ */ jsx11("p", { role: "alert", className: "text-sm text-red-700 bg-red-50 rounded-lg px-3 py-2", children: messages["common.actionFailure"] }),
|
|
1621
|
+
/* @__PURE__ */ jsx11("ul", { className: "space-y-2", children: rules.map((rule, index) => /* @__PURE__ */ jsxs8("li", { className: "flex items-center gap-2", children: [
|
|
1622
|
+
/* @__PURE__ */ jsx11(
|
|
1623
|
+
SelectField,
|
|
1363
1624
|
{
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1625
|
+
hideLabel: true,
|
|
1626
|
+
label: messages["availability.weekdayLabel"],
|
|
1627
|
+
value: String(rule.weekday),
|
|
1628
|
+
options: weekdayOptions,
|
|
1629
|
+
onChange: (next) => updateRule(index, { weekday: Number(next) })
|
|
1369
1630
|
}
|
|
1370
1631
|
),
|
|
1371
|
-
/* @__PURE__ */
|
|
1632
|
+
/* @__PURE__ */ jsx11(
|
|
1372
1633
|
"input",
|
|
1373
1634
|
{
|
|
1374
1635
|
"aria-label": messages["availability.startsAtLocal"],
|
|
@@ -1378,7 +1639,7 @@ function WeeklyRulesEditor({ resourceId }) {
|
|
|
1378
1639
|
className: "min-h-11 rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-2 text-sm"
|
|
1379
1640
|
}
|
|
1380
1641
|
),
|
|
1381
|
-
/* @__PURE__ */
|
|
1642
|
+
/* @__PURE__ */ jsx11(
|
|
1382
1643
|
"input",
|
|
1383
1644
|
{
|
|
1384
1645
|
"aria-label": messages["availability.endsAtLocal"],
|
|
@@ -1388,14 +1649,14 @@ function WeeklyRulesEditor({ resourceId }) {
|
|
|
1388
1649
|
className: "min-h-11 rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-2 text-sm"
|
|
1389
1650
|
}
|
|
1390
1651
|
),
|
|
1391
|
-
/* @__PURE__ */
|
|
1652
|
+
/* @__PURE__ */ jsx11(
|
|
1392
1653
|
"button",
|
|
1393
1654
|
{
|
|
1394
1655
|
type: "button",
|
|
1395
1656
|
onClick: () => removeRule(index),
|
|
1396
1657
|
"aria-label": messages["availability.removeRule"],
|
|
1397
1658
|
className: "min-h-11 min-w-11 inline-flex items-center justify-center rounded-lg text-red-700 hover:bg-red-50",
|
|
1398
|
-
children: /* @__PURE__ */
|
|
1659
|
+
children: /* @__PURE__ */ jsx11(Trash2, { "aria-hidden": "true", className: "w-4 h-4" })
|
|
1399
1660
|
}
|
|
1400
1661
|
)
|
|
1401
1662
|
] }, index)) }),
|
|
@@ -1407,12 +1668,12 @@ function WeeklyRulesEditor({ resourceId }) {
|
|
|
1407
1668
|
onClick: addRule,
|
|
1408
1669
|
className: "inline-flex items-center gap-2 min-h-11 px-3 py-2 rounded-lg text-sm font-medium border border-gray-300 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-800",
|
|
1409
1670
|
children: [
|
|
1410
|
-
/* @__PURE__ */
|
|
1671
|
+
/* @__PURE__ */ jsx11(Plus, { "aria-hidden": "true", className: "w-4 h-4" }),
|
|
1411
1672
|
messages["availability.addRule"]
|
|
1412
1673
|
]
|
|
1413
1674
|
}
|
|
1414
1675
|
),
|
|
1415
|
-
/* @__PURE__ */
|
|
1676
|
+
/* @__PURE__ */ jsx11(
|
|
1416
1677
|
"button",
|
|
1417
1678
|
{
|
|
1418
1679
|
type: "button",
|
|
@@ -1428,21 +1689,28 @@ function WeeklyRulesEditor({ resourceId }) {
|
|
|
1428
1689
|
|
|
1429
1690
|
// src/components/AvailabilityExceptionsEditor.tsx
|
|
1430
1691
|
import { Plus as Plus2, Trash2 as Trash22 } from "lucide-react";
|
|
1431
|
-
import { useState as
|
|
1692
|
+
import { useState as useState5, useMemo as useMemo6 } from "react";
|
|
1432
1693
|
import { AVAILABILITY_EXCEPTION_KIND } from "@adatechnology/scheduling-contracts";
|
|
1433
|
-
import { jsx as
|
|
1694
|
+
import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1434
1695
|
var SELECT_CLASS = "min-h-11 rounded-lg border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-900 px-2 text-sm";
|
|
1435
1696
|
function AvailabilityExceptionsEditor({ resourceId, timezone }) {
|
|
1436
1697
|
const { locale } = useSchedulingConfig();
|
|
1437
1698
|
const messages = resolveSchedulingMessages(locale);
|
|
1699
|
+
const kindOptions = useMemo6(
|
|
1700
|
+
() => [
|
|
1701
|
+
{ value: AVAILABILITY_EXCEPTION_KIND.BLOCK, label: messages["availability.exceptionKind.blocked"] },
|
|
1702
|
+
{ value: AVAILABILITY_EXCEPTION_KIND.EXTRA, label: messages["availability.exceptionKind.extra"] }
|
|
1703
|
+
],
|
|
1704
|
+
[messages]
|
|
1705
|
+
);
|
|
1438
1706
|
const { data, isLoading, isError: isLoadError } = useAvailabilityExceptions(resourceId);
|
|
1439
1707
|
const addException = useAddAvailabilityException();
|
|
1440
1708
|
const removeException = useRemoveAvailabilityException();
|
|
1441
1709
|
const emptyValue = () => formatDateTimeLocalInTimeZone(/* @__PURE__ */ new Date(), timezone);
|
|
1442
|
-
const [from, setFrom] =
|
|
1443
|
-
const [until, setUntil] =
|
|
1444
|
-
const [kind, setKind] =
|
|
1445
|
-
const [reason, setReason] =
|
|
1710
|
+
const [from, setFrom] = useState5(emptyValue);
|
|
1711
|
+
const [until, setUntil] = useState5(emptyValue);
|
|
1712
|
+
const [kind, setKind] = useState5(AVAILABILITY_EXCEPTION_KIND.BLOCK);
|
|
1713
|
+
const [reason, setReason] = useState5("");
|
|
1446
1714
|
async function handleAdd() {
|
|
1447
1715
|
if (!from || !until) return;
|
|
1448
1716
|
try {
|
|
@@ -1462,11 +1730,11 @@ function AvailabilityExceptionsEditor({ resourceId, timezone }) {
|
|
|
1462
1730
|
}
|
|
1463
1731
|
}
|
|
1464
1732
|
return /* @__PURE__ */ jsxs9("section", { className: "space-y-3", children: [
|
|
1465
|
-
/* @__PURE__ */
|
|
1466
|
-
(addException.isError || removeException.isError) && /* @__PURE__ */
|
|
1467
|
-
isLoadError && /* @__PURE__ */
|
|
1468
|
-
isLoading && /* @__PURE__ */
|
|
1469
|
-
/* @__PURE__ */
|
|
1733
|
+
/* @__PURE__ */ jsx12("h3", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: messages["availability.exceptionsTitle"] }),
|
|
1734
|
+
(addException.isError || removeException.isError) && /* @__PURE__ */ jsx12("p", { role: "alert", className: "text-sm text-red-700 bg-red-50 rounded-lg px-3 py-2", children: messages["common.actionFailure"] }),
|
|
1735
|
+
isLoadError && /* @__PURE__ */ jsx12("p", { role: "alert", className: "text-sm text-red-700 bg-red-50 rounded-lg px-3 py-2", children: messages["common.loadFailure"] }),
|
|
1736
|
+
isLoading && /* @__PURE__ */ jsx12("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: messages["common.loading"] }),
|
|
1737
|
+
/* @__PURE__ */ jsx12("ul", { className: "space-y-2", children: (data ?? []).map((exception) => /* @__PURE__ */ jsxs9("li", { className: "flex items-center gap-2 text-sm", children: [
|
|
1470
1738
|
/* @__PURE__ */ jsxs9("span", { className: "flex-1", children: [
|
|
1471
1739
|
messages[`availability.exceptionKind.${exception.kind === "block" ? "blocked" : "extra"}`],
|
|
1472
1740
|
" \u2014",
|
|
@@ -1476,19 +1744,19 @@ function AvailabilityExceptionsEditor({ resourceId, timezone }) {
|
|
|
1476
1744
|
exception.during.end.toLocaleString(locale),
|
|
1477
1745
|
exception.reason ? ` (${exception.reason})` : ""
|
|
1478
1746
|
] }),
|
|
1479
|
-
/* @__PURE__ */
|
|
1747
|
+
/* @__PURE__ */ jsx12(
|
|
1480
1748
|
"button",
|
|
1481
1749
|
{
|
|
1482
1750
|
type: "button",
|
|
1483
1751
|
onClick: () => removeException.mutate({ id: exception.id, resourceId }),
|
|
1484
1752
|
"aria-label": messages["availability.removeException"],
|
|
1485
1753
|
className: "min-h-11 min-w-11 inline-flex items-center justify-center rounded-lg text-red-700 hover:bg-red-50",
|
|
1486
|
-
children: /* @__PURE__ */
|
|
1754
|
+
children: /* @__PURE__ */ jsx12(Trash22, { "aria-hidden": "true", className: "w-4 h-4" })
|
|
1487
1755
|
}
|
|
1488
1756
|
)
|
|
1489
1757
|
] }, exception.id)) }),
|
|
1490
1758
|
/* @__PURE__ */ jsxs9("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1491
|
-
/* @__PURE__ */
|
|
1759
|
+
/* @__PURE__ */ jsx12(
|
|
1492
1760
|
DateTimeField,
|
|
1493
1761
|
{
|
|
1494
1762
|
label: messages["availability.exceptionFrom"],
|
|
@@ -1496,7 +1764,7 @@ function AvailabilityExceptionsEditor({ resourceId, timezone }) {
|
|
|
1496
1764
|
onChange: setFrom
|
|
1497
1765
|
}
|
|
1498
1766
|
),
|
|
1499
|
-
/* @__PURE__ */
|
|
1767
|
+
/* @__PURE__ */ jsx12(
|
|
1500
1768
|
DateTimeField,
|
|
1501
1769
|
{
|
|
1502
1770
|
label: messages["availability.exceptionUntil"],
|
|
@@ -1504,20 +1772,17 @@ function AvailabilityExceptionsEditor({ resourceId, timezone }) {
|
|
|
1504
1772
|
onChange: setUntil
|
|
1505
1773
|
}
|
|
1506
1774
|
),
|
|
1507
|
-
/* @__PURE__ */
|
|
1508
|
-
|
|
1775
|
+
/* @__PURE__ */ jsx12(
|
|
1776
|
+
SelectField,
|
|
1509
1777
|
{
|
|
1510
|
-
|
|
1778
|
+
hideLabel: true,
|
|
1779
|
+
label: messages["availability.exceptionsTitle"],
|
|
1511
1780
|
value: kind,
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
children: [
|
|
1515
|
-
/* @__PURE__ */ jsx11("option", { value: AVAILABILITY_EXCEPTION_KIND.BLOCK, children: messages["availability.exceptionKind.blocked"] }),
|
|
1516
|
-
/* @__PURE__ */ jsx11("option", { value: AVAILABILITY_EXCEPTION_KIND.EXTRA, children: messages["availability.exceptionKind.extra"] })
|
|
1517
|
-
]
|
|
1781
|
+
options: kindOptions,
|
|
1782
|
+
onChange: (next) => setKind(next)
|
|
1518
1783
|
}
|
|
1519
1784
|
),
|
|
1520
|
-
/* @__PURE__ */
|
|
1785
|
+
/* @__PURE__ */ jsx12(
|
|
1521
1786
|
"input",
|
|
1522
1787
|
{
|
|
1523
1788
|
"aria-label": messages["availability.exceptionReason"],
|
|
@@ -1536,7 +1801,7 @@ function AvailabilityExceptionsEditor({ resourceId, timezone }) {
|
|
|
1536
1801
|
disabled: addException.isPending,
|
|
1537
1802
|
className: "inline-flex items-center gap-2 min-h-11 px-3 py-2 rounded-lg text-sm font-medium bg-brand-600 text-white hover:bg-brand-700 disabled:opacity-50",
|
|
1538
1803
|
children: [
|
|
1539
|
-
/* @__PURE__ */
|
|
1804
|
+
/* @__PURE__ */ jsx12(Plus2, { "aria-hidden": "true", className: "w-4 h-4" }),
|
|
1540
1805
|
messages["availability.addException"]
|
|
1541
1806
|
]
|
|
1542
1807
|
}
|
|
@@ -1546,7 +1811,7 @@ function AvailabilityExceptionsEditor({ resourceId, timezone }) {
|
|
|
1546
1811
|
}
|
|
1547
1812
|
|
|
1548
1813
|
// src/components/AvailabilityEditor.tsx
|
|
1549
|
-
import { jsx as
|
|
1814
|
+
import { jsx as jsx13, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1550
1815
|
function AvailabilityEditor({ resourceId, timezone }) {
|
|
1551
1816
|
const { locale } = useSchedulingConfig();
|
|
1552
1817
|
const messages = resolveSchedulingMessages(locale);
|
|
@@ -1558,22 +1823,22 @@ function AvailabilityEditor({ resourceId, timezone }) {
|
|
|
1558
1823
|
] }),
|
|
1559
1824
|
timezone
|
|
1560
1825
|
] }),
|
|
1561
|
-
/* @__PURE__ */
|
|
1562
|
-
/* @__PURE__ */
|
|
1826
|
+
/* @__PURE__ */ jsx13(WeeklyRulesEditor, { resourceId }),
|
|
1827
|
+
/* @__PURE__ */ jsx13(AvailabilityExceptionsEditor, { resourceId, timezone })
|
|
1563
1828
|
] });
|
|
1564
1829
|
}
|
|
1565
1830
|
|
|
1566
1831
|
// src/workspace/AvailabilityArea.tsx
|
|
1567
|
-
import { jsx as
|
|
1832
|
+
import { jsx as jsx14, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1568
1833
|
function AvailabilityArea() {
|
|
1569
1834
|
const { locale } = useSchedulingConfig();
|
|
1570
1835
|
const messages = resolveSchedulingMessages(locale);
|
|
1571
1836
|
const { data, isLoading, isError } = useResources({ active: true, pageSize: MAX_PAGE_SIZE2 });
|
|
1572
|
-
const [resourceId, setResourceId] =
|
|
1837
|
+
const [resourceId, setResourceId] = useState6("");
|
|
1573
1838
|
const resources = data?.data ?? [];
|
|
1574
1839
|
const selectedResource = resources.find((resource) => resource.id === resourceId);
|
|
1575
1840
|
return /* @__PURE__ */ jsxs11("div", { className: "flex flex-1 min-h-0 min-w-0 flex-col gap-4 overflow-y-auto p-4", children: [
|
|
1576
|
-
/* @__PURE__ */
|
|
1841
|
+
/* @__PURE__ */ jsx14(
|
|
1577
1842
|
ResourceSelect,
|
|
1578
1843
|
{
|
|
1579
1844
|
label: messages["agenda.resourceLabel"],
|
|
@@ -1583,9 +1848,9 @@ function AvailabilityArea() {
|
|
|
1583
1848
|
onChange: setResourceId
|
|
1584
1849
|
}
|
|
1585
1850
|
),
|
|
1586
|
-
isError && /* @__PURE__ */
|
|
1587
|
-
isLoading && /* @__PURE__ */
|
|
1588
|
-
!isLoading && !isError && !selectedResource && /* @__PURE__ */
|
|
1851
|
+
isError && /* @__PURE__ */ jsx14(ErrorBanner, { message: messages["common.loadFailure"] }),
|
|
1852
|
+
isLoading && /* @__PURE__ */ jsx14(ListSkeleton, { label: messages["common.loading"], rows: 3 }),
|
|
1853
|
+
!isLoading && !isError && !selectedResource && /* @__PURE__ */ jsx14(
|
|
1589
1854
|
EmptyState,
|
|
1590
1855
|
{
|
|
1591
1856
|
icon: Clock,
|
|
@@ -1593,7 +1858,7 @@ function AvailabilityArea() {
|
|
|
1593
1858
|
hint: messages["availability.selectResourceHint"]
|
|
1594
1859
|
}
|
|
1595
1860
|
),
|
|
1596
|
-
selectedResource && /* @__PURE__ */
|
|
1861
|
+
selectedResource && /* @__PURE__ */ jsx14(
|
|
1597
1862
|
AvailabilityEditor,
|
|
1598
1863
|
{
|
|
1599
1864
|
resourceId: selectedResource.id,
|
|
@@ -1606,12 +1871,12 @@ function AvailabilityArea() {
|
|
|
1606
1871
|
|
|
1607
1872
|
// src/workspace/BookingsArea.tsx
|
|
1608
1873
|
import { ClipboardList } from "lucide-react";
|
|
1609
|
-
import { useState as
|
|
1874
|
+
import { useState as useState9 } from "react";
|
|
1610
1875
|
import { MAX_PAGE_SIZE as MAX_PAGE_SIZE3 } from "@adatechnology/scheduling-contracts";
|
|
1611
1876
|
|
|
1612
1877
|
// src/components/BookingsTable.tsx
|
|
1613
1878
|
import { ArrowDown, ArrowLeft, ArrowRight, ArrowUp, ArrowUpDown } from "lucide-react";
|
|
1614
|
-
import { useState as
|
|
1879
|
+
import { useState as useState7 } from "react";
|
|
1615
1880
|
import { BOOKING_STATUS as BOOKING_STATUS4 } from "@adatechnology/scheduling-contracts";
|
|
1616
1881
|
|
|
1617
1882
|
// src/components/bookingsTableState.util.ts
|
|
@@ -1661,7 +1926,7 @@ function filterBookingsByStatus(bookings, statusFilters) {
|
|
|
1661
1926
|
}
|
|
1662
1927
|
|
|
1663
1928
|
// src/components/BookingsTable.tsx
|
|
1664
|
-
import { jsx as
|
|
1929
|
+
import { jsx as jsx15, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1665
1930
|
var ALL_STATUSES = Object.values(BOOKING_STATUS4);
|
|
1666
1931
|
var HEADER_BUTTON_CLASS = "inline-flex items-center gap-1 text-left text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400";
|
|
1667
1932
|
var CHECKBOX_CELL_CLASS = "px-3 py-2";
|
|
@@ -1680,7 +1945,7 @@ function BookingsTable({
|
|
|
1680
1945
|
}) {
|
|
1681
1946
|
const { locale } = useSchedulingConfig();
|
|
1682
1947
|
const messages = resolveSchedulingMessages(locale);
|
|
1683
|
-
const [selected, setSelected] =
|
|
1948
|
+
const [selected, setSelected] = useState7(/* @__PURE__ */ new Set());
|
|
1684
1949
|
const visibleBookings = filterBookingsByStatus(bookings, state.statusFilters);
|
|
1685
1950
|
const allSelected = visibleBookings.length > 0 && visibleBookings.every((booking) => selected.has(booking.id));
|
|
1686
1951
|
function toggleSort(column) {
|
|
@@ -1710,12 +1975,12 @@ function BookingsTable({
|
|
|
1710
1975
|
setSelected(next);
|
|
1711
1976
|
}
|
|
1712
1977
|
function renderSortIcon(column) {
|
|
1713
|
-
if (state.sortColumn !== column) return /* @__PURE__ */
|
|
1714
|
-
return state.sortDirection === "asc" ? /* @__PURE__ */
|
|
1978
|
+
if (state.sortColumn !== column) return /* @__PURE__ */ jsx15(ArrowUpDown, { "aria-hidden": "true", className: "w-3 h-3" });
|
|
1979
|
+
return state.sortDirection === "asc" ? /* @__PURE__ */ jsx15(ArrowUp, { "aria-hidden": "true", className: "w-3 h-3" }) : /* @__PURE__ */ jsx15(ArrowDown, { "aria-hidden": "true", className: "w-3 h-3" });
|
|
1715
1980
|
}
|
|
1716
1981
|
function renderHeader(column, labelKey) {
|
|
1717
1982
|
const ariaSort = state.sortColumn !== column ? "none" : state.sortDirection === "asc" ? "ascending" : "descending";
|
|
1718
|
-
return /* @__PURE__ */
|
|
1983
|
+
return /* @__PURE__ */ jsx15("th", { scope: "col", className: "px-3 py-2", "aria-sort": ariaSort, children: /* @__PURE__ */ jsxs12("button", { type: "button", onClick: () => toggleSort(column), className: HEADER_BUTTON_CLASS, children: [
|
|
1719
1984
|
messages[labelKey],
|
|
1720
1985
|
renderSortIcon(column)
|
|
1721
1986
|
] }) });
|
|
@@ -1723,7 +1988,7 @@ function BookingsTable({
|
|
|
1723
1988
|
return /* @__PURE__ */ jsxs12("div", { className: "space-y-3", children: [
|
|
1724
1989
|
/* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap items-center gap-3", children: [
|
|
1725
1990
|
/* @__PURE__ */ jsxs12("fieldset", { className: "flex flex-wrap items-center gap-2", children: [
|
|
1726
|
-
/* @__PURE__ */
|
|
1991
|
+
/* @__PURE__ */ jsx15("legend", { className: "sr-only", children: messages["booking.filterByStatus"] }),
|
|
1727
1992
|
ALL_STATUSES.map((status) => {
|
|
1728
1993
|
const isActive = state.statusFilters.includes(status);
|
|
1729
1994
|
return /* @__PURE__ */ jsxs12(
|
|
@@ -1731,7 +1996,7 @@ function BookingsTable({
|
|
|
1731
1996
|
{
|
|
1732
1997
|
className: `flex cursor-pointer items-center gap-1.5 rounded-full border px-3 py-1.5 text-sm transition-colors ${isActive ? "border-brand-500 bg-brand-50 text-brand-800 dark:bg-brand-900/40 dark:text-brand-200" : "border-gray-300 text-gray-600 hover:bg-gray-50 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-800"}`,
|
|
1733
1998
|
children: [
|
|
1734
|
-
/* @__PURE__ */
|
|
1999
|
+
/* @__PURE__ */ jsx15(
|
|
1735
2000
|
"input",
|
|
1736
2001
|
{
|
|
1737
2002
|
type: "checkbox",
|
|
@@ -1747,17 +2012,17 @@ function BookingsTable({
|
|
|
1747
2012
|
);
|
|
1748
2013
|
})
|
|
1749
2014
|
] }),
|
|
1750
|
-
!isBookingsTableStateDefault(state) && /* @__PURE__ */
|
|
2015
|
+
!isBookingsTableStateDefault(state) && /* @__PURE__ */ jsx15(
|
|
1751
2016
|
"button",
|
|
1752
2017
|
{
|
|
1753
2018
|
type: "button",
|
|
1754
2019
|
onClick: () => onStateChange?.(DEFAULT_BOOKINGS_TABLE_STATE),
|
|
1755
|
-
className: "min-h-11 px-3 text-sm font-medium text-brand-700 hover:underline dark:text-brand-
|
|
2020
|
+
className: "min-h-11 px-3 text-sm font-medium text-brand-700 hover:underline dark:text-brand-400",
|
|
1756
2021
|
children: messages["common.clearFilters"]
|
|
1757
2022
|
}
|
|
1758
2023
|
)
|
|
1759
2024
|
] }),
|
|
1760
|
-
selected.size > 0 && bulkActions.length > 0 && /* @__PURE__ */
|
|
2025
|
+
selected.size > 0 && bulkActions.length > 0 && /* @__PURE__ */ jsx15("div", { className: "flex items-center gap-2 rounded-lg bg-brand-50 dark:bg-brand-900/30 px-3 py-2", children: bulkActions.map((action) => /* @__PURE__ */ jsx15(
|
|
1761
2026
|
"button",
|
|
1762
2027
|
{
|
|
1763
2028
|
type: "button",
|
|
@@ -1769,8 +2034,8 @@ function BookingsTable({
|
|
|
1769
2034
|
)) }),
|
|
1770
2035
|
/* @__PURE__ */ jsxs12("div", { className: `${SURFACE_BORDER} overflow-x-auto`, children: [
|
|
1771
2036
|
/* @__PURE__ */ jsxs12("table", { className: "min-w-full text-sm", children: [
|
|
1772
|
-
/* @__PURE__ */
|
|
1773
|
-
/* @__PURE__ */
|
|
2037
|
+
/* @__PURE__ */ jsx15("thead", { className: "border-b border-gray-200 bg-gray-50 dark:border-gray-800 dark:bg-gray-800/60", children: /* @__PURE__ */ jsxs12("tr", { children: [
|
|
2038
|
+
/* @__PURE__ */ jsx15("th", { scope: "col", className: CHECKBOX_CELL_CLASS, children: /* @__PURE__ */ jsx15(
|
|
1774
2039
|
"input",
|
|
1775
2040
|
{
|
|
1776
2041
|
type: "checkbox",
|
|
@@ -1784,12 +2049,12 @@ function BookingsTable({
|
|
|
1784
2049
|
renderHeader("startsAt", "booking.column.startsAt"),
|
|
1785
2050
|
renderHeader("endsAt", "booking.column.endsAt")
|
|
1786
2051
|
] }) }),
|
|
1787
|
-
/* @__PURE__ */
|
|
2052
|
+
/* @__PURE__ */ jsx15("tbody", { children: visibleBookings.map((booking, index) => /* @__PURE__ */ jsxs12(
|
|
1788
2053
|
"tr",
|
|
1789
2054
|
{
|
|
1790
2055
|
className: `${index % 2 === 1 ? ROW_STRIPE : ""} hover:bg-brand-50/50 dark:hover:bg-brand-900/10`,
|
|
1791
2056
|
children: [
|
|
1792
|
-
/* @__PURE__ */
|
|
2057
|
+
/* @__PURE__ */ jsx15("td", { className: CHECKBOX_CELL_CLASS, children: /* @__PURE__ */ jsx15(
|
|
1793
2058
|
"input",
|
|
1794
2059
|
{
|
|
1795
2060
|
type: "checkbox",
|
|
@@ -1798,19 +2063,19 @@ function BookingsTable({
|
|
|
1798
2063
|
onChange: () => toggleRowSelected(booking.id)
|
|
1799
2064
|
}
|
|
1800
2065
|
) }),
|
|
1801
|
-
/* @__PURE__ */
|
|
1802
|
-
/* @__PURE__ */
|
|
1803
|
-
/* @__PURE__ */
|
|
1804
|
-
/* @__PURE__ */
|
|
2066
|
+
/* @__PURE__ */ jsx15("td", { className: "px-3 py-2", children: /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => onRowClick?.(booking), className: "min-h-11 text-left hover:underline", children: booking.title }) }),
|
|
2067
|
+
/* @__PURE__ */ jsx15("td", { className: "px-3 py-2", children: /* @__PURE__ */ jsx15(BookingStatusBadge, { status: booking.status }) }),
|
|
2068
|
+
/* @__PURE__ */ jsx15("td", { className: "px-3 py-2 tabular-nums whitespace-nowrap", children: booking.startsAt.toLocaleString(locale) }),
|
|
2069
|
+
/* @__PURE__ */ jsx15("td", { className: "px-3 py-2 tabular-nums whitespace-nowrap", children: booking.endsAt.toLocaleString(locale) })
|
|
1805
2070
|
]
|
|
1806
2071
|
},
|
|
1807
2072
|
booking.id
|
|
1808
2073
|
)) })
|
|
1809
2074
|
] }),
|
|
1810
|
-
visibleBookings.length === 0 && /* @__PURE__ */
|
|
2075
|
+
visibleBookings.length === 0 && /* @__PURE__ */ jsx15("p", { className: "px-3 py-8 text-center text-sm text-gray-500 dark:text-gray-400", children: messages["common.empty"] })
|
|
1811
2076
|
] }),
|
|
1812
2077
|
pagination && pagination.totalPages > 1 && /* @__PURE__ */ jsxs12("div", { className: "flex items-center justify-end gap-3", children: [
|
|
1813
|
-
/* @__PURE__ */
|
|
2078
|
+
/* @__PURE__ */ jsx15(
|
|
1814
2079
|
"button",
|
|
1815
2080
|
{
|
|
1816
2081
|
type: "button",
|
|
@@ -1818,7 +2083,7 @@ function BookingsTable({
|
|
|
1818
2083
|
disabled: state.page <= 1,
|
|
1819
2084
|
onClick: () => goToPage(state.page - 1),
|
|
1820
2085
|
className: ICON_BUTTON,
|
|
1821
|
-
children: /* @__PURE__ */
|
|
2086
|
+
children: /* @__PURE__ */ jsx15(ArrowLeft, { "aria-hidden": "true", className: "w-4 h-4" })
|
|
1822
2087
|
}
|
|
1823
2088
|
),
|
|
1824
2089
|
/* @__PURE__ */ jsxs12("span", { className: "text-sm tabular-nums text-gray-500 dark:text-gray-400", children: [
|
|
@@ -1826,7 +2091,7 @@ function BookingsTable({
|
|
|
1826
2091
|
" / ",
|
|
1827
2092
|
pagination.totalPages
|
|
1828
2093
|
] }),
|
|
1829
|
-
/* @__PURE__ */
|
|
2094
|
+
/* @__PURE__ */ jsx15(
|
|
1830
2095
|
"button",
|
|
1831
2096
|
{
|
|
1832
2097
|
type: "button",
|
|
@@ -1834,7 +2099,7 @@ function BookingsTable({
|
|
|
1834
2099
|
disabled: state.page >= pagination.totalPages,
|
|
1835
2100
|
onClick: () => goToPage(state.page + 1),
|
|
1836
2101
|
className: ICON_BUTTON,
|
|
1837
|
-
children: /* @__PURE__ */
|
|
2102
|
+
children: /* @__PURE__ */ jsx15(ArrowRight, { "aria-hidden": "true", className: "w-4 h-4" })
|
|
1838
2103
|
}
|
|
1839
2104
|
)
|
|
1840
2105
|
] })
|
|
@@ -1842,15 +2107,15 @@ function BookingsTable({
|
|
|
1842
2107
|
}
|
|
1843
2108
|
|
|
1844
2109
|
// src/hooks/useBookingsTableState.hook.ts
|
|
1845
|
-
import { useEffect as
|
|
2110
|
+
import { useEffect as useEffect4, useState as useState8 } from "react";
|
|
1846
2111
|
var OWNED_PARAM_KEYS = ["sortBy", "sortDirection", "status", "page"];
|
|
1847
2112
|
function readInitialState() {
|
|
1848
2113
|
if (typeof window === "undefined") return DEFAULT_BOOKINGS_TABLE_STATE;
|
|
1849
2114
|
return parseBookingsTableState(window.location.search);
|
|
1850
2115
|
}
|
|
1851
2116
|
function useBookingsTableState() {
|
|
1852
|
-
const [state, setState] =
|
|
1853
|
-
|
|
2117
|
+
const [state, setState] = useState8(readInitialState);
|
|
2118
|
+
useEffect4(() => {
|
|
1854
2119
|
if (typeof window === "undefined") return;
|
|
1855
2120
|
const params = new URLSearchParams(window.location.search);
|
|
1856
2121
|
for (const key of OWNED_PARAM_KEYS) params.delete(key);
|
|
@@ -1863,7 +2128,7 @@ function useBookingsTableState() {
|
|
|
1863
2128
|
}
|
|
1864
2129
|
|
|
1865
2130
|
// src/workspace/BookingsArea.tsx
|
|
1866
|
-
import { jsx as
|
|
2131
|
+
import { jsx as jsx16, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1867
2132
|
var PAGE_SIZE = 20;
|
|
1868
2133
|
function BookingsArea() {
|
|
1869
2134
|
const { locale } = useSchedulingConfig();
|
|
@@ -1879,7 +2144,7 @@ function BookingsArea() {
|
|
|
1879
2144
|
});
|
|
1880
2145
|
const confirmBooking = useConfirmBooking();
|
|
1881
2146
|
const { data: resourcesData } = useResources({ pageSize: MAX_PAGE_SIZE3 });
|
|
1882
|
-
const [selectedBookingId, setSelectedBookingId] =
|
|
2147
|
+
const [selectedBookingId, setSelectedBookingId] = useState9(void 0);
|
|
1883
2148
|
const selectedBooking = data?.data.find((booking) => booking.id === selectedBookingId);
|
|
1884
2149
|
const selectedBookingResourceTimezone = resourcesData?.data.find(
|
|
1885
2150
|
(resource) => resource.id === selectedBooking?.resourceIds[0]
|
|
@@ -1889,10 +2154,10 @@ function BookingsArea() {
|
|
|
1889
2154
|
}
|
|
1890
2155
|
return /* @__PURE__ */ jsxs13("div", { className: "flex flex-1 min-h-0 min-w-0", children: [
|
|
1891
2156
|
/* @__PURE__ */ jsxs13("div", { className: "flex flex-1 min-h-0 min-w-0 flex-col gap-4 overflow-y-auto p-4", children: [
|
|
1892
|
-
isError && /* @__PURE__ */
|
|
1893
|
-
confirmBooking.isError && /* @__PURE__ */
|
|
1894
|
-
isLoading && /* @__PURE__ */
|
|
1895
|
-
!isLoading && !isError && (data?.data.length ?? 0) === 0 && tableState.statusFilters.length === 0 && /* @__PURE__ */
|
|
2157
|
+
isError && /* @__PURE__ */ jsx16(ErrorBanner, { message: messages["common.loadFailure"] }),
|
|
2158
|
+
confirmBooking.isError && /* @__PURE__ */ jsx16(ErrorBanner, { message: messages["common.actionFailure"] }),
|
|
2159
|
+
isLoading && /* @__PURE__ */ jsx16(ListSkeleton, { label: messages["common.loading"], rows: 8 }),
|
|
2160
|
+
!isLoading && !isError && (data?.data.length ?? 0) === 0 && tableState.statusFilters.length === 0 && /* @__PURE__ */ jsx16(
|
|
1896
2161
|
EmptyState,
|
|
1897
2162
|
{
|
|
1898
2163
|
icon: ClipboardList,
|
|
@@ -1900,7 +2165,7 @@ function BookingsArea() {
|
|
|
1900
2165
|
hint: messages["booking.emptyHint"]
|
|
1901
2166
|
}
|
|
1902
2167
|
),
|
|
1903
|
-
!isLoading && ((data?.data.length ?? 0) > 0 || tableState.statusFilters.length > 0) && /* @__PURE__ */
|
|
2168
|
+
!isLoading && ((data?.data.length ?? 0) > 0 || tableState.statusFilters.length > 0) && /* @__PURE__ */ jsx16(
|
|
1904
2169
|
BookingsTable,
|
|
1905
2170
|
{
|
|
1906
2171
|
bookings: data?.data ?? [],
|
|
@@ -1912,7 +2177,7 @@ function BookingsArea() {
|
|
|
1912
2177
|
}
|
|
1913
2178
|
)
|
|
1914
2179
|
] }),
|
|
1915
|
-
selectedBooking && /* @__PURE__ */
|
|
2180
|
+
selectedBooking && /* @__PURE__ */ jsx16(
|
|
1916
2181
|
BookingDrawer,
|
|
1917
2182
|
{
|
|
1918
2183
|
booking: selectedBooking,
|
|
@@ -1926,28 +2191,41 @@ function BookingsArea() {
|
|
|
1926
2191
|
|
|
1927
2192
|
// src/workspace/ResourcesArea.tsx
|
|
1928
2193
|
import { Plus as Plus3, Trash2 as Trash23, Users } from "lucide-react";
|
|
1929
|
-
import { useState as
|
|
2194
|
+
import { useState as useState11 } from "react";
|
|
1930
2195
|
import { MAX_PAGE_SIZE as MAX_PAGE_SIZE4 } from "@adatechnology/scheduling-contracts";
|
|
1931
2196
|
|
|
1932
2197
|
// src/components/ResourceForm.tsx
|
|
1933
|
-
import { useId as
|
|
2198
|
+
import { useId as useId3, useMemo as useMemo7, useState as useState10 } from "react";
|
|
1934
2199
|
import { RESOURCE_KIND } from "@adatechnology/scheduling-contracts";
|
|
1935
|
-
import { jsx as
|
|
2200
|
+
import { jsx as jsx17, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1936
2201
|
var INPUT_CLASS2 = "w-full min-h-11 px-3 py-2 border border-gray-300 dark:border-gray-700 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-brand-500 bg-white dark:bg-gray-900";
|
|
1937
2202
|
var LABEL_CLASS = "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1";
|
|
1938
2203
|
var BUTTON_PRIMARY2 = "inline-flex items-center justify-center gap-2 px-3 py-2 rounded-lg text-sm font-medium transition-colors min-h-11 bg-brand-600 text-white hover:bg-brand-700 disabled:opacity-50";
|
|
2204
|
+
var FALLBACK_TIMEZONES = ["America/Sao_Paulo", "America/Manaus", "America/Belem", "UTC"];
|
|
2205
|
+
function buildTimezoneOptions(current) {
|
|
2206
|
+
const supported = Intl.supportedValuesOf?.("timeZone") ?? FALLBACK_TIMEZONES;
|
|
2207
|
+
const zones = supported.includes(current) ? supported : [current, ...supported];
|
|
2208
|
+
return zones.map((zone) => ({ value: zone, label: zone.replace(/_/g, " ") }));
|
|
2209
|
+
}
|
|
1939
2210
|
function ResourceForm({ initialValues, onSubmit }) {
|
|
1940
2211
|
const { locale } = useSchedulingConfig();
|
|
1941
2212
|
const messages = resolveSchedulingMessages(locale);
|
|
1942
|
-
const formId =
|
|
2213
|
+
const formId = useId3();
|
|
1943
2214
|
const nameId = `${formId}-name`;
|
|
1944
|
-
const
|
|
1945
|
-
const
|
|
1946
|
-
const [
|
|
1947
|
-
const [
|
|
1948
|
-
const [
|
|
1949
|
-
const
|
|
1950
|
-
|
|
2215
|
+
const [name, setName] = useState10(initialValues?.name ?? "");
|
|
2216
|
+
const [kind, setKind] = useState10(initialValues?.kind ?? RESOURCE_KIND.PERSON);
|
|
2217
|
+
const [timezone, setTimezone] = useState10(initialValues?.timezone ?? "America/Sao_Paulo");
|
|
2218
|
+
const [active, setActive] = useState10(initialValues?.active ?? true);
|
|
2219
|
+
const [submitting, setSubmitting] = useState10(false);
|
|
2220
|
+
const kindOptions = useMemo7(
|
|
2221
|
+
() => [
|
|
2222
|
+
{ value: RESOURCE_KIND.PERSON, label: messages["resource.kind.person"] },
|
|
2223
|
+
{ value: RESOURCE_KIND.ROOM, label: messages["resource.kind.room"] },
|
|
2224
|
+
{ value: RESOURCE_KIND.EQUIPMENT, label: messages["resource.kind.equipment"] }
|
|
2225
|
+
],
|
|
2226
|
+
[messages]
|
|
2227
|
+
);
|
|
2228
|
+
const timezoneOptions = useMemo7(() => buildTimezoneOptions(timezone), [timezone]);
|
|
1951
2229
|
async function handleSubmit(event) {
|
|
1952
2230
|
event.preventDefault();
|
|
1953
2231
|
setSubmitting(true);
|
|
@@ -1959,8 +2237,8 @@ function ResourceForm({ initialValues, onSubmit }) {
|
|
|
1959
2237
|
}
|
|
1960
2238
|
return /* @__PURE__ */ jsxs14("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
1961
2239
|
/* @__PURE__ */ jsxs14("div", { children: [
|
|
1962
|
-
/* @__PURE__ */
|
|
1963
|
-
/* @__PURE__ */
|
|
2240
|
+
/* @__PURE__ */ jsx17("label", { htmlFor: nameId, className: LABEL_CLASS, children: messages["resource.name"] }),
|
|
2241
|
+
/* @__PURE__ */ jsx17(
|
|
1964
2242
|
"input",
|
|
1965
2243
|
{
|
|
1966
2244
|
id: nameId,
|
|
@@ -1972,72 +2250,59 @@ function ResourceForm({ initialValues, onSubmit }) {
|
|
|
1972
2250
|
}
|
|
1973
2251
|
)
|
|
1974
2252
|
] }),
|
|
1975
|
-
/* @__PURE__ */
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
"
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
/* @__PURE__ */ jsx16(
|
|
1995
|
-
"input",
|
|
1996
|
-
{
|
|
1997
|
-
id: timezoneId,
|
|
1998
|
-
type: "text",
|
|
1999
|
-
required: true,
|
|
2000
|
-
value: timezone,
|
|
2001
|
-
onChange: (event) => setTimezone(event.target.value),
|
|
2002
|
-
placeholder: "America/Sao_Paulo",
|
|
2003
|
-
className: INPUT_CLASS2
|
|
2004
|
-
}
|
|
2005
|
-
)
|
|
2006
|
-
] }),
|
|
2253
|
+
/* @__PURE__ */ jsx17(
|
|
2254
|
+
SelectField,
|
|
2255
|
+
{
|
|
2256
|
+
label: messages["resource.kind"],
|
|
2257
|
+
value: kind,
|
|
2258
|
+
options: kindOptions,
|
|
2259
|
+
onChange: (next) => setKind(next)
|
|
2260
|
+
}
|
|
2261
|
+
),
|
|
2262
|
+
/* @__PURE__ */ jsx17(
|
|
2263
|
+
SelectField,
|
|
2264
|
+
{
|
|
2265
|
+
label: messages["resource.timezone"],
|
|
2266
|
+
value: timezone,
|
|
2267
|
+
options: timezoneOptions,
|
|
2268
|
+
onChange: setTimezone,
|
|
2269
|
+
searchable: true
|
|
2270
|
+
}
|
|
2271
|
+
),
|
|
2007
2272
|
initialValues && /* @__PURE__ */ jsxs14("label", { className: "flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300 min-h-11", children: [
|
|
2008
|
-
/* @__PURE__ */
|
|
2273
|
+
/* @__PURE__ */ jsx17("input", { type: "checkbox", checked: active, onChange: (event) => setActive(event.target.checked) }),
|
|
2009
2274
|
messages["resource.active"]
|
|
2010
2275
|
] }),
|
|
2011
|
-
/* @__PURE__ */
|
|
2276
|
+
/* @__PURE__ */ jsx17("button", { type: "submit", disabled: submitting, className: BUTTON_PRIMARY2, children: messages["common.save"] })
|
|
2012
2277
|
] });
|
|
2013
2278
|
}
|
|
2014
2279
|
|
|
2015
2280
|
// src/components/ResourceList.tsx
|
|
2016
|
-
import { jsx as
|
|
2281
|
+
import { jsx as jsx18, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2017
2282
|
function ResourceList({ resources, onSelect }) {
|
|
2018
2283
|
const { locale } = useSchedulingConfig();
|
|
2019
2284
|
const messages = resolveSchedulingMessages(locale);
|
|
2020
2285
|
if (resources.length === 0) {
|
|
2021
|
-
return /* @__PURE__ */
|
|
2286
|
+
return /* @__PURE__ */ jsx18("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: messages["common.empty"] });
|
|
2022
2287
|
}
|
|
2023
|
-
return /* @__PURE__ */
|
|
2288
|
+
return /* @__PURE__ */ jsx18("ul", { className: `${SURFACE_BORDER} divide-y divide-gray-200 overflow-hidden dark:divide-gray-800`, children: resources.map((resource, index) => /* @__PURE__ */ jsx18("li", { className: index % 2 === 1 ? ROW_STRIPE : void 0, children: /* @__PURE__ */ jsxs15(
|
|
2024
2289
|
"button",
|
|
2025
2290
|
{
|
|
2026
2291
|
type: "button",
|
|
2027
2292
|
onClick: () => onSelect(resource),
|
|
2028
2293
|
className: "flex w-full items-center gap-3 px-4 py-3 min-h-11 text-left text-sm transition-colors hover:bg-brand-50/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-brand-500 dark:hover:bg-brand-900/20",
|
|
2029
2294
|
children: [
|
|
2030
|
-
/* @__PURE__ */
|
|
2031
|
-
/* @__PURE__ */
|
|
2032
|
-
/* @__PURE__ */
|
|
2033
|
-
!resource.active && /* @__PURE__ */
|
|
2295
|
+
/* @__PURE__ */ jsx18("span", { className: "flex-1 font-medium text-gray-900 dark:text-gray-100", children: resource.name }),
|
|
2296
|
+
/* @__PURE__ */ jsx18("span", { className: "text-gray-500 dark:text-gray-400", children: messages[`resource.kind.${resource.kind}`] }),
|
|
2297
|
+
/* @__PURE__ */ jsx18("span", { className: "text-gray-500 dark:text-gray-400", children: resource.timezone }),
|
|
2298
|
+
!resource.active && /* @__PURE__ */ jsx18("span", { className: "rounded-full bg-gray-200 px-2 py-0.5 text-xs font-medium text-gray-600 dark:bg-gray-700 dark:text-gray-300", children: messages["resource.inactive"] })
|
|
2034
2299
|
]
|
|
2035
2300
|
}
|
|
2036
2301
|
) }, resource.id)) });
|
|
2037
2302
|
}
|
|
2038
2303
|
|
|
2039
2304
|
// src/workspace/ResourcesArea.tsx
|
|
2040
|
-
import { jsx as
|
|
2305
|
+
import { jsx as jsx19, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2041
2306
|
function ResourcesArea() {
|
|
2042
2307
|
const { locale } = useSchedulingConfig();
|
|
2043
2308
|
const messages = resolveSchedulingMessages(locale);
|
|
@@ -2045,7 +2310,7 @@ function ResourcesArea() {
|
|
|
2045
2310
|
const createResource = useCreateResource();
|
|
2046
2311
|
const updateResource = useUpdateResource();
|
|
2047
2312
|
const deleteResource = useDeleteResource();
|
|
2048
|
-
const [draft, setDraft] =
|
|
2313
|
+
const [draft, setDraft] = useState11(void 0);
|
|
2049
2314
|
const isDraftOpen = draft !== void 0;
|
|
2050
2315
|
const isEditing = Boolean(draft);
|
|
2051
2316
|
const resources = data?.data ?? [];
|
|
@@ -2062,17 +2327,17 @@ function ResourcesArea() {
|
|
|
2062
2327
|
}
|
|
2063
2328
|
function renderCreateButton() {
|
|
2064
2329
|
return /* @__PURE__ */ jsxs16("button", { type: "button", onClick: () => setDraft(null), className: BUTTON_PRIMARY, children: [
|
|
2065
|
-
/* @__PURE__ */
|
|
2330
|
+
/* @__PURE__ */ jsx19(Plus3, { "aria-hidden": "true", className: "h-4 w-4" }),
|
|
2066
2331
|
messages["resource.newResource"]
|
|
2067
2332
|
] });
|
|
2068
2333
|
}
|
|
2069
2334
|
return /* @__PURE__ */ jsxs16("div", { className: "flex flex-1 min-h-0 min-w-0", children: [
|
|
2070
2335
|
/* @__PURE__ */ jsxs16("div", { className: "flex flex-1 min-h-0 min-w-0 flex-col gap-4 overflow-y-auto p-4", children: [
|
|
2071
|
-
/* @__PURE__ */
|
|
2072
|
-
isError && /* @__PURE__ */
|
|
2073
|
-
(createResource.isError || updateResource.isError || deleteResource.isError) && /* @__PURE__ */
|
|
2074
|
-
isLoading && /* @__PURE__ */
|
|
2075
|
-
!isLoading && !isError && resources.length === 0 && /* @__PURE__ */
|
|
2336
|
+
/* @__PURE__ */ jsx19("div", { className: "flex justify-end", children: renderCreateButton() }),
|
|
2337
|
+
isError && /* @__PURE__ */ jsx19(ErrorBanner, { message: messages["common.loadFailure"] }),
|
|
2338
|
+
(createResource.isError || updateResource.isError || deleteResource.isError) && /* @__PURE__ */ jsx19(ErrorBanner, { message: messages["common.actionFailure"] }),
|
|
2339
|
+
isLoading && /* @__PURE__ */ jsx19(ListSkeleton, { label: messages["common.loading"] }),
|
|
2340
|
+
!isLoading && !isError && resources.length === 0 && /* @__PURE__ */ jsx19(
|
|
2076
2341
|
EmptyState,
|
|
2077
2342
|
{
|
|
2078
2343
|
icon: Users,
|
|
@@ -2081,9 +2346,9 @@ function ResourcesArea() {
|
|
|
2081
2346
|
action: renderCreateButton()
|
|
2082
2347
|
}
|
|
2083
2348
|
),
|
|
2084
|
-
!isLoading && resources.length > 0 && /* @__PURE__ */
|
|
2349
|
+
!isLoading && resources.length > 0 && /* @__PURE__ */ jsx19(ResourceList, { resources, onSelect: setDraft })
|
|
2085
2350
|
] }),
|
|
2086
|
-
isDraftOpen && /* @__PURE__ */
|
|
2351
|
+
isDraftOpen && /* @__PURE__ */ jsx19(
|
|
2087
2352
|
SidePanel,
|
|
2088
2353
|
{
|
|
2089
2354
|
title: isEditing ? messages["resource.editTitle"] : messages["resource.createTitle"],
|
|
@@ -2099,12 +2364,12 @@ function ResourcesArea() {
|
|
|
2099
2364
|
},
|
|
2100
2365
|
className: BUTTON_DANGER,
|
|
2101
2366
|
children: [
|
|
2102
|
-
/* @__PURE__ */
|
|
2367
|
+
/* @__PURE__ */ jsx19(Trash23, { "aria-hidden": "true", className: "h-4 w-4" }),
|
|
2103
2368
|
messages["common.remove"]
|
|
2104
2369
|
]
|
|
2105
2370
|
}
|
|
2106
2371
|
) : void 0,
|
|
2107
|
-
children: /* @__PURE__ */
|
|
2372
|
+
children: /* @__PURE__ */ jsx19(
|
|
2108
2373
|
ResourceForm,
|
|
2109
2374
|
{
|
|
2110
2375
|
...draft ? { initialValues: draft } : {},
|
|
@@ -2119,24 +2384,24 @@ function ResourcesArea() {
|
|
|
2119
2384
|
|
|
2120
2385
|
// src/workspace/ServicesArea.tsx
|
|
2121
2386
|
import { Plus as Plus4, Trash2 as Trash24, Wrench } from "lucide-react";
|
|
2122
|
-
import { useState as
|
|
2387
|
+
import { useState as useState13 } from "react";
|
|
2123
2388
|
import { MAX_PAGE_SIZE as MAX_PAGE_SIZE5 } from "@adatechnology/scheduling-contracts";
|
|
2124
2389
|
|
|
2125
2390
|
// src/components/ServiceForm.tsx
|
|
2126
|
-
import { useState as
|
|
2127
|
-
import { jsx as
|
|
2391
|
+
import { useState as useState12 } from "react";
|
|
2392
|
+
import { jsx as jsx20, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2128
2393
|
var INPUT_CLASS3 = "w-full min-h-11 px-3 py-2 border border-gray-300 dark:border-gray-700 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-brand-500 bg-white dark:bg-gray-900";
|
|
2129
2394
|
var LABEL_CLASS2 = "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1";
|
|
2130
2395
|
var BUTTON_PRIMARY3 = "inline-flex items-center justify-center gap-2 px-3 py-2 rounded-lg text-sm font-medium transition-colors min-h-11 bg-brand-600 text-white hover:bg-brand-700 disabled:opacity-50";
|
|
2131
2396
|
function ServiceForm({ initialValues, onSubmit }) {
|
|
2132
2397
|
const { locale } = useSchedulingConfig();
|
|
2133
2398
|
const messages = resolveSchedulingMessages(locale);
|
|
2134
|
-
const [name, setName] =
|
|
2135
|
-
const [durationMinutes, setDurationMinutes] =
|
|
2136
|
-
const [bufferBeforeMinutes, setBufferBeforeMinutes] =
|
|
2137
|
-
const [bufferAfterMinutes, setBufferAfterMinutes] =
|
|
2138
|
-
const [active, setActive] =
|
|
2139
|
-
const [submitting, setSubmitting] =
|
|
2399
|
+
const [name, setName] = useState12(initialValues?.name ?? "");
|
|
2400
|
+
const [durationMinutes, setDurationMinutes] = useState12(initialValues?.durationMinutes ?? 30);
|
|
2401
|
+
const [bufferBeforeMinutes, setBufferBeforeMinutes] = useState12(initialValues?.bufferBeforeMinutes ?? 0);
|
|
2402
|
+
const [bufferAfterMinutes, setBufferAfterMinutes] = useState12(initialValues?.bufferAfterMinutes ?? 0);
|
|
2403
|
+
const [active, setActive] = useState12(initialValues?.active ?? true);
|
|
2404
|
+
const [submitting, setSubmitting] = useState12(false);
|
|
2140
2405
|
async function handleSubmit(event) {
|
|
2141
2406
|
event.preventDefault();
|
|
2142
2407
|
setSubmitting(true);
|
|
@@ -2148,8 +2413,8 @@ function ServiceForm({ initialValues, onSubmit }) {
|
|
|
2148
2413
|
}
|
|
2149
2414
|
return /* @__PURE__ */ jsxs17("form", { onSubmit: handleSubmit, className: "space-y-4", children: [
|
|
2150
2415
|
/* @__PURE__ */ jsxs17("div", { children: [
|
|
2151
|
-
/* @__PURE__ */
|
|
2152
|
-
/* @__PURE__ */
|
|
2416
|
+
/* @__PURE__ */ jsx20("label", { htmlFor: "scheduling-service-name", className: LABEL_CLASS2, children: messages["service.name"] }),
|
|
2417
|
+
/* @__PURE__ */ jsx20(
|
|
2153
2418
|
"input",
|
|
2154
2419
|
{
|
|
2155
2420
|
id: "scheduling-service-name",
|
|
@@ -2162,8 +2427,8 @@ function ServiceForm({ initialValues, onSubmit }) {
|
|
|
2162
2427
|
)
|
|
2163
2428
|
] }),
|
|
2164
2429
|
/* @__PURE__ */ jsxs17("div", { children: [
|
|
2165
|
-
/* @__PURE__ */
|
|
2166
|
-
/* @__PURE__ */
|
|
2430
|
+
/* @__PURE__ */ jsx20("label", { htmlFor: "scheduling-service-duration", className: LABEL_CLASS2, children: messages["service.durationMinutes"] }),
|
|
2431
|
+
/* @__PURE__ */ jsx20(
|
|
2167
2432
|
"input",
|
|
2168
2433
|
{
|
|
2169
2434
|
id: "scheduling-service-duration",
|
|
@@ -2178,8 +2443,8 @@ function ServiceForm({ initialValues, onSubmit }) {
|
|
|
2178
2443
|
] }),
|
|
2179
2444
|
/* @__PURE__ */ jsxs17("div", { className: "grid grid-cols-2 gap-3", children: [
|
|
2180
2445
|
/* @__PURE__ */ jsxs17("div", { children: [
|
|
2181
|
-
/* @__PURE__ */
|
|
2182
|
-
/* @__PURE__ */
|
|
2446
|
+
/* @__PURE__ */ jsx20("label", { htmlFor: "scheduling-service-buffer-before", className: LABEL_CLASS2, children: messages["service.bufferBeforeMinutes"] }),
|
|
2447
|
+
/* @__PURE__ */ jsx20(
|
|
2183
2448
|
"input",
|
|
2184
2449
|
{
|
|
2185
2450
|
id: "scheduling-service-buffer-before",
|
|
@@ -2192,8 +2457,8 @@ function ServiceForm({ initialValues, onSubmit }) {
|
|
|
2192
2457
|
)
|
|
2193
2458
|
] }),
|
|
2194
2459
|
/* @__PURE__ */ jsxs17("div", { children: [
|
|
2195
|
-
/* @__PURE__ */
|
|
2196
|
-
/* @__PURE__ */
|
|
2460
|
+
/* @__PURE__ */ jsx20("label", { htmlFor: "scheduling-service-buffer-after", className: LABEL_CLASS2, children: messages["service.bufferAfterMinutes"] }),
|
|
2461
|
+
/* @__PURE__ */ jsx20(
|
|
2197
2462
|
"input",
|
|
2198
2463
|
{
|
|
2199
2464
|
id: "scheduling-service-buffer-after",
|
|
@@ -2207,41 +2472,41 @@ function ServiceForm({ initialValues, onSubmit }) {
|
|
|
2207
2472
|
] })
|
|
2208
2473
|
] }),
|
|
2209
2474
|
initialValues && /* @__PURE__ */ jsxs17("label", { className: "flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300 min-h-11", children: [
|
|
2210
|
-
/* @__PURE__ */
|
|
2475
|
+
/* @__PURE__ */ jsx20("input", { type: "checkbox", checked: active, onChange: (event) => setActive(event.target.checked) }),
|
|
2211
2476
|
messages["service.active"]
|
|
2212
2477
|
] }),
|
|
2213
|
-
/* @__PURE__ */
|
|
2478
|
+
/* @__PURE__ */ jsx20("button", { type: "submit", disabled: submitting, className: BUTTON_PRIMARY3, children: messages["common.save"] })
|
|
2214
2479
|
] });
|
|
2215
2480
|
}
|
|
2216
2481
|
|
|
2217
2482
|
// src/components/ServiceList.tsx
|
|
2218
|
-
import { jsx as
|
|
2483
|
+
import { jsx as jsx21, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2219
2484
|
function ServiceList({ services, onSelect }) {
|
|
2220
2485
|
const { locale } = useSchedulingConfig();
|
|
2221
2486
|
const messages = resolveSchedulingMessages(locale);
|
|
2222
2487
|
if (services.length === 0) {
|
|
2223
|
-
return /* @__PURE__ */
|
|
2488
|
+
return /* @__PURE__ */ jsx21("p", { className: "text-sm text-gray-500 dark:text-gray-400", children: messages["common.empty"] });
|
|
2224
2489
|
}
|
|
2225
|
-
return /* @__PURE__ */
|
|
2490
|
+
return /* @__PURE__ */ jsx21("ul", { className: `${SURFACE_BORDER} divide-y divide-gray-200 overflow-hidden dark:divide-gray-800`, children: services.map((service, index) => /* @__PURE__ */ jsx21("li", { className: index % 2 === 1 ? ROW_STRIPE : void 0, children: /* @__PURE__ */ jsxs18(
|
|
2226
2491
|
"button",
|
|
2227
2492
|
{
|
|
2228
2493
|
type: "button",
|
|
2229
2494
|
onClick: () => onSelect(service),
|
|
2230
2495
|
className: "flex w-full items-center gap-3 px-4 py-3 min-h-11 text-left text-sm transition-colors hover:bg-brand-50/60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-brand-500 dark:hover:bg-brand-900/20",
|
|
2231
2496
|
children: [
|
|
2232
|
-
/* @__PURE__ */
|
|
2497
|
+
/* @__PURE__ */ jsx21("span", { className: "flex-1 font-medium text-gray-900 dark:text-gray-100", children: service.name }),
|
|
2233
2498
|
/* @__PURE__ */ jsxs18("span", { className: "text-gray-500 dark:text-gray-400", children: [
|
|
2234
2499
|
service.durationMinutes,
|
|
2235
2500
|
" min"
|
|
2236
2501
|
] }),
|
|
2237
|
-
!service.active && /* @__PURE__ */
|
|
2502
|
+
!service.active && /* @__PURE__ */ jsx21("span", { className: "rounded-full bg-gray-200 px-2 py-0.5 text-xs font-medium text-gray-600 dark:bg-gray-700 dark:text-gray-300", children: messages["service.inactive"] })
|
|
2238
2503
|
]
|
|
2239
2504
|
}
|
|
2240
2505
|
) }, service.id)) });
|
|
2241
2506
|
}
|
|
2242
2507
|
|
|
2243
2508
|
// src/workspace/ServicesArea.tsx
|
|
2244
|
-
import { jsx as
|
|
2509
|
+
import { jsx as jsx22, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2245
2510
|
function ServicesArea() {
|
|
2246
2511
|
const { locale } = useSchedulingConfig();
|
|
2247
2512
|
const messages = resolveSchedulingMessages(locale);
|
|
@@ -2249,7 +2514,7 @@ function ServicesArea() {
|
|
|
2249
2514
|
const createService = useCreateService();
|
|
2250
2515
|
const updateService = useUpdateService();
|
|
2251
2516
|
const deleteService = useDeleteService();
|
|
2252
|
-
const [draft, setDraft] =
|
|
2517
|
+
const [draft, setDraft] = useState13(void 0);
|
|
2253
2518
|
const isDraftOpen = draft !== void 0;
|
|
2254
2519
|
const isEditing = Boolean(draft);
|
|
2255
2520
|
const services = data?.data ?? [];
|
|
@@ -2266,17 +2531,17 @@ function ServicesArea() {
|
|
|
2266
2531
|
}
|
|
2267
2532
|
function renderCreateButton() {
|
|
2268
2533
|
return /* @__PURE__ */ jsxs19("button", { type: "button", onClick: () => setDraft(null), className: BUTTON_PRIMARY, children: [
|
|
2269
|
-
/* @__PURE__ */
|
|
2534
|
+
/* @__PURE__ */ jsx22(Plus4, { "aria-hidden": "true", className: "h-4 w-4" }),
|
|
2270
2535
|
messages["service.newService"]
|
|
2271
2536
|
] });
|
|
2272
2537
|
}
|
|
2273
2538
|
return /* @__PURE__ */ jsxs19("div", { className: "flex flex-1 min-h-0 min-w-0", children: [
|
|
2274
2539
|
/* @__PURE__ */ jsxs19("div", { className: "flex flex-1 min-h-0 min-w-0 flex-col gap-4 overflow-y-auto p-4", children: [
|
|
2275
|
-
/* @__PURE__ */
|
|
2276
|
-
isError && /* @__PURE__ */
|
|
2277
|
-
(createService.isError || updateService.isError || deleteService.isError) && /* @__PURE__ */
|
|
2278
|
-
isLoading && /* @__PURE__ */
|
|
2279
|
-
!isLoading && !isError && services.length === 0 && /* @__PURE__ */
|
|
2540
|
+
/* @__PURE__ */ jsx22("div", { className: "flex justify-end", children: renderCreateButton() }),
|
|
2541
|
+
isError && /* @__PURE__ */ jsx22(ErrorBanner, { message: messages["common.loadFailure"] }),
|
|
2542
|
+
(createService.isError || updateService.isError || deleteService.isError) && /* @__PURE__ */ jsx22(ErrorBanner, { message: messages["common.actionFailure"] }),
|
|
2543
|
+
isLoading && /* @__PURE__ */ jsx22(ListSkeleton, { label: messages["common.loading"] }),
|
|
2544
|
+
!isLoading && !isError && services.length === 0 && /* @__PURE__ */ jsx22(
|
|
2280
2545
|
EmptyState,
|
|
2281
2546
|
{
|
|
2282
2547
|
icon: Wrench,
|
|
@@ -2285,9 +2550,9 @@ function ServicesArea() {
|
|
|
2285
2550
|
action: renderCreateButton()
|
|
2286
2551
|
}
|
|
2287
2552
|
),
|
|
2288
|
-
!isLoading && services.length > 0 && /* @__PURE__ */
|
|
2553
|
+
!isLoading && services.length > 0 && /* @__PURE__ */ jsx22(ServiceList, { services, onSelect: setDraft })
|
|
2289
2554
|
] }),
|
|
2290
|
-
isDraftOpen && /* @__PURE__ */
|
|
2555
|
+
isDraftOpen && /* @__PURE__ */ jsx22(
|
|
2291
2556
|
SidePanel,
|
|
2292
2557
|
{
|
|
2293
2558
|
title: isEditing ? messages["service.editTitle"] : messages["service.createTitle"],
|
|
@@ -2303,12 +2568,12 @@ function ServicesArea() {
|
|
|
2303
2568
|
},
|
|
2304
2569
|
className: BUTTON_DANGER,
|
|
2305
2570
|
children: [
|
|
2306
|
-
/* @__PURE__ */
|
|
2571
|
+
/* @__PURE__ */ jsx22(Trash24, { "aria-hidden": "true", className: "h-4 w-4" }),
|
|
2307
2572
|
messages["common.remove"]
|
|
2308
2573
|
]
|
|
2309
2574
|
}
|
|
2310
2575
|
) : void 0,
|
|
2311
|
-
children: /* @__PURE__ */
|
|
2576
|
+
children: /* @__PURE__ */ jsx22(
|
|
2312
2577
|
ServiceForm,
|
|
2313
2578
|
{
|
|
2314
2579
|
...draft ? { initialValues: draft } : {},
|
|
@@ -2338,9 +2603,9 @@ function isSchedulingWorkspaceArea(value) {
|
|
|
2338
2603
|
}
|
|
2339
2604
|
|
|
2340
2605
|
// src/workspace/WorkspaceAreaNav.tsx
|
|
2341
|
-
import { jsx as
|
|
2606
|
+
import { jsx as jsx23, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2342
2607
|
var ITEM_BASE = "flex items-center gap-2 px-3 py-2 -mb-px border-b-2 text-sm font-medium transition-colors min-h-11 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-brand-500";
|
|
2343
|
-
var ITEM_ACTIVE = "border-brand-600 text-brand-700 dark:text-brand-
|
|
2608
|
+
var ITEM_ACTIVE = "border-brand-600 text-brand-700 dark:text-brand-400";
|
|
2344
2609
|
var ITEM_IDLE = "border-transparent text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100";
|
|
2345
2610
|
var AREA_ICON = {
|
|
2346
2611
|
[SCHEDULING_WORKSPACE_AREA.AGENDA]: CalendarDays2,
|
|
@@ -2357,7 +2622,7 @@ function WorkspaceAreaNav({ area, labels, onSelect }) {
|
|
|
2357
2622
|
[SCHEDULING_WORKSPACE_AREA.SERVICES, labels.servicesTab],
|
|
2358
2623
|
[SCHEDULING_WORKSPACE_AREA.AVAILABILITY, labels.availabilityTab]
|
|
2359
2624
|
];
|
|
2360
|
-
return /* @__PURE__ */
|
|
2625
|
+
return /* @__PURE__ */ jsx23("nav", { "aria-label": labels.areaNav, className: "flex shrink-0 gap-1 overflow-x-auto border-b border-gray-200 px-4 dark:border-gray-800", children: items.map(([value, label]) => {
|
|
2361
2626
|
const Icon = AREA_ICON[value];
|
|
2362
2627
|
return /* @__PURE__ */ jsxs20(
|
|
2363
2628
|
"button",
|
|
@@ -2367,7 +2632,7 @@ function WorkspaceAreaNav({ area, labels, onSelect }) {
|
|
|
2367
2632
|
"aria-current": value === area ? "page" : void 0,
|
|
2368
2633
|
className: `${ITEM_BASE} ${value === area ? ITEM_ACTIVE : ITEM_IDLE} whitespace-nowrap`,
|
|
2369
2634
|
children: [
|
|
2370
|
-
/* @__PURE__ */
|
|
2635
|
+
/* @__PURE__ */ jsx23(Icon, { "aria-hidden": "true", className: "w-4 h-4 shrink-0" }),
|
|
2371
2636
|
label
|
|
2372
2637
|
]
|
|
2373
2638
|
},
|
|
@@ -2377,7 +2642,7 @@ function WorkspaceAreaNav({ area, labels, onSelect }) {
|
|
|
2377
2642
|
}
|
|
2378
2643
|
|
|
2379
2644
|
// src/workspace/SchedulingWorkspace.tsx
|
|
2380
|
-
import { jsx as
|
|
2645
|
+
import { jsx as jsx24, jsxs as jsxs21 } from "react/jsx-runtime";
|
|
2381
2646
|
var AREA_COMPONENT = {
|
|
2382
2647
|
[SCHEDULING_WORKSPACE_AREA.AGENDA]: AgendaArea,
|
|
2383
2648
|
[SCHEDULING_WORKSPACE_AREA.BOOKINGS]: BookingsArea,
|
|
@@ -2392,7 +2657,7 @@ function SchedulingWorkspace({
|
|
|
2392
2657
|
onAreaChange
|
|
2393
2658
|
}) {
|
|
2394
2659
|
const labels = { ...DEFAULT_SCHEDULING_WORKSPACE_LABELS, ...labelsOverride };
|
|
2395
|
-
const [internalArea, setInternalArea] =
|
|
2660
|
+
const [internalArea, setInternalArea] = useState14(SCHEDULING_WORKSPACE_AREA.AGENDA);
|
|
2396
2661
|
const area = areaProp ?? internalArea;
|
|
2397
2662
|
const AreaComponent = AREA_COMPONENT[area];
|
|
2398
2663
|
function handleSelectArea(next) {
|
|
@@ -2401,11 +2666,11 @@ function SchedulingWorkspace({
|
|
|
2401
2666
|
}
|
|
2402
2667
|
return /* @__PURE__ */ jsxs21("div", { className: "flex h-full flex-col bg-white text-gray-900 dark:bg-gray-900 dark:text-gray-100", children: [
|
|
2403
2668
|
/* @__PURE__ */ jsxs21("header", { className: "flex shrink-0 flex-wrap items-center gap-3 px-4 pb-2 pt-4", children: [
|
|
2404
|
-
/* @__PURE__ */
|
|
2669
|
+
/* @__PURE__ */ jsx24("h1", { className: "mr-auto text-xl font-semibold tracking-tight text-gray-900 dark:text-gray-100", children: labels.title }),
|
|
2405
2670
|
renderHeaderActions?.()
|
|
2406
2671
|
] }),
|
|
2407
|
-
/* @__PURE__ */
|
|
2408
|
-
/* @__PURE__ */
|
|
2672
|
+
/* @__PURE__ */ jsx24(WorkspaceAreaNav, { area, labels, onSelect: handleSelectArea }),
|
|
2673
|
+
/* @__PURE__ */ jsx24("div", { className: "relative flex flex-1 min-h-0 overflow-hidden", children: /* @__PURE__ */ jsx24(AreaComponent, {}) })
|
|
2409
2674
|
] });
|
|
2410
2675
|
}
|
|
2411
2676
|
export {
|