@fayz-ai/plugin-agenda 0.1.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/components/AgendaMobileList.d.ts +10 -0
  2. package/dist/components/AgendaMobileList.d.ts.map +1 -0
  3. package/dist/components/AppointmentModal.d.ts +4 -2
  4. package/dist/components/AppointmentModal.d.ts.map +1 -1
  5. package/dist/components/AppointmentPopover.d.ts.map +1 -1
  6. package/dist/components/EventContextMenu.d.ts.map +1 -1
  7. package/dist/config.d.ts +62 -1
  8. package/dist/config.d.ts.map +1 -1
  9. package/dist/data/index.d.ts +1 -0
  10. package/dist/data/index.d.ts.map +1 -1
  11. package/dist/data/mock.d.ts +15 -1
  12. package/dist/data/mock.d.ts.map +1 -1
  13. package/dist/data/supabase.d.ts.map +1 -1
  14. package/dist/index.cjs +614 -308
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.ts +2 -0
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +452 -147
  19. package/dist/index.js.map +1 -1
  20. package/dist/locales/en.d.ts.map +1 -1
  21. package/dist/locales/pt-BR.d.ts.map +1 -1
  22. package/dist/store.d.ts +3 -0
  23. package/dist/store.d.ts.map +1 -1
  24. package/dist/types.d.ts +8 -0
  25. package/dist/types.d.ts.map +1 -1
  26. package/dist/views/CalendarView.d.ts.map +1 -1
  27. package/package.json +5 -8
  28. package/src/components/AgendaMobileList.tsx +103 -0
  29. package/src/components/AppointmentModal.tsx +86 -9
  30. package/src/components/AppointmentPopover.tsx +20 -10
  31. package/src/components/EventContextMenu.tsx +3 -0
  32. package/src/config.ts +104 -5
  33. package/src/data/index.ts +1 -0
  34. package/src/data/mock.ts +22 -5
  35. package/src/data/supabase.ts +13 -2
  36. package/src/index.ts +8 -3
  37. package/src/locales/en.ts +12 -0
  38. package/src/locales/pt-BR.ts +12 -0
  39. package/src/store.ts +4 -2
  40. package/src/types.ts +8 -0
  41. package/src/views/CalendarView.tsx +220 -54
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var chunkSYDBDI3Q_cjs = require('./chunk-SYDBDI3Q.cjs');
4
- var React11 = require('react');
4
+ var React12 = require('react');
5
5
  var core = require('@fayz-ai/core');
6
6
  var saas = require('@fayz-ai/saas');
7
7
  var FullCalendar = require('@fullcalendar/react');
@@ -19,7 +19,7 @@ var vanilla = require('zustand/vanilla');
19
19
 
20
20
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
21
21
 
22
- var React11__default = /*#__PURE__*/_interopDefault(React11);
22
+ var React12__default = /*#__PURE__*/_interopDefault(React12);
23
23
  var FullCalendar__default = /*#__PURE__*/_interopDefault(FullCalendar);
24
24
  var dayGridPlugin__default = /*#__PURE__*/_interopDefault(dayGridPlugin);
25
25
  var timeGridPlugin__default = /*#__PURE__*/_interopDefault(timeGridPlugin);
@@ -80,23 +80,47 @@ var DEFAULT_BOOKING_TYPES = [
80
80
  requiresClient: false
81
81
  }
82
82
  ];
83
+ var SIMPLE_EVENT_BOOKING_TYPES = [
84
+ {
85
+ id: "event",
86
+ label: "Event",
87
+ icon: "Calendar",
88
+ color: "#6366f1",
89
+ fields: { title: true, client: false, professional: false, services: false, location: false, status: false },
90
+ requiresServices: false,
91
+ requiresClient: false
92
+ }
93
+ ];
83
94
  function resolveConfig(options) {
95
+ const simple = options?.eventMode === "simple";
96
+ const bookingTypes = options?.bookingTypes ?? (simple ? SIMPLE_EVENT_BOOKING_TYPES : DEFAULT_BOOKING_TYPES);
97
+ const capabilities = {
98
+ title: bookingTypes.some((bt) => bt.fields.title === true),
99
+ client: bookingTypes.some((bt) => bt.fields.client),
100
+ professional: bookingTypes.some((bt) => bt.fields.professional),
101
+ services: bookingTypes.some((bt) => bt.fields.services),
102
+ location: bookingTypes.some((bt) => bt.fields.location),
103
+ status: bookingTypes.some((bt) => bt.fields.status)
104
+ };
84
105
  return {
85
106
  modules: {
86
- workingHours: options?.modules?.workingHours !== false,
87
- confirmations: options?.modules?.confirmations !== false,
88
- conflictDetection: options?.modules?.conflictDetection !== false,
107
+ // Off-unless-explicit in simple mode (`?? !simple`); default-on in full.
108
+ workingHours: options?.modules?.workingHours ?? !simple,
109
+ confirmations: options?.modules?.confirmations ?? !simple,
110
+ conflictDetection: options?.modules?.conflictDetection ?? !simple,
89
111
  dragAndDrop: options?.modules?.dragAndDrop !== false,
90
112
  locationSelection: options?.modules?.locationSelection === true,
91
113
  financial: options?.modules?.financial ?? !!options?.financialBridge
92
114
  },
115
+ simpleMode: simple,
116
+ capabilities,
93
117
  labels: { ...DEFAULT_LABELS, ...options?.labels },
94
118
  currency: { ...DEFAULT_CURRENCY, ...options?.currency },
95
119
  bookingKind: options?.bookingKind ?? "appointment",
96
120
  orderKind: options?.orderKind ?? "service_order",
97
121
  scheduleKind: options?.scheduleKind ?? "working_hours",
98
122
  statuses: options?.statuses ?? DEFAULT_STATUSES,
99
- bookingTypes: options?.bookingTypes ?? DEFAULT_BOOKING_TYPES,
123
+ bookingTypes,
100
124
  businessHours: options?.businessHours ?? { startTime: "07:00", endTime: "21:00" },
101
125
  slotDuration: options?.slotDuration ?? 30,
102
126
  professionalKind: options?.professionalKind ?? "staff",
@@ -104,6 +128,8 @@ function resolveConfig(options) {
104
128
  autoCreateOrder: options?.autoCreateOrder !== false,
105
129
  locations: options?.locations ?? [],
106
130
  confirmationChannels: options?.confirmationChannels ?? [],
131
+ cancellationDetails: options?.cancellationDetails,
132
+ onBookingCreated: options?.onBookingCreated,
107
133
  contactLookup: options?.contactLookup,
108
134
  clientEntityDef: options?.clientEntityDef,
109
135
  serviceLookup: options?.serviceLookup,
@@ -431,10 +457,10 @@ function toDateLocale(locale) {
431
457
  }
432
458
  function MiniCalendar({ selectedDate, onDateSelect, onMonthChange }) {
433
459
  const locale = core.getCurrentLocale();
434
- const [viewMonth, setViewMonth] = React11__default.default.useState(() => new Date(selectedDate.getFullYear(), selectedDate.getMonth(), 1));
460
+ const [viewMonth, setViewMonth] = React12__default.default.useState(() => new Date(selectedDate.getFullYear(), selectedDate.getMonth(), 1));
435
461
  const monthLabel = viewMonth.toLocaleDateString(toDateLocale(locale), { month: "long", year: "numeric" });
436
462
  const dayLabels = DAY_LABELS[locale] ?? DAY_LABELS.en;
437
- const weeks = React11.useMemo(() => {
463
+ const weeks = React12.useMemo(() => {
438
464
  const year = viewMonth.getFullYear();
439
465
  const month = viewMonth.getMonth();
440
466
  const firstDay = new Date(year, month, 1);
@@ -505,14 +531,14 @@ function SearchCombobox({
505
531
  loading,
506
532
  onFocus: onFocusProp
507
533
  }) {
508
- const [open, setOpen] = React11.useState(false);
509
- const [activeIndex, setActiveIndex] = React11.useState(-1);
510
- const [expanded, setExpanded] = React11.useState(!inlineLabel);
511
- const [dropRect, setDropRect] = React11.useState(null);
512
- const inputRef = React11.useRef(null);
513
- const wrapperRef = React11.useRef(null);
514
- const listRef = React11.useRef(null);
515
- React11.useEffect(() => {
534
+ const [open, setOpen] = React12.useState(false);
535
+ const [activeIndex, setActiveIndex] = React12.useState(-1);
536
+ const [expanded, setExpanded] = React12.useState(!inlineLabel);
537
+ const [dropRect, setDropRect] = React12.useState(null);
538
+ const inputRef = React12.useRef(null);
539
+ const wrapperRef = React12.useRef(null);
540
+ const listRef = React12.useRef(null);
541
+ React12.useEffect(() => {
516
542
  if (value && inlineLabel) setExpanded(true);
517
543
  if (!value && inlineLabel && !open) setExpanded(false);
518
544
  }, [value, open]);
@@ -521,13 +547,13 @@ function SearchCombobox({
521
547
  }
522
548
  const showCreate = open && allowCreate && onCreateNew && value.trim().length > 0 && !options.some((o) => o.label.toLowerCase() === value.trim().toLowerCase());
523
549
  const totalItems = options.length + (showCreate ? 1 : 0);
524
- React11.useEffect(() => {
550
+ React12.useEffect(() => {
525
551
  setActiveIndex(-1);
526
552
  }, [options]);
527
- React11.useEffect(() => {
553
+ React12.useEffect(() => {
528
554
  if ((options.length > 0 || showCreate || loading) && document.activeElement === inputRef.current) setOpen(true);
529
555
  }, [options, showCreate, loading]);
530
- const handleKeyDown = React11.useCallback((e) => {
556
+ const handleKeyDown = React12.useCallback((e) => {
531
557
  if (!open || totalItems === 0) {
532
558
  if (e.key === "ArrowDown" && totalItems > 0) {
533
559
  e.preventDefault();
@@ -564,7 +590,7 @@ function SearchCombobox({
564
590
  break;
565
591
  }
566
592
  }, [open, options, totalItems, activeIndex, onSelect, showCreate, onCreateNew, value]);
567
- React11.useEffect(() => {
593
+ React12.useEffect(() => {
568
594
  if (activeIndex < 0 || !listRef.current) return;
569
595
  const items = listRef.current.children;
570
596
  if (items[activeIndex]) items[activeIndex].scrollIntoView({ block: "nearest" });
@@ -720,27 +746,27 @@ var ICON_COLORS = {
720
746
  bank_transfer: "text-warning",
721
747
  check: "text-muted-foreground"
722
748
  };
723
- function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPaymentChange, formatCurrency, inline }) {
749
+ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPaymentChange, formatCurrency: formatCurrency2, inline }) {
724
750
  const t = core.useTranslation();
725
751
  const config = useAgendaConfig();
726
752
  const bridge = config.financialBridge;
727
- const [visible, setVisible] = React11.useState(false);
728
- const [loading, setLoading] = React11.useState(true);
729
- const [detail, setDetail] = React11.useState(null);
730
- const [selectedTypeId, setSelectedTypeId] = React11.useState("");
731
- const [paymentTypes, setPaymentTypes] = React11.useState([]);
732
- const [bankAccounts, setBankAccounts] = React11.useState([]);
733
- const [filteredMethods, setFilteredMethods] = React11.useState([]);
734
- const [selectedBankId, setSelectedBankId] = React11.useState("");
735
- const [paymentMethodId, setPaymentMethodId] = React11.useState("");
736
- const [cardBrand, setCardBrand] = React11.useState("");
737
- const [cardInstallments, setCardInstallments] = React11.useState(1);
738
- const [processing, setProcessing] = React11.useState(false);
739
- const [justPaid, setJustPaid] = React11.useState(false);
740
- React11.useEffect(() => {
753
+ const [visible, setVisible] = React12.useState(false);
754
+ const [loading, setLoading] = React12.useState(true);
755
+ const [detail, setDetail] = React12.useState(null);
756
+ const [selectedTypeId, setSelectedTypeId] = React12.useState("");
757
+ const [paymentTypes, setPaymentTypes] = React12.useState([]);
758
+ const [bankAccounts, setBankAccounts] = React12.useState([]);
759
+ const [filteredMethods, setFilteredMethods] = React12.useState([]);
760
+ const [selectedBankId, setSelectedBankId] = React12.useState("");
761
+ const [paymentMethodId, setPaymentMethodId] = React12.useState("");
762
+ const [cardBrand, setCardBrand] = React12.useState("");
763
+ const [cardInstallments, setCardInstallments] = React12.useState(1);
764
+ const [processing, setProcessing] = React12.useState(false);
765
+ const [justPaid, setJustPaid] = React12.useState(false);
766
+ React12.useEffect(() => {
741
767
  requestAnimationFrame(() => setVisible(true));
742
768
  }, []);
743
- React11.useEffect(() => {
769
+ React12.useEffect(() => {
744
770
  let cancelled = false;
745
771
  setLoading(true);
746
772
  Promise.all([
@@ -802,7 +828,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
802
828
  !loading && isPaid && justPaid && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "text-center py-8 space-y-2 animate-in fade-in zoom-in-95 duration-300", children: [
803
829
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-14 w-14 mx-auto items-center justify-center rounded-full bg-success/10 animate-bounce", style: { animationDuration: "600ms", animationIterationCount: "2" }, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-7 w-7 text-success" }) }),
804
830
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-success", children: t("agenda.payment.checkoutComplete") }),
805
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: formatCurrency(detail.totalAmount) })
831
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: formatCurrency2(detail.totalAmount) })
806
832
  ] }),
807
833
  !loading && detail && detail.status !== "none" && !justPaid && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 animate-in fade-in duration-300", children: [
808
834
  detail.movements.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: detail.movements.map((mov) => {
@@ -834,21 +860,21 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
834
860
  ] }),
835
861
  isPartial && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-warning/10 px-2 py-0.5 text-[10px] font-medium text-warning", children: [
836
862
  "! ",
837
- formatCurrency(mov.paidAmount),
863
+ formatCurrency2(mov.paidAmount),
838
864
  "/",
839
- formatCurrency(mov.amount)
865
+ formatCurrency2(mov.amount)
840
866
  ] }),
841
867
  isOverdue && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-destructive/10 px-2 py-0.5 text-[10px] font-medium text-destructive", children: [
842
868
  "! ",
843
869
  t("agenda.payment.overdue")
844
870
  ] }),
845
871
  mov.status === "pending" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-flex items-center rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground", children: t("agenda.payment.pending") }),
846
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: formatCurrency(mov.paidAmount || mov.amount) })
872
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: formatCurrency2(mov.paidAmount || mov.amount) })
847
873
  ] })
848
874
  ] }, mov.id);
849
875
  }) }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm px-1", children: [
850
876
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: t("agenda.payment.total") }),
851
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: formatCurrency(detail.totalAmount) })
877
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: formatCurrency2(detail.totalAmount) })
852
878
  ] }),
853
879
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
854
880
  detail.referenceNumber && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] font-mono text-muted-foreground", children: [
@@ -873,12 +899,12 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
873
899
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-medium text-muted-foreground mb-2", children: t("agenda.payment.serviceSummary") }),
874
900
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-0.5", children: services.map((svc, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm py-1", children: [
875
901
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: svc.name }),
876
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground", children: formatCurrency(svc.price) })
902
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground", children: formatCurrency2(svc.price) })
877
903
  ] }, i)) })
878
904
  ] }),
879
905
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm font-semibold pt-2 border-t", children: [
880
906
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("agenda.payment.total") }),
881
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatCurrency(orderTotal) })
907
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatCurrency2(orderTotal) })
882
908
  ] }),
883
909
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
884
910
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-medium text-muted-foreground mb-2", children: t("agenda.payment.paymentMethod") }),
@@ -953,7 +979,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
953
979
  children: Array.from({ length: 12 }, (_, i) => i + 1).map((n) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: n, children: [
954
980
  n,
955
981
  "x ",
956
- n > 1 ? formatCurrency(orderTotal / n) : ""
982
+ n > 1 ? formatCurrency2(orderTotal / n) : ""
957
983
  ] }, n))
958
984
  }
959
985
  )
@@ -982,7 +1008,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
982
1008
  onClick: handleCheckout,
983
1009
  disabled: processing || !selectedTypeId,
984
1010
  className: "w-full rounded-lg bg-primary border border-primary py-2.5 text-sm font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset disabled:opacity-40 transition-colors",
985
- children: processing ? t("agenda.payment.checkingOut") : `${t("agenda.payment.checkout")} ${formatCurrency(orderTotal)}`
1011
+ children: processing ? t("agenda.payment.checkingOut") : `${t("agenda.payment.checkout")} ${formatCurrency2(orderTotal)}`
986
1012
  }
987
1013
  )
988
1014
  ] })
@@ -1043,9 +1069,9 @@ function formatCompactDate(dateStr, locale) {
1043
1069
  });
1044
1070
  }
1045
1071
  function InlineNumber({ value, onChange, min = 0, step = 1, className, disabled, tooltip }) {
1046
- const [draft, setDraft] = React11.useState(String(value));
1047
- const [focused, setFocused] = React11.useState(false);
1048
- React11.useEffect(() => {
1072
+ const [draft, setDraft] = React12.useState(String(value));
1073
+ const [focused, setFocused] = React12.useState(false);
1074
+ React12.useEffect(() => {
1049
1075
  if (!focused) setDraft(String(value));
1050
1076
  }, [value, focused]);
1051
1077
  function commit() {
@@ -1089,11 +1115,11 @@ function InlineNumber({ value, onChange, min = 0, step = 1, className, disabled,
1089
1115
  ] }) });
1090
1116
  }
1091
1117
  function StatusSelect({ value, onChange, statuses, bookingDate }) {
1092
- const [open, setOpen] = React11.useState(false);
1093
- const ref = React11.useRef(null);
1094
- const dropdownRef = React11.useRef(null);
1118
+ const [open, setOpen] = React12.useState(false);
1119
+ const ref = React12.useRef(null);
1120
+ const dropdownRef = React12.useRef(null);
1095
1121
  const current = statuses.find((s) => s.value === value);
1096
- React11.useEffect(() => {
1122
+ React12.useEffect(() => {
1097
1123
  if (!open) return;
1098
1124
  const handler = (e) => {
1099
1125
  const target = e.target;
@@ -1104,8 +1130,8 @@ function StatusSelect({ value, onChange, statuses, bookingDate }) {
1104
1130
  document.addEventListener("mousedown", handler);
1105
1131
  return () => document.removeEventListener("mousedown", handler);
1106
1132
  }, [open]);
1107
- const btnRef = React11.useRef(null);
1108
- const [dropRect, setDropRect] = React11.useState(null);
1133
+ const btnRef = React12.useRef(null);
1134
+ const [dropRect, setDropRect] = React12.useState(null);
1109
1135
  function handleOpen() {
1110
1136
  if (btnRef.current) setDropRect(btnRef.current.getBoundingClientRect());
1111
1137
  setOpen((p) => !p);
@@ -1165,10 +1191,10 @@ function StatusSelect({ value, onChange, statuses, bookingDate }) {
1165
1191
  }
1166
1192
  function ProfessionalSelect({ value, onChange, professionals }) {
1167
1193
  const t = core.useTranslation();
1168
- const [open, setOpen] = React11.useState(false);
1169
- const ref = React11.useRef(null);
1194
+ const [open, setOpen] = React12.useState(false);
1195
+ const ref = React12.useRef(null);
1170
1196
  const current = professionals.find((p) => p.id === value);
1171
- React11.useEffect(() => {
1197
+ React12.useEffect(() => {
1172
1198
  if (!open) return;
1173
1199
  const handler = (e) => {
1174
1200
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -1206,10 +1232,10 @@ function ProfessionalSelect({ value, onChange, professionals }) {
1206
1232
  }
1207
1233
  function LocationSelect({ value, onChange, locations }) {
1208
1234
  const t = core.useTranslation();
1209
- const [open, setOpen] = React11.useState(false);
1210
- const ref = React11.useRef(null);
1235
+ const [open, setOpen] = React12.useState(false);
1236
+ const ref = React12.useRef(null);
1211
1237
  const current = locations.find((l) => l.id === value);
1212
- React11.useEffect(() => {
1238
+ React12.useEffect(() => {
1213
1239
  if (!open) return;
1214
1240
  const handler = (e) => {
1215
1241
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -1245,12 +1271,12 @@ function LocationSelect({ value, onChange, locations }) {
1245
1271
  )) })
1246
1272
  ] });
1247
1273
  }
1248
- function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose, onCreated }) {
1274
+ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, externalSource, onClose, onCreated }) {
1249
1275
  const t = core.useTranslation();
1250
1276
  const locale = core.getCurrentLocale();
1251
1277
  const config = useAgendaConfig();
1252
1278
  const provider2 = useAgendaProvider();
1253
- const initialSnapshot = React11.useRef("");
1279
+ const initialSnapshot = React12.useRef("");
1254
1280
  const createBooking = useAgendaStore((s) => s.createBooking);
1255
1281
  const updateBooking = useAgendaStore((s) => s.updateBooking);
1256
1282
  const deleteBooking = useAgendaStore((s) => s.deleteBooking);
@@ -1258,54 +1284,60 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1258
1284
  const agendaDateRange = useAgendaStore((s) => s.dateRange);
1259
1285
  const professionals = useAgendaStore((s) => s.professionals);
1260
1286
  const storeLocations = useAgendaStore((s) => s.locations);
1261
- const [loading, setLoading] = React11.useState(mode === "edit");
1262
- const [bookingType, setBookingType] = React11.useState(prefill?.kind ?? "appointment");
1263
- const [clientId, setClientId] = React11.useState(prefill?.clientId ?? "");
1264
- const [clientSearch, setClientSearch] = React11.useState("");
1265
- const [clientPhone, setClientPhone] = React11.useState("");
1266
- const [professionalId, setProfessionalId] = React11.useState(prefill?.professionalId ?? "");
1267
- const [locationId, setLocationId] = React11.useState(prefill?.locationId ?? "");
1268
- const [services, setServices] = React11.useState([]);
1269
- const [serviceSearch, setServiceSearch] = React11.useState("");
1270
- const [date, setDate] = React11.useState(() => {
1287
+ const defaultBookingType = config.bookingTypes[0]?.id ?? "appointment";
1288
+ const [loading, setLoading] = React12.useState(mode === "edit");
1289
+ const [bookingType, setBookingType] = React12.useState(prefill?.kind ?? defaultBookingType);
1290
+ const [title, setTitle] = React12.useState("");
1291
+ const [clientId, setClientId] = React12.useState(prefill?.clientId ?? "");
1292
+ const [clientSearch, setClientSearch] = React12.useState("");
1293
+ const [clientPhone, setClientPhone] = React12.useState("");
1294
+ const [professionalId, setProfessionalId] = React12.useState(prefill?.professionalId ?? "");
1295
+ const [locationId, setLocationId] = React12.useState(prefill?.locationId ?? "");
1296
+ const [services, setServices] = React12.useState([]);
1297
+ const [serviceSearch, setServiceSearch] = React12.useState("");
1298
+ const [date, setDate] = React12.useState(() => {
1271
1299
  if (prefill?.startsAt) return prefill.startsAt.slice(0, 10);
1272
1300
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1273
1301
  });
1274
- const [startTime, setStartTime] = React11.useState(() => {
1302
+ const [startTime, setStartTime] = React12.useState(() => {
1275
1303
  if (prefill?.startsAt) {
1276
1304
  const d = new Date(prefill.startsAt);
1277
1305
  return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
1278
1306
  }
1279
1307
  return "09:00";
1280
1308
  });
1281
- const [status, setStatus] = React11.useState("scheduled");
1282
- const [notes, setNotes] = React11.useState("");
1283
- const [showNotes, setShowNotes] = React11.useState(false);
1284
- const [saving, setSaving] = React11.useState(false);
1285
- const [conflict, setConflict] = React11.useState(false);
1286
- const [quickCreate, setQuickCreate] = React11.useState(false);
1287
- const [newClientName, setNewClientName] = React11.useState("");
1288
- const [newClientPhone, setNewClientPhone] = React11.useState("");
1289
- const [newClientEmail, setNewClientEmail] = React11.useState("");
1290
- const [creatingClient, setCreatingClient] = React11.useState(false);
1291
- const [clientOptions, setClientOptions] = React11.useState([]);
1292
- const [clientSearching, setClientSearching] = React11.useState(false);
1293
- const [serviceOptions, setServiceOptions] = React11.useState([]);
1294
- const [initialServiceOptions, setInitialServiceOptions] = React11.useState([]);
1295
- const [editingDate, setEditingDate] = React11.useState(false);
1296
- const [editingTime, setEditingTime] = React11.useState(false);
1297
- const [confirmingDelete, setConfirmingDelete] = React11.useState(false);
1298
- const [deleting, setDeleting] = React11.useState(false);
1299
- const [editOrderId, setEditOrderId] = React11.useState(null);
1300
- const [editOrderTotal, setEditOrderTotal] = React11.useState(0);
1301
- const [editPaymentStatus, setEditPaymentStatus] = React11.useState("none");
1302
- const [paymentStatusLoading, setPaymentStatusLoading] = React11.useState(false);
1303
- const [modalTab, setModalTab] = React11.useState("appointment");
1304
- const [clientData, setClientData] = React11.useState(null);
1305
- const [clientDataLoading, setClientDataLoading] = React11.useState(false);
1306
- const [clientEditing, setClientEditing] = React11.useState(false);
1309
+ const [status, setStatus] = React12.useState("scheduled");
1310
+ const [notes, setNotes] = React12.useState("");
1311
+ const [showNotes, setShowNotes] = React12.useState(false);
1312
+ const [saving, setSaving] = React12.useState(false);
1313
+ const [conflict, setConflict] = React12.useState(false);
1314
+ const [quickCreate, setQuickCreate] = React12.useState(false);
1315
+ const [newClientName, setNewClientName] = React12.useState("");
1316
+ const [newClientPhone, setNewClientPhone] = React12.useState("");
1317
+ const [newClientEmail, setNewClientEmail] = React12.useState("");
1318
+ const [creatingClient, setCreatingClient] = React12.useState(false);
1319
+ const [clientOptions, setClientOptions] = React12.useState([]);
1320
+ const [clientSearching, setClientSearching] = React12.useState(false);
1321
+ const [serviceOptions, setServiceOptions] = React12.useState([]);
1322
+ const [initialServiceOptions, setInitialServiceOptions] = React12.useState([]);
1323
+ const [editingDate, setEditingDate] = React12.useState(false);
1324
+ const [editingTime, setEditingTime] = React12.useState(false);
1325
+ const [confirmingDelete, setConfirmingDelete] = React12.useState(false);
1326
+ const [deleting, setDeleting] = React12.useState(false);
1327
+ const [editOrderId, setEditOrderId] = React12.useState(null);
1328
+ const [editOrderTotal, setEditOrderTotal] = React12.useState(0);
1329
+ const [editPaymentStatus, setEditPaymentStatus] = React12.useState("none");
1330
+ const [paymentStatusLoading, setPaymentStatusLoading] = React12.useState(false);
1331
+ const [modalTab, setModalTab] = React12.useState("appointment");
1332
+ const [clientData, setClientData] = React12.useState(null);
1333
+ const [clientDataLoading, setClientDataLoading] = React12.useState(false);
1334
+ const [clientEditing, setClientEditing] = React12.useState(false);
1335
+ const [cancellationReasons, setCancellationReasons] = React12.useState([]);
1336
+ const [cancellationReasonId, setCancellationReasonId] = React12.useState("");
1337
+ const [cancellationNotes, setCancellationNotes] = React12.useState("");
1338
+ const [cancellationReasonsLoading, setCancellationReasonsLoading] = React12.useState(false);
1307
1339
  const activeType = config.bookingTypes.find((bt) => bt.id === bookingType) ?? config.bookingTypes[0];
1308
- React11.useEffect(() => {
1340
+ React12.useEffect(() => {
1309
1341
  if (!open) return;
1310
1342
  setClientId(prefill?.clientId ?? "");
1311
1343
  setClientSearch("");
@@ -1324,7 +1356,8 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1324
1356
  setNewClientPhone("");
1325
1357
  setNewClientEmail("");
1326
1358
  setCreatingClient(false);
1327
- setBookingType(prefill?.kind ?? "appointment");
1359
+ setBookingType(prefill?.kind ?? defaultBookingType);
1360
+ setTitle("");
1328
1361
  setEditingDate(false);
1329
1362
  setEditingTime(false);
1330
1363
  setConfirmingDelete(false);
@@ -1336,6 +1369,9 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1336
1369
  setClientData(null);
1337
1370
  setClientDataLoading(false);
1338
1371
  setClientEditing(false);
1372
+ setCancellationReasonId("");
1373
+ setCancellationNotes("");
1374
+ setCancellationReasonsLoading(false);
1339
1375
  setModalTab(initialTab || "appointment");
1340
1376
  if (!prefill?.professionalId && professionals.length === 1) setProfessionalId(professionals[0].id);
1341
1377
  setDate(() => {
@@ -1350,7 +1386,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1350
1386
  return "09:00";
1351
1387
  });
1352
1388
  }, [open]);
1353
- React11.useEffect(() => {
1389
+ React12.useEffect(() => {
1354
1390
  if (!open || mode !== "edit" || !bookingId) return;
1355
1391
  setLoading(true);
1356
1392
  provider2.getBookingById(bookingId).then((b) => {
@@ -1358,7 +1394,8 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1358
1394
  setLoading(false);
1359
1395
  return;
1360
1396
  }
1361
- setBookingType(b.kind ?? "appointment");
1397
+ setBookingType(b.kind ?? defaultBookingType);
1398
+ setTitle(b.clientName ?? "");
1362
1399
  setClientId(b.clientId ?? "");
1363
1400
  setClientSearch(b.clientName ?? "");
1364
1401
  setProfessionalId(b.professionalId ?? "");
@@ -1380,6 +1417,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1380
1417
  setEditPaymentStatus(b.paymentStatus ?? "none");
1381
1418
  const svcSnap = b.services ? b.services.map((s) => ({ id: s.serviceId ?? "", dur: s.durationMinutes, price: s.price })) : [];
1382
1419
  initialSnapshot.current = JSON.stringify({
1420
+ title: b.clientName ?? "",
1383
1421
  clientId: b.clientId ?? "",
1384
1422
  professionalId: b.professionalId ?? "",
1385
1423
  locationId: b.locationId ?? "",
@@ -1387,6 +1425,8 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1387
1425
  startTime: `${String(new Date(b.startsAt).getHours()).padStart(2, "0")}:${String(new Date(b.startsAt).getMinutes()).padStart(2, "0")}`,
1388
1426
  status: b.status,
1389
1427
  notes: b.notes ?? "",
1428
+ cancellationReasonId: "",
1429
+ cancellationNotes: "",
1390
1430
  services: svcSnap
1391
1431
  });
1392
1432
  setLoading(false);
@@ -1403,7 +1443,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1403
1443
  }
1404
1444
  }).catch(() => setLoading(false));
1405
1445
  }, [open, mode, bookingId]);
1406
- React11.useEffect(() => {
1446
+ React12.useEffect(() => {
1407
1447
  if (modalTab !== "client" || !clientId || !config.contactLookup || clientData) return;
1408
1448
  setClientDataLoading(true);
1409
1449
  config.contactLookup.getById(clientId).then((result) => {
@@ -1411,7 +1451,12 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1411
1451
  setClientDataLoading(false);
1412
1452
  }).catch(() => setClientDataLoading(false));
1413
1453
  }, [modalTab, clientId]);
1414
- React11.useEffect(() => {
1454
+ React12.useEffect(() => {
1455
+ if (!open || status !== "cancelled" || !config.cancellationDetails || cancellationReasons.length > 0) return;
1456
+ setCancellationReasonsLoading(true);
1457
+ config.cancellationDetails.listReasons().then((reasons) => setCancellationReasons(reasons)).catch(() => setCancellationReasons([])).finally(() => setCancellationReasonsLoading(false));
1458
+ }, [open, status, config.cancellationDetails, cancellationReasons.length]);
1459
+ React12.useEffect(() => {
1415
1460
  if (!open || !config.serviceLookup) return;
1416
1461
  const lookup = config.serviceLookup;
1417
1462
  const fetch = lookup.list ? () => lookup.list() : () => lookup.search("");
@@ -1420,7 +1465,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1420
1465
  setInitialServiceOptions(opts);
1421
1466
  }).catch(() => setInitialServiceOptions([]));
1422
1467
  }, [open]);
1423
- React11.useEffect(() => {
1468
+ React12.useEffect(() => {
1424
1469
  if (!config.contactLookup || clientSearch.length < 1 || clientId) {
1425
1470
  setClientOptions([]);
1426
1471
  setClientSearching(false);
@@ -1438,7 +1483,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1438
1483
  }, 250);
1439
1484
  return () => clearTimeout(timer);
1440
1485
  }, [clientSearch, clientId]);
1441
- React11.useEffect(() => {
1486
+ React12.useEffect(() => {
1442
1487
  if (!config.serviceLookup || serviceSearch.length < 1) {
1443
1488
  setServiceOptions([]);
1444
1489
  return;
@@ -1454,19 +1499,19 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1454
1499
  return () => clearTimeout(timer);
1455
1500
  }, [serviceSearch]);
1456
1501
  const serviceDuration = services.reduce((sum, s) => sum + s.durationMinutes, 0);
1457
- const selectionDuration = React11.useMemo(() => {
1502
+ const selectionDuration = React12.useMemo(() => {
1458
1503
  if (!prefill?.startsAt || !prefill?.endsAt) return 0;
1459
1504
  return Math.round((new Date(prefill.endsAt).getTime() - new Date(prefill.startsAt).getTime()) / 6e4);
1460
1505
  }, [prefill?.startsAt, prefill?.endsAt]);
1461
1506
  const totalDuration = serviceDuration > 0 ? serviceDuration : selectionDuration;
1462
1507
  const totalPrice = services.reduce((sum, s) => sum + s.price, 0);
1463
1508
  const { currency } = config;
1464
- const endTime = React11.useMemo(() => {
1509
+ const endTime = React12.useMemo(() => {
1465
1510
  const [h, m] = startTime.split(":").map(Number);
1466
1511
  const total = h * 60 + m + totalDuration;
1467
1512
  return `${String(Math.floor(total / 60) % 24).padStart(2, "0")}:${String(total % 60).padStart(2, "0")}`;
1468
1513
  }, [startTime, totalDuration]);
1469
- React11.useEffect(() => {
1514
+ React12.useEffect(() => {
1470
1515
  if (!professionalId || totalDuration === 0) {
1471
1516
  setConflict(false);
1472
1517
  return;
@@ -1510,10 +1555,11 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1510
1555
  locationId: locationId || void 0,
1511
1556
  startsAt,
1512
1557
  notes: notes || void 0,
1558
+ title: activeType.fields.title ? title.trim() : void 0,
1513
1559
  services: services.map((s) => ({ serviceId: s.serviceId, name: s.name, durationMinutes: s.durationMinutes, price: s.price }))
1514
1560
  });
1515
1561
  onClose();
1516
- onCreated?.(newBooking.id);
1562
+ onCreated?.(newBooking.id, externalSource);
1517
1563
  } else if (bookingId) {
1518
1564
  await updateBooking(bookingId, {
1519
1565
  clientId,
@@ -1522,8 +1568,17 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1522
1568
  startsAt,
1523
1569
  notes: notes || void 0,
1524
1570
  status,
1571
+ title: activeType.fields.title ? title.trim() : void 0,
1525
1572
  services: services.map((s) => ({ serviceId: s.serviceId, name: s.name, durationMinutes: s.durationMinutes, price: s.price }))
1526
1573
  });
1574
+ if (status === "cancelled" && config.cancellationDetails && cancellationReasonId) {
1575
+ await config.cancellationDetails.saveCancellationDetails({
1576
+ bookingId,
1577
+ reasonId: cancellationReasonId,
1578
+ notes: cancellationNotes.trim() || void 0,
1579
+ cancelledAt: (/* @__PURE__ */ new Date()).toISOString()
1580
+ });
1581
+ }
1527
1582
  onClose();
1528
1583
  }
1529
1584
  } catch {
@@ -1541,12 +1596,15 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1541
1596
  setDeleting(false);
1542
1597
  }
1543
1598
  const isPaidBooking = editPaymentStatus === "paid";
1544
- const formSnapshot = React11.useMemo(
1545
- () => JSON.stringify({ clientId, professionalId, locationId, date, startTime, status, notes, services: services.map((s) => ({ id: s.serviceId, dur: s.durationMinutes, price: s.price })) }),
1546
- [clientId, professionalId, locationId, date, startTime, status, notes, services]
1599
+ const selectedCancellationReason = cancellationReasons.find((reason) => reason.id === cancellationReasonId);
1600
+ const needsCancellationReason = mode === "edit" && status === "cancelled" && !!config.cancellationDetails;
1601
+ const needsCancellationNotes = needsCancellationReason && !!selectedCancellationReason?.requiresNotes;
1602
+ const formSnapshot = React12.useMemo(
1603
+ () => JSON.stringify({ title, clientId, professionalId, locationId, date, startTime, status, notes, cancellationReasonId, cancellationNotes, services: services.map((s) => ({ id: s.serviceId, dur: s.durationMinutes, price: s.price })) }),
1604
+ [title, clientId, professionalId, locationId, date, startTime, status, notes, cancellationReasonId, cancellationNotes, services]
1547
1605
  );
1548
1606
  const isDirty = mode === "create" || formSnapshot !== initialSnapshot.current;
1549
- const canSubmit = (!activeType.requiresClient || clientId) && (!activeType.fields.professional || professionalId) && (!activeType.requiresServices || services.length > 0) && !conflict && !saving;
1607
+ const canSubmit = (!activeType.fields.title || title.trim()) && (!activeType.requiresClient || clientId) && (!activeType.fields.professional || professionalId) && (!activeType.requiresServices || services.length > 0) && (!needsCancellationReason || !!cancellationReasonId) && (!needsCancellationNotes || !!cancellationNotes.trim()) && !conflict && !saving;
1550
1608
  const showTabs = config.bookingTypes.length > 1;
1551
1609
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Modal, { open, onOpenChange: (v) => {
1552
1610
  if (!v) onClose();
@@ -1721,6 +1779,20 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1721
1779
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 flex-1 rounded-lg bg-muted animate-pulse" })
1722
1780
  ] }, i)) }),
1723
1781
  !loading && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1782
+ activeType.fields.title && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 py-1", children: [
1783
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Calendar, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1784
+ /* @__PURE__ */ jsxRuntime.jsx(
1785
+ "input",
1786
+ {
1787
+ type: "text",
1788
+ value: title,
1789
+ onChange: (e) => setTitle(e.target.value),
1790
+ placeholder: t("agenda.appointment.addTitle"),
1791
+ autoFocus: mode === "create",
1792
+ className: "w-full flex-1 rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-2.5 py-1.5 text-sm font-medium focus:outline-none focus:ring-2 focus:ring-ring"
1793
+ }
1794
+ )
1795
+ ] }),
1724
1796
  activeType.fields.client && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 py-1", children: [
1725
1797
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1726
1798
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: clientId && !quickCreate ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 rounded-md bg-muted/30 px-2.5 py-1.5 text-sm", children: [
@@ -1933,6 +2005,37 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
1933
2005
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CircleDot, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1934
2006
  /* @__PURE__ */ jsxRuntime.jsx(StatusSelect, { value: status, onChange: setStatus, statuses: config.statuses, bookingDate: date })
1935
2007
  ] }),
2008
+ needsCancellationReason && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 py-1", children: [
2009
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Ban, { className: "h-4 w-4 mt-2 text-muted-foreground shrink-0" }),
2010
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-2", children: [
2011
+ /* @__PURE__ */ jsxRuntime.jsxs(
2012
+ "select",
2013
+ {
2014
+ value: cancellationReasonId,
2015
+ onChange: (event) => setCancellationReasonId(event.target.value),
2016
+ disabled: cancellationReasonsLoading,
2017
+ className: "w-full rounded-input border border-input bg-card px-2.5 py-1.5 text-sm shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring",
2018
+ children: [
2019
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: cancellationReasonsLoading ? t("agenda.cancellation.loadingReasons") : t("agenda.cancellation.selectReason") }),
2020
+ cancellationReasons.map((reason) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: reason.id, children: [
2021
+ reason.label,
2022
+ reason.requiresNotes ? " *" : ""
2023
+ ] }, reason.id))
2024
+ ]
2025
+ }
2026
+ ),
2027
+ /* @__PURE__ */ jsxRuntime.jsx(
2028
+ "textarea",
2029
+ {
2030
+ value: cancellationNotes,
2031
+ onChange: (event) => setCancellationNotes(event.target.value),
2032
+ placeholder: needsCancellationNotes ? t("agenda.cancellation.notesRequired") : t("agenda.cancellation.notesOptional"),
2033
+ rows: 2,
2034
+ className: "w-full rounded-input border border-input bg-card px-2.5 py-1.5 text-sm resize-none shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] focus:outline-none focus:ring-2 focus:ring-ring"
2035
+ }
2036
+ )
2037
+ ] })
2038
+ ] }),
1936
2039
  !showNotes ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 py-1", children: [
1937
2040
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1938
2041
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2060,19 +2163,19 @@ function FloatingPanel({
2060
2163
  children,
2061
2164
  zIndex = 100
2062
2165
  }) {
2063
- const ref = React11.useRef(null);
2064
- const [phase, setPhase] = React11.useState("visible");
2065
- const phaseRef = React11.useRef("visible");
2166
+ const ref = React12.useRef(null);
2167
+ const [phase, setPhase] = React12.useState("visible");
2168
+ const phaseRef = React12.useRef("visible");
2066
2169
  phaseRef.current = phase;
2067
- const onCloseRef = React11.useRef(onClose);
2170
+ const onCloseRef = React12.useRef(onClose);
2068
2171
  onCloseRef.current = onClose;
2069
- const expandCallbackRef = React11.useRef(null);
2070
- const dismiss = React11.useCallback(() => {
2172
+ const expandCallbackRef = React12.useRef(null);
2173
+ const dismiss = React12.useCallback(() => {
2071
2174
  if (phaseRef.current !== "visible") return;
2072
2175
  setPhase("dismissing");
2073
2176
  setTimeout(() => onCloseRef.current(), 150);
2074
2177
  }, []);
2075
- const expandThen = React11.useCallback((callback) => {
2178
+ const expandThen = React12.useCallback((callback) => {
2076
2179
  if (phaseRef.current !== "visible") return;
2077
2180
  expandCallbackRef.current = callback;
2078
2181
  setPhase("expanding");
@@ -2081,7 +2184,7 @@ function FloatingPanel({
2081
2184
  onCloseRef.current();
2082
2185
  }, 220);
2083
2186
  }, []);
2084
- React11.useEffect(() => {
2187
+ React12.useEffect(() => {
2085
2188
  function handleClick(e) {
2086
2189
  if (phaseRef.current !== "visible") return;
2087
2190
  if (ref.current && !ref.current.contains(e.target)) {
@@ -2132,10 +2235,10 @@ function fmtCurrency2(amount, locale, code) {
2132
2235
  return new Intl.NumberFormat(locale, { style: "currency", currency: code }).format(amount);
2133
2236
  }
2134
2237
  function PopoverStatusSelect({ value, statuses, bookingStartsAt, onChange }) {
2135
- const [open, setOpen] = React11.useState(false);
2136
- const ref = React11.useRef(null);
2238
+ const [open, setOpen] = React12.useState(false);
2239
+ const ref = React12.useRef(null);
2137
2240
  const current = statuses.find((s) => s.value === value);
2138
- React11.useEffect(() => {
2241
+ React12.useEffect(() => {
2139
2242
  if (!open) return;
2140
2243
  const h = (e) => {
2141
2244
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -2188,8 +2291,8 @@ function AppointmentPopover({ booking, position, onClose, onEdit }) {
2188
2291
  const deleteBooking = useAgendaStore((s) => s.deleteBooking);
2189
2292
  const updateStatus = useAgendaStore((s) => s.updateBookingStatus);
2190
2293
  const openModal = useAgendaStore((s) => s.openAppointmentModal);
2191
- const [confirmingDelete, setConfirmingDelete] = React11.useState(false);
2192
- const [deleting, setDeleting] = React11.useState(false);
2294
+ const [confirmingDelete, setConfirmingDelete] = React12.useState(false);
2295
+ const [deleting, setDeleting] = React12.useState(false);
2193
2296
  const statusConfig = config.statuses.find((s) => s.value === booking.status);
2194
2297
  const color = statusConfig?.color ?? "#6b7280";
2195
2298
  const timeRange = `${fmtTime(booking.startsAt)}${booking.endsAt ? " \u2013 " + fmtTime(booking.endsAt) : ""}`;
@@ -2247,7 +2350,7 @@ function AppointmentPopover({ booking, position, onClose, onEdit }) {
2247
2350
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
2248
2351
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1.5 h-3 w-3 rounded shrink-0", style: { backgroundColor: color } }),
2249
2352
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
2250
- /* @__PURE__ */ jsxRuntime.jsx(saas.PersonLink, { personId: booking.clientId, name: booking.clientName ?? "Unknown", className: "text-base" }),
2353
+ config.capabilities.client ? /* @__PURE__ */ jsxRuntime.jsx(saas.PersonLink, { personId: booking.clientId, name: booking.clientName ?? "Unknown", className: "text-base" }) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-base font-medium truncate", children: booking.clientName || t("agenda.appointment.untitled") }),
2251
2354
  /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground mt-0.5", children: [
2252
2355
  fmtDate(booking.startsAt),
2253
2356
  " \xB7 ",
@@ -2267,7 +2370,7 @@ function AppointmentPopover({ booking, position, onClose, onEdit }) {
2267
2370
  ] })
2268
2371
  ] }, svc.id)) })
2269
2372
  ] }),
2270
- booking.professionalName && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
2373
+ config.capabilities.professional && booking.professionalName && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
2271
2374
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
2272
2375
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: booking.professionalName })
2273
2376
  ] }),
@@ -2318,7 +2421,7 @@ function AppointmentPopover({ booking, position, onClose, onEdit }) {
2318
2421
  )
2319
2422
  ] });
2320
2423
  })(),
2321
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-2 border-t fayz-glass-divider", children: /* @__PURE__ */ jsxRuntime.jsx(
2424
+ config.capabilities.status && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pt-2 border-t fayz-glass-divider", children: /* @__PURE__ */ jsxRuntime.jsx(
2322
2425
  PopoverStatusSelect,
2323
2426
  {
2324
2427
  value: booking.status,
@@ -2347,9 +2450,9 @@ function Tooltip2({ anchor, label }) {
2347
2450
  );
2348
2451
  }
2349
2452
  function StatusDot({ color, label, isActive, disabled, onClick }) {
2350
- const [hover, setHover] = React11.useState(false);
2351
- const btnRef = React11.useRef(null);
2352
- const [rect, setRect] = React11.useState(null);
2453
+ const [hover, setHover] = React12.useState(false);
2454
+ const btnRef = React12.useRef(null);
2455
+ const [rect, setRect] = React12.useState(null);
2353
2456
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2354
2457
  /* @__PURE__ */ jsxRuntime.jsx(
2355
2458
  "button",
@@ -2376,8 +2479,8 @@ function EventContextMenu({ booking, position, onClose, onEdit }) {
2376
2479
  const config = useAgendaConfig();
2377
2480
  const updateStatus = useAgendaStore((s) => s.updateBookingStatus);
2378
2481
  const deleteBooking = useAgendaStore((s) => s.deleteBooking);
2379
- const [confirming, setConfirming] = React11.useState(false);
2380
- const [deleting, setDeleting] = React11.useState(false);
2482
+ const [confirming, setConfirming] = React12.useState(false);
2483
+ const [deleting, setDeleting] = React12.useState(false);
2381
2484
  async function handleDelete() {
2382
2485
  setDeleting(true);
2383
2486
  try {
@@ -2435,7 +2538,7 @@ function EventContextMenu({ booking, position, onClose, onEdit }) {
2435
2538
  }
2436
2539
  )
2437
2540
  ] }),
2438
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t px-4 py-3", children: [
2541
+ config.capabilities.status && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t px-4 py-3", children: [
2439
2542
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-medium text-muted-foreground mb-2", children: t("agenda.contextMenu.changeStatus") }),
2440
2543
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1.5", children: config.statuses.map((s) => {
2441
2544
  const available = isStatusAvailable(s, booking.startsAt);
@@ -2459,6 +2562,66 @@ function EventContextMenu({ booking, position, onClose, onEdit }) {
2459
2562
  ] })
2460
2563
  ] }) });
2461
2564
  }
2565
+ function startOfDay(d) {
2566
+ const x = new Date(d);
2567
+ x.setHours(0, 0, 0, 0);
2568
+ return x;
2569
+ }
2570
+ function timeLabel(iso) {
2571
+ const d = new Date(iso);
2572
+ return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
2573
+ }
2574
+ function AgendaMobileList({ bookings, statusColors, currencyCode, onSelect }) {
2575
+ const t = core.useTranslation();
2576
+ const locale = core.getCurrentLocale() === "pt-BR" ? "pt-BR" : "en";
2577
+ const groups = React12.useMemo(() => {
2578
+ const todayStart = startOfDay(/* @__PURE__ */ new Date()).getTime();
2579
+ const sorted = bookings.filter((b) => !!b.startsAt).slice().sort((a, b) => a.startsAt < b.startsAt ? -1 : a.startsAt > b.startsAt ? 1 : 0);
2580
+ const map = /* @__PURE__ */ new Map();
2581
+ for (const b of sorted) {
2582
+ const day = startOfDay(new Date(b.startsAt));
2583
+ if (day.getTime() < todayStart) continue;
2584
+ const key = day.toISOString().slice(0, 10);
2585
+ const list = map.get(key) ?? [];
2586
+ list.push(b);
2587
+ map.set(key, list);
2588
+ }
2589
+ return Array.from(map.entries()).map(([key, items]) => ({ key, date: /* @__PURE__ */ new Date(key + "T00:00:00"), items }));
2590
+ }, [bookings]);
2591
+ function dayLabel(date) {
2592
+ const today = startOfDay(/* @__PURE__ */ new Date());
2593
+ const diff = Math.round((startOfDay(date).getTime() - today.getTime()) / 864e5);
2594
+ if (diff === 0) return t("agenda.list.today");
2595
+ if (diff === 1) return t("agenda.list.tomorrow");
2596
+ return date.toLocaleDateString(locale, { weekday: "long", day: "numeric", month: "long" });
2597
+ }
2598
+ if (groups.length === 0) {
2599
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 flex flex-col items-center justify-center px-6 py-16 text-center text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm", children: t("agenda.list.empty") }) });
2600
+ }
2601
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto px-3 py-3 space-y-5", children: groups.map((g) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
2602
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "px-1 pb-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: dayLabel(g.date) }),
2603
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: g.items.map((b) => {
2604
+ const color = statusColors[b.status] ?? "#6b7280";
2605
+ const hasAmount = typeof b.orderTotal === "number" && b.orderTotal > 0;
2606
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2607
+ "button",
2608
+ {
2609
+ onClick: () => onSelect(b),
2610
+ className: "flex w-full items-center gap-3 rounded-lg border bg-card px-3 py-2.5 text-left shadow-sm transition-colors active:bg-muted/50",
2611
+ children: [
2612
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "h-2.5 w-2.5 shrink-0 rounded-full", style: { backgroundColor: color } }),
2613
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
2614
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-sm font-medium text-foreground", children: b.clientName ?? "\u2014" }),
2615
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: timeLabel(b.startsAt) })
2616
+ ] }),
2617
+ hasAmount && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-sm font-semibold tabular-nums text-foreground", children: core.formatCurrency(b.orderTotal, currencyCode) })
2618
+ ]
2619
+ },
2620
+ b.id
2621
+ );
2622
+ }) })
2623
+ ] }, g.key)) });
2624
+ }
2462
2625
  function bookingToEvent(booking, statusColors) {
2463
2626
  const color = statusColors[booking.status] ?? "#6b7280";
2464
2627
  const serviceNames = booking.services?.map((s) => s.name).join(", ") ?? "";
@@ -2581,18 +2744,44 @@ function CalendarSkeleton({ profCount = 2 }) {
2581
2744
  ] }, h)) })
2582
2745
  ] });
2583
2746
  }
2584
- var VIEW_OPTIONS = [
2747
+ var RESOURCE_VIEW_OPTIONS = [
2585
2748
  { key: "resourceTimeGridDay", labelKey: "agenda.calendar.viewDay" },
2586
2749
  { key: "resourceTimeGridWeek", labelKey: "agenda.calendar.viewWeek" },
2587
2750
  { key: "dayGridMonth", labelKey: "agenda.calendar.viewMonth" }
2588
2751
  ];
2752
+ var PLAIN_VIEW_OPTIONS = [
2753
+ { key: "timeGridDay", labelKey: "agenda.calendar.viewDay" },
2754
+ { key: "timeGridWeek", labelKey: "agenda.calendar.viewWeek" },
2755
+ { key: "dayGridMonth", labelKey: "agenda.calendar.viewMonth" },
2756
+ { key: "listMonth", labelKey: "agenda.calendar.viewList" }
2757
+ ];
2758
+ var MOBILE_LIST_VIEW = "__mobileList__";
2759
+ function viewForNoResources(view) {
2760
+ return view.startsWith("resource") ? "dayGridMonth" : view;
2761
+ }
2762
+ function viewForResources(view) {
2763
+ if (view === "timeGridWeek") return "resourceTimeGridWeek";
2764
+ if (view === "timeGridDay") return "resourceTimeGridDay";
2765
+ return view;
2766
+ }
2767
+ function useIsMobile() {
2768
+ const [isMobile, setIsMobile] = React12.useState(() => typeof window !== "undefined" && window.matchMedia("(max-width: 767px)").matches);
2769
+ React12.useEffect(() => {
2770
+ if (typeof window === "undefined") return;
2771
+ const mq = window.matchMedia("(max-width: 767px)");
2772
+ const onChange = () => setIsMobile(mq.matches);
2773
+ mq.addEventListener("change", onChange);
2774
+ return () => mq.removeEventListener("change", onChange);
2775
+ }, []);
2776
+ return isMobile;
2777
+ }
2589
2778
  function CalendarView() {
2590
2779
  injectCalendarStyles();
2591
2780
  const config = useAgendaConfig();
2592
2781
  const t = core.useTranslation();
2593
2782
  const currentLocale = core.getCurrentLocale();
2594
2783
  const fcLocale = currentLocale === "pt-BR" ? ptBrLocale__default.default : void 0;
2595
- const calendarRef = React11.useRef(null);
2784
+ const calendarRef = React12.useRef(null);
2596
2785
  const prefs = saas.usePluginPrefs("agenda", { calendarView: config.defaultCalendarView ?? "resourceTimeGridWeek" });
2597
2786
  const agendaSettings = useAgendaSettings();
2598
2787
  const effectiveBusinessHours = {
@@ -2618,28 +2807,40 @@ function CalendarView() {
2618
2807
  const _openModal = useAgendaStore((s) => s.openAppointmentModal);
2619
2808
  const closeModal = useAgendaStore((s) => s.closeAppointmentModal);
2620
2809
  const modalState = useAgendaStore((s) => s.appointmentModal);
2621
- const [selectedBooking, setSelectedBooking] = React11.useState(null);
2622
- const [popoverAnchor, setPopoverAnchor] = React11.useState(null);
2623
- const [popoverVisible, _setPopoverVisible] = React11.useState(false);
2624
- const popoverVisibleRef = React11.useRef(false);
2625
- const setPopoverVisible = React11.useCallback((v) => {
2810
+ const isMobile = useIsMobile();
2811
+ const resourceCapable = professionals.length > 0;
2812
+ const viewOptions = resourceCapable ? RESOURCE_VIEW_OPTIONS : PLAIN_VIEW_OPTIONS;
2813
+ const [mobileView, setMobileView] = React12.useState(MOBILE_LIST_VIEW);
2814
+ const mobileViewOptions = React12.useMemo(
2815
+ () => [
2816
+ ...viewOptions.filter((o) => o.key !== "listMonth"),
2817
+ { key: MOBILE_LIST_VIEW, labelKey: "agenda.calendar.viewList" }
2818
+ ],
2819
+ [viewOptions]
2820
+ );
2821
+ const mobileGridActive = isMobile && mobileView !== MOBILE_LIST_VIEW;
2822
+ const [selectedBooking, setSelectedBooking] = React12.useState(null);
2823
+ const [popoverAnchor, setPopoverAnchor] = React12.useState(null);
2824
+ const [popoverVisible, _setPopoverVisible] = React12.useState(false);
2825
+ const popoverVisibleRef = React12.useRef(false);
2826
+ const setPopoverVisible = React12.useCallback((v) => {
2626
2827
  popoverVisibleRef.current = v;
2627
2828
  _setPopoverVisible(v);
2628
2829
  }, []);
2629
- const [contextMenu, setContextMenu] = React11.useState(null);
2630
- const [contextMenuVisible, _setContextMenuVisible] = React11.useState(false);
2631
- const contextMenuVisibleRef = React11.useRef(false);
2632
- const setContextMenuVisible = React11.useCallback((v) => {
2830
+ const [contextMenu, setContextMenu] = React12.useState(null);
2831
+ const [contextMenuVisible, _setContextMenuVisible] = React12.useState(false);
2832
+ const contextMenuVisibleRef = React12.useRef(false);
2833
+ const setContextMenuVisible = React12.useCallback((v) => {
2633
2834
  contextMenuVisibleRef.current = v;
2634
2835
  _setContextMenuVisible(v);
2635
2836
  }, []);
2636
- const [scheduleConfirm, setScheduleConfirm] = React11.useState(null);
2637
- const [calendarTitle, setCalendarTitle] = React11.useState("");
2638
- const [selectedDate, setSelectedDate] = React11.useState(/* @__PURE__ */ new Date());
2639
- const [profSectionOpen, setProfSectionOpen] = React11.useState(true);
2640
- const [statusSectionOpen, setStatusSectionOpen] = React11.useState(false);
2641
- const [visibleRange, setVisibleRange] = React11.useState(null);
2642
- const openModal = React11.useCallback((...args) => {
2837
+ const [scheduleConfirm, setScheduleConfirm] = React12.useState(null);
2838
+ const [calendarTitle, setCalendarTitle] = React12.useState("");
2839
+ const [selectedDate, setSelectedDate] = React12.useState(/* @__PURE__ */ new Date());
2840
+ const [profSectionOpen, setProfSectionOpen] = React12.useState(true);
2841
+ const [statusSectionOpen, setStatusSectionOpen] = React12.useState(false);
2842
+ const [visibleRange, setVisibleRange] = React12.useState(null);
2843
+ const openModal = React12.useCallback((...args) => {
2643
2844
  setPopoverVisible(false);
2644
2845
  setSelectedBooking(null);
2645
2846
  setPopoverAnchor(null);
@@ -2647,17 +2848,35 @@ function CalendarView() {
2647
2848
  setContextMenu(null);
2648
2849
  _openModal(...args);
2649
2850
  }, [_openModal]);
2650
- const statusColors = React11.useMemo(() => {
2851
+ const statusColors = React12.useMemo(() => {
2651
2852
  const map = {};
2652
2853
  for (const s of config.statuses) map[s.value] = s.color;
2653
2854
  return map;
2654
2855
  }, [config.statuses]);
2655
- React11.useEffect(() => {
2856
+ React12.useEffect(() => {
2656
2857
  fetchProfessionals();
2657
2858
  fetchSchedules();
2658
2859
  if (config.modules.locationSelection) fetchLocations();
2659
2860
  }, []);
2660
- const resources = React11.useMemo(() => {
2861
+ React12.useEffect(() => {
2862
+ const api = calendarRef.current?.getApi();
2863
+ if (!api) return;
2864
+ if (!resourceCapable && currentView.startsWith("resource")) {
2865
+ api.changeView("dayGridMonth");
2866
+ } else if (resourceCapable && (currentView === "timeGridWeek" || currentView === "timeGridDay")) {
2867
+ api.changeView(viewForResources(currentView));
2868
+ }
2869
+ }, [resourceCapable, currentView]);
2870
+ React12.useEffect(() => {
2871
+ if (!isMobile) return;
2872
+ const start = /* @__PURE__ */ new Date();
2873
+ start.setHours(0, 0, 0, 0);
2874
+ start.setDate(start.getDate() - 1);
2875
+ const end = new Date(start);
2876
+ end.setDate(end.getDate() + 91);
2877
+ fetchBookings({ start: start.toISOString(), end: end.toISOString() });
2878
+ }, [isMobile, fetchBookings]);
2879
+ const resources = React12.useMemo(() => {
2661
2880
  const filtered = selectedProfIds.length > 0 ? professionals.filter((p) => selectedProfIds.includes(p.id)) : professionals;
2662
2881
  return filtered.map((p) => ({
2663
2882
  id: p.id,
@@ -2665,29 +2884,31 @@ function CalendarView() {
2665
2884
  extendedProps: { avatarUrl: p.avatarUrl, locationName: p.locationName }
2666
2885
  }));
2667
2886
  }, [professionals, selectedProfIds]);
2668
- const bookingEvents = React11.useMemo(() => bookings.map((b) => bookingToEvent(b, statusColors)), [bookings, statusColors]);
2669
- const allLocations = React11.useMemo(() => {
2887
+ const bookingEvents = React12.useMemo(() => bookings.map((b) => bookingToEvent(b, statusColors)), [bookings, statusColors]);
2888
+ const allLocations = React12.useMemo(() => {
2670
2889
  const map = /* @__PURE__ */ new Map();
2671
2890
  for (const loc of config.locations) map.set(loc.id, loc.name);
2672
2891
  for (const loc of storeLocations) map.set(loc.id, loc.name);
2673
2892
  return map;
2674
2893
  }, [config.locations, storeLocations]);
2675
- const locationMap = React11.useMemo(() => {
2894
+ const locationMap = React12.useMemo(() => {
2676
2895
  if (!config.modules.locationSelection || allLocations.size === 0) return void 0;
2677
2896
  return allLocations;
2678
2897
  }, [config.modules.locationSelection, allLocations]);
2679
- const availabilityEvents = React11.useMemo(
2898
+ const availabilityEvents = React12.useMemo(
2680
2899
  () => generateAvailabilityEvents(schedules, professionals, visibleRange, effectiveBusinessHours, locationMap),
2681
2900
  [schedules, professionals, visibleRange, effectiveBusinessHours, locationMap]
2682
2901
  );
2683
- const events = React11.useMemo(() => [...bookingEvents, ...availabilityEvents], [bookingEvents, availabilityEvents]);
2684
- const handleDatesSet = React11.useCallback((arg) => {
2902
+ const events = React12.useMemo(() => [...bookingEvents, ...availabilityEvents], [bookingEvents, availabilityEvents]);
2903
+ const storedView = prefs.get("calendarView");
2904
+ const effectiveInitialView = resourceCapable ? viewForResources(storedView) : viewForNoResources(storedView);
2905
+ const handleDatesSet = React12.useCallback((arg) => {
2685
2906
  setCalendarTitle(arg.view.title);
2686
2907
  setView(arg.view.type);
2687
2908
  setVisibleRange({ start: arg.startStr, end: arg.endStr });
2688
2909
  fetchBookings({ start: arg.startStr, end: arg.endStr });
2689
2910
  }, [fetchBookings, setView]);
2690
- const handleEventDidMount = React11.useCallback((arg) => {
2911
+ const handleEventDidMount = React12.useCallback((arg) => {
2691
2912
  const booking = arg.event.extendedProps?.booking;
2692
2913
  if (booking) arg.el.setAttribute("data-booking-id", booking.id);
2693
2914
  arg.el.addEventListener("dblclick", (e) => {
@@ -2714,9 +2935,9 @@ function CalendarView() {
2714
2935
  setPopoverAnchor(null);
2715
2936
  });
2716
2937
  }, []);
2717
- const skipClickRef = React11.useRef(false);
2718
- const hoverTimerRef = React11.useRef(null);
2719
- const handleEventMouseEnter = React11.useCallback((arg) => {
2938
+ const skipClickRef = React12.useRef(false);
2939
+ const hoverTimerRef = React12.useRef(null);
2940
+ const handleEventMouseEnter = React12.useCallback((arg) => {
2720
2941
  if (arg.event.display === "background") return;
2721
2942
  if (arg.event.extendedProps?.isLocationLabel) return;
2722
2943
  const booking = arg.event.extendedProps.booking;
@@ -2735,13 +2956,13 @@ function CalendarView() {
2735
2956
  setPopoverVisible(true);
2736
2957
  }, 3e3);
2737
2958
  }, []);
2738
- const handleEventMouseLeave = React11.useCallback(() => {
2959
+ const handleEventMouseLeave = React12.useCallback(() => {
2739
2960
  if (hoverTimerRef.current) {
2740
2961
  clearTimeout(hoverTimerRef.current);
2741
2962
  hoverTimerRef.current = null;
2742
2963
  }
2743
2964
  }, []);
2744
- const handleEventClick = React11.useCallback((arg) => {
2965
+ const handleEventClick = React12.useCallback((arg) => {
2745
2966
  if (skipClickRef.current) {
2746
2967
  skipClickRef.current = false;
2747
2968
  return;
@@ -2764,7 +2985,7 @@ function CalendarView() {
2764
2985
  setPopoverAnchor({ x, y });
2765
2986
  setPopoverVisible(true);
2766
2987
  }, []);
2767
- const findScheduleForSlot = React11.useCallback((professionalId, dateTimeStr) => {
2988
+ const findScheduleForSlot = React12.useCallback((professionalId, dateTimeStr) => {
2768
2989
  if (!professionalId) return null;
2769
2990
  const dt = new Date(dateTimeStr);
2770
2991
  const dateStr = dt.toISOString().slice(0, 10);
@@ -2781,7 +3002,7 @@ function CalendarView() {
2781
3002
  }
2782
3003
  return null;
2783
3004
  }, [schedules]);
2784
- const handleSelect = React11.useCallback((arg) => {
3005
+ const handleSelect = React12.useCallback((arg) => {
2785
3006
  calendarRef.current?.getApi().unselect();
2786
3007
  if (popoverVisibleRef.current || contextMenuVisibleRef.current) {
2787
3008
  setPopoverVisible(false);
@@ -2797,13 +3018,13 @@ function CalendarView() {
2797
3018
  const locId = schedule?.locationId ?? schedule?.metadata?.locationId;
2798
3019
  openModal("create", { prefill: { startsAt: arg.startStr, endsAt: arg.endStr, professionalId, locationId: locId || void 0 } });
2799
3020
  };
2800
- if (!schedule) {
3021
+ if (config.modules.workingHours && !schedule) {
2801
3022
  setScheduleConfirm({ onConfirm: proceed });
2802
3023
  return;
2803
3024
  }
2804
3025
  proceed();
2805
- }, [openModal, findScheduleForSlot]);
2806
- const handleEventDrop = React11.useCallback(async (arg) => {
3026
+ }, [openModal, findScheduleForSlot, config.modules.workingHours]);
3027
+ const handleEventDrop = React12.useCallback(async (arg) => {
2807
3028
  const booking = arg.event.extendedProps.booking;
2808
3029
  try {
2809
3030
  await rescheduleBooking(booking.id, arg.event.startStr, arg.event.endStr, arg.newResource?.id ?? void 0);
@@ -2811,13 +3032,19 @@ function CalendarView() {
2811
3032
  arg.revert();
2812
3033
  }
2813
3034
  }, [rescheduleBooking]);
2814
- const handleViewChange = React11.useCallback((v) => {
3035
+ const handleViewChange = React12.useCallback((v) => {
2815
3036
  calendarRef.current?.getApi().changeView(v);
2816
3037
  prefs.set("calendarView", v);
2817
3038
  }, [prefs]);
2818
- const handlePrev = React11.useCallback(() => calendarRef.current?.getApi().prev(), []);
2819
- const handleNext = React11.useCallback(() => calendarRef.current?.getApi().next(), []);
2820
- const handleToday = React11.useCallback(() => {
3039
+ const handleMobileViewChange = React12.useCallback((v) => {
3040
+ setMobileView(v);
3041
+ if (v === MOBILE_LIST_VIEW) return;
3042
+ setView(v);
3043
+ calendarRef.current?.getApi()?.changeView(v);
3044
+ }, [setView]);
3045
+ const handlePrev = React12.useCallback(() => calendarRef.current?.getApi().prev(), []);
3046
+ const handleNext = React12.useCallback(() => calendarRef.current?.getApi().next(), []);
3047
+ const handleToday = React12.useCallback(() => {
2821
3048
  calendarRef.current?.getApi().today();
2822
3049
  setSelectedDate(/* @__PURE__ */ new Date());
2823
3050
  }, []);
@@ -2833,9 +3060,9 @@ function CalendarView() {
2833
3060
  const next = selectedStatuses.includes(value) ? selectedStatuses.filter((s) => s !== value) : [...selectedStatuses, value];
2834
3061
  setFilters({ statuses: next });
2835
3062
  }
2836
- const wrapperRef = React11.useRef(null);
2837
- const [dayHeaderCompact, setDayHeaderCompact] = React11.useState(false);
2838
- React11.useEffect(() => {
3063
+ const wrapperRef = React12.useRef(null);
3064
+ const [dayHeaderCompact, setDayHeaderCompact] = React12.useState(false);
3065
+ React12.useEffect(() => {
2839
3066
  const container = wrapperRef.current;
2840
3067
  if (!container) return;
2841
3068
  let frame = 0;
@@ -2878,7 +3105,7 @@ function CalendarView() {
2878
3105
  ro.disconnect();
2879
3106
  };
2880
3107
  }, [resources, currentView, visibleRange]);
2881
- const applyResourceStriping = React11.useCallback(() => {
3108
+ const applyResourceStriping = React12.useCallback(() => {
2882
3109
  const container = wrapperRef.current;
2883
3110
  if (!container) return;
2884
3111
  const resourceIds = resources.map((r) => r.id);
@@ -2889,7 +3116,7 @@ function CalendarView() {
2889
3116
  el.classList.toggle("fc-resource-alt", idx % 2 === 1);
2890
3117
  });
2891
3118
  }, [resources]);
2892
- React11.useEffect(() => {
3119
+ React12.useEffect(() => {
2893
3120
  applyResourceStriping();
2894
3121
  const t1 = setTimeout(applyResourceStriping, 100);
2895
3122
  const t2 = setTimeout(applyResourceStriping, 300);
@@ -2898,15 +3125,28 @@ function CalendarView() {
2898
3125
  clearTimeout(t2);
2899
3126
  };
2900
3127
  }, [applyResourceStriping, currentView, visibleRange, bookings]);
2901
- React11.useEffect(() => {
3128
+ React12.useEffect(() => {
2902
3129
  function handleOpenBooking(e) {
2903
- const bookingId = e.detail?.bookingId;
2904
- if (bookingId) openModal("edit", { bookingId });
3130
+ const detail = e.detail ?? {};
3131
+ const externalSource = detail.source ? { type: String(detail.source), id: detail.waitlistId, waitlistId: detail.waitlistId } : void 0;
3132
+ if (detail.bookingId) {
3133
+ openModal("edit", { bookingId: detail.bookingId, externalSource });
3134
+ return;
3135
+ }
3136
+ openModal("create", {
3137
+ prefill: detail.prefill ?? {
3138
+ clientId: detail.clientId,
3139
+ professionalId: detail.professionalId,
3140
+ locationId: detail.locationId,
3141
+ startsAt: detail.startsAt
3142
+ },
3143
+ externalSource
3144
+ });
2905
3145
  }
2906
3146
  window.addEventListener("agenda:open-booking", handleOpenBooking);
2907
3147
  return () => window.removeEventListener("agenda:open-booking", handleOpenBooking);
2908
3148
  }, [openModal]);
2909
- const dayHeaderContent = React11.useCallback((arg) => {
3149
+ const dayHeaderContent = React12.useCallback((arg) => {
2910
3150
  const date = arg.date;
2911
3151
  const dateLocale = currentLocale === "pt-BR" ? "pt-BR" : "en";
2912
3152
  const wrapStyle = { display: "flex", flexDirection: "column", alignItems: "center", lineHeight: 1.15 };
@@ -2924,7 +3164,7 @@ function CalendarView() {
2924
3164
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: dateStyle, children: `${date.getMonth() + 1}/${date.getDate()}` })
2925
3165
  ] });
2926
3166
  }, [dayHeaderCompact, currentLocale]);
2927
- const resourceLabelContent = React11.useCallback((arg) => {
3167
+ const resourceLabelContent = React12.useCallback((arg) => {
2928
3168
  const { id, title, extendedProps } = arg.resource;
2929
3169
  const initials = title.split(" ").map((n) => n[0]).join("").slice(0, 2);
2930
3170
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-2 py-1.5", children: [
@@ -2944,7 +3184,7 @@ function CalendarView() {
2944
3184
  ] })
2945
3185
  ] });
2946
3186
  }, []);
2947
- const eventContent = React11.useCallback((arg) => {
3187
+ const eventContent = React12.useCallback((arg) => {
2948
3188
  if (arg.event.extendedProps?.isLocationLabel) {
2949
3189
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-1.5 py-0.5 overflow-hidden", children: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] font-medium truncate", children: arg.event.title }) });
2950
3190
  }
@@ -2975,110 +3215,135 @@ function CalendarView() {
2975
3215
  }
2976
3216
  ),
2977
3217
  /* @__PURE__ */ jsxRuntime.jsx(MiniCalendar, { selectedDate, onDateSelect: handleMiniDateSelect }),
2978
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px bg-border my-4" }),
2979
- /* @__PURE__ */ jsxRuntime.jsxs(
2980
- "button",
2981
- {
2982
- onClick: () => setProfSectionOpen((p) => !p),
2983
- className: "flex items-center justify-between w-full text-xs font-medium text-muted-foreground hover:text-foreground py-1 transition-colors",
2984
- children: [
2985
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Agendas" }),
2986
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: `h-3.5 w-3.5 transition-transform ${profSectionOpen ? "" : "-rotate-90"}` })
2987
- ]
2988
- }
2989
- ),
2990
- profSectionOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1.5 space-y-0.5", children: professionals.map((prof) => {
2991
- const active = selectedProfIds.length === 0 || selectedProfIds.includes(prof.id);
2992
- return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2.5 rounded px-1 py-1 cursor-pointer hover:bg-muted/50 transition-colors", children: [
2993
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border-2 transition-colors ${active ? "border-primary bg-primary" : "border-muted-foreground/30"}`, children: active && /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "h-2.5 w-2.5 text-primary-foreground", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 6l3 3 5-5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
2994
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-xs truncate ${active ? "text-foreground" : "text-muted-foreground"}`, children: prof.name }),
2995
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", className: "sr-only", checked: active, onChange: () => toggleProfessional(prof.id) })
2996
- ] }, prof.id);
2997
- }) }),
2998
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px bg-border my-4" }),
2999
- /* @__PURE__ */ jsxRuntime.jsxs(
3000
- "button",
3001
- {
3002
- onClick: () => setStatusSectionOpen((p) => !p),
3003
- className: "flex items-center justify-between w-full text-xs font-medium text-muted-foreground hover:text-foreground py-1 transition-colors",
3004
- children: [
3005
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Status" }),
3006
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: `h-3.5 w-3.5 transition-transform ${statusSectionOpen ? "" : "-rotate-90"}` })
3007
- ]
3008
- }
3009
- ),
3010
- statusSectionOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1.5 space-y-0.5", children: config.statuses.map((st) => {
3011
- const active = selectedStatuses.length === 0 || selectedStatuses.includes(st.value);
3012
- return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2.5 rounded px-1 py-1 cursor-pointer hover:bg-muted/50 transition-colors", children: [
3013
- /* @__PURE__ */ jsxRuntime.jsx(
3014
- "span",
3015
- {
3016
- className: "flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border-2 transition-colors",
3017
- style: { borderColor: active ? st.color : st.color + "40", backgroundColor: active ? st.color : "transparent" },
3018
- children: active && /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "h-2.5 w-2.5 text-white", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 6l3 3 5-5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
3019
- }
3020
- ),
3021
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-xs ${active ? "text-foreground" : "text-muted-foreground"}`, children: st.label }),
3022
- /* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", className: "sr-only", checked: active, onChange: () => toggleStatus(st.value) })
3023
- ] }, st.value);
3024
- }) })
3218
+ config.capabilities.professional && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3219
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px bg-border my-4" }),
3220
+ /* @__PURE__ */ jsxRuntime.jsxs(
3221
+ "button",
3222
+ {
3223
+ onClick: () => setProfSectionOpen((p) => !p),
3224
+ className: "flex items-center justify-between w-full text-xs font-medium text-muted-foreground hover:text-foreground py-1 transition-colors",
3225
+ children: [
3226
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Agendas" }),
3227
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: `h-3.5 w-3.5 transition-transform ${profSectionOpen ? "" : "-rotate-90"}` })
3228
+ ]
3229
+ }
3230
+ ),
3231
+ profSectionOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1.5 space-y-0.5", children: professionals.map((prof) => {
3232
+ const active = selectedProfIds.length === 0 || selectedProfIds.includes(prof.id);
3233
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2.5 rounded px-1 py-1 cursor-pointer hover:bg-muted/50 transition-colors", children: [
3234
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border-2 transition-colors ${active ? "border-primary bg-primary" : "border-muted-foreground/30"}`, children: active && /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "h-2.5 w-2.5 text-primary-foreground", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 6l3 3 5-5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
3235
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-xs truncate ${active ? "text-foreground" : "text-muted-foreground"}`, children: prof.name }),
3236
+ /* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", className: "sr-only", checked: active, onChange: () => toggleProfessional(prof.id) })
3237
+ ] }, prof.id);
3238
+ }) })
3239
+ ] }),
3240
+ config.capabilities.status && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3241
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px bg-border my-4" }),
3242
+ /* @__PURE__ */ jsxRuntime.jsxs(
3243
+ "button",
3244
+ {
3245
+ onClick: () => setStatusSectionOpen((p) => !p),
3246
+ className: "flex items-center justify-between w-full text-xs font-medium text-muted-foreground hover:text-foreground py-1 transition-colors",
3247
+ children: [
3248
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Status" }),
3249
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: `h-3.5 w-3.5 transition-transform ${statusSectionOpen ? "" : "-rotate-90"}` })
3250
+ ]
3251
+ }
3252
+ ),
3253
+ statusSectionOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1.5 space-y-0.5", children: config.statuses.map((st) => {
3254
+ const active = selectedStatuses.length === 0 || selectedStatuses.includes(st.value);
3255
+ return /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex items-center gap-2.5 rounded px-1 py-1 cursor-pointer hover:bg-muted/50 transition-colors", children: [
3256
+ /* @__PURE__ */ jsxRuntime.jsx(
3257
+ "span",
3258
+ {
3259
+ className: "flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border-2 transition-colors",
3260
+ style: { borderColor: active ? st.color : st.color + "40", backgroundColor: active ? st.color : "transparent" },
3261
+ children: active && /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "h-2.5 w-2.5 text-white", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 6l3 3 5-5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
3262
+ }
3263
+ ),
3264
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-xs ${active ? "text-foreground" : "text-muted-foreground"}`, children: st.label }),
3265
+ /* @__PURE__ */ jsxRuntime.jsx("input", { type: "checkbox", className: "sr-only", checked: active, onChange: () => toggleStatus(st.value) })
3266
+ ] }, st.value);
3267
+ }) })
3268
+ ] })
3025
3269
  ] }),
3026
3270
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 flex flex-col min-w-0 overflow-hidden", children: [
3027
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 h-12 shrink-0 border-b", children: [
3028
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
3029
- /* @__PURE__ */ jsxRuntime.jsx(
3030
- "button",
3031
- {
3032
- onClick: () => openModal("create"),
3033
- className: "md:hidden flex h-8 w-8 items-center justify-center rounded-full bg-primary text-primary-foreground",
3034
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4" })
3035
- }
3036
- ),
3037
- /* @__PURE__ */ jsxRuntime.jsx(
3038
- "button",
3039
- {
3040
- onClick: handleToday,
3041
- className: "rounded-md border px-3 py-1 text-sm font-medium hover:bg-muted bg-card shadow-button active:shadow-button-inset transition-colors",
3042
- children: t("agenda.toolbar.today")
3043
- }
3044
- ),
3045
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5", children: [
3046
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: handlePrev, className: "flex h-7 w-7 items-center justify-center rounded-full hover:bg-muted transition-colors", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-4 w-4" }) }),
3047
- /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: handleNext, className: "flex h-7 w-7 items-center justify-center rounded-full hover:bg-muted transition-colors", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" }) })
3271
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "shrink-0 border-b", children: [
3272
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between pl-4 pr-14 md:px-4 h-12", children: [
3273
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
3274
+ /* @__PURE__ */ jsxRuntime.jsx(
3275
+ "button",
3276
+ {
3277
+ onClick: () => openModal("create"),
3278
+ className: "md:hidden flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground",
3279
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4" })
3280
+ }
3281
+ ),
3282
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `${mobileGridActive ? "flex" : "hidden"} md:flex items-center gap-3`, children: [
3283
+ /* @__PURE__ */ jsxRuntime.jsx(
3284
+ "button",
3285
+ {
3286
+ onClick: handleToday,
3287
+ className: "rounded-md border px-3 py-1 text-sm font-medium hover:bg-muted bg-card shadow-button active:shadow-button-inset transition-colors",
3288
+ children: t("agenda.toolbar.today")
3289
+ }
3290
+ ),
3291
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5", children: [
3292
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: handlePrev, className: "flex h-7 w-7 items-center justify-center rounded-full hover:bg-muted transition-colors", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-4 w-4" }) }),
3293
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: handleNext, className: "flex h-7 w-7 items-center justify-center rounded-full hover:bg-muted transition-colors", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" }) })
3294
+ ] })
3295
+ ] }),
3296
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-base sm:text-lg font-normal truncate", children: isMobile && !mobileGridActive ? t("agenda.calendar.viewAgenda") : calendarTitle })
3048
3297
  ] }),
3049
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-base sm:text-lg font-normal truncate", children: calendarTitle })
3298
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
3299
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden md:flex items-center gap-1", children: viewOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
3300
+ "button",
3301
+ {
3302
+ onClick: () => handleViewChange(opt.key),
3303
+ className: `rounded-md px-3 py-1 text-sm transition-colors ${currentView === opt.key ? "bg-primary/10 text-primary font-medium" : "text-muted-foreground hover:bg-muted/50"}`,
3304
+ children: t(opt.labelKey)
3305
+ },
3306
+ opt.key
3307
+ )) }),
3308
+ /* @__PURE__ */ jsxRuntime.jsx(
3309
+ ui.Button,
3310
+ {
3311
+ variant: "outline",
3312
+ size: "icon",
3313
+ onClick: () => window.location.hash = "/settings/agenda",
3314
+ title: "Agenda Settings",
3315
+ className: "ml-1",
3316
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings, { className: "h-4 w-4" })
3317
+ }
3318
+ )
3319
+ ] })
3050
3320
  ] }),
3051
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
3052
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden sm:flex items-center gap-1", children: VIEW_OPTIONS.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
3053
- "button",
3054
- {
3055
- onClick: () => handleViewChange(opt.key),
3056
- className: `rounded-md px-3 py-1 text-sm transition-colors ${currentView === opt.key ? "bg-primary/10 text-primary font-medium" : "text-muted-foreground hover:bg-muted/50"}`,
3057
- children: t(opt.labelKey)
3058
- },
3059
- opt.key
3060
- )) }),
3061
- /* @__PURE__ */ jsxRuntime.jsx(
3062
- ui.Button,
3063
- {
3064
- variant: "outline",
3065
- size: "icon",
3066
- onClick: () => window.location.hash = "/settings/agenda",
3067
- title: "Agenda Settings",
3068
- className: "ml-1",
3069
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Settings, { className: "h-4 w-4" })
3070
- }
3071
- )
3072
- ] })
3321
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex md:hidden items-center gap-1 overflow-x-auto px-3 pb-2", children: mobileViewOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
3322
+ "button",
3323
+ {
3324
+ onClick: () => handleMobileViewChange(opt.key),
3325
+ className: `shrink-0 rounded-md px-3 py-1 text-sm transition-colors ${mobileView === opt.key ? "bg-primary/10 text-primary font-medium" : "text-muted-foreground hover:bg-muted/50"}`,
3326
+ children: t(opt.labelKey)
3327
+ },
3328
+ opt.key
3329
+ )) })
3073
3330
  ] }),
3074
- professionalsLoading && professionals.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(CalendarSkeleton, { profCount: 2 }) : /* @__PURE__ */ jsxRuntime.jsx("div", { ref: wrapperRef, className: "flex-1 overflow-hidden agenda-calendar", children: /* @__PURE__ */ jsxRuntime.jsx(
3331
+ isMobile && !mobileGridActive ? /* @__PURE__ */ jsxRuntime.jsx(
3332
+ AgendaMobileList,
3333
+ {
3334
+ bookings,
3335
+ statusColors,
3336
+ currencyCode: config.currency.code,
3337
+ onSelect: (b) => openModal("edit", { bookingId: b.id })
3338
+ }
3339
+ ) : professionalsLoading && professionals.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(CalendarSkeleton, { profCount: 2 }) : /* @__PURE__ */ jsxRuntime.jsx("div", { ref: wrapperRef, className: "flex-1 overflow-hidden agenda-calendar", children: /* @__PURE__ */ jsxRuntime.jsx(
3075
3340
  FullCalendar__default.default,
3076
3341
  {
3077
3342
  ref: calendarRef,
3078
3343
  plugins: [dayGridPlugin__default.default, timeGridPlugin__default.default, resourceTimeGridPlugin__default.default, interactionPlugin__default.default, listPlugin__default.default],
3079
- initialView: prefs.get("calendarView") === "timeGridWeek" ? "resourceTimeGridWeek" : prefs.get("calendarView"),
3344
+ initialView: mobileGridActive ? mobileView : effectiveInitialView,
3080
3345
  schedulerLicenseKey: "CC-Attribution-NonCommercial-NoDerivatives",
3081
- resources,
3346
+ resources: resourceCapable ? resources : void 0,
3082
3347
  events,
3083
3348
  headerToolbar: false,
3084
3349
  locale: fcLocale,
@@ -3168,8 +3433,14 @@ function CalendarView() {
3168
3433
  bookingId: modalState.bookingId,
3169
3434
  prefill: modalState.prefill,
3170
3435
  initialTab: modalState.initialTab,
3436
+ externalSource: modalState.externalSource,
3171
3437
  onClose: closeModal,
3172
- onCreated: (newId) => {
3438
+ onCreated: (newId, source) => {
3439
+ if (config.onBookingCreated) {
3440
+ void Promise.resolve(config.onBookingCreated({ bookingId: newId, source })).catch((error) => {
3441
+ console.error("Failed to run agenda booking-created bridge", error);
3442
+ });
3443
+ }
3173
3444
  setTimeout(() => {
3174
3445
  const booking = bookings.find((b) => b.id === newId);
3175
3446
  if (!booking) return;
@@ -3195,7 +3466,7 @@ function CalendarView() {
3195
3466
  function AgendaPage({ config, provider: provider2, store }) {
3196
3467
  const runtime = core.usePluginRuntimeOptional();
3197
3468
  const tenantId = runtime?.context.tenant?.id;
3198
- React11__default.default.useEffect(() => {
3469
+ React12__default.default.useEffect(() => {
3199
3470
  chunkSYDBDI3Q_cjs.setAgendaTenantId(tenantId);
3200
3471
  return () => chunkSYDBDI3Q_cjs.setAgendaTenantId(void 0);
3201
3472
  }, [tenantId]);
@@ -3310,7 +3581,8 @@ async function hydrateCanonicalBookings(core, bookings) {
3310
3581
  locationId: booking.location_id,
3311
3582
  metadata: booking.metadata ?? {},
3312
3583
  clientId: booking.party_id,
3313
- clientName: client?.name ?? null,
3584
+ // Simple events (no party) fall back to the title stored in metadata.
3585
+ clientName: client?.name ?? booking.metadata?.title ?? null,
3314
3586
  clientPhone: client?.phone ?? null,
3315
3587
  clientEmail: client?.email ?? null,
3316
3588
  clientAvatarUrl: client?.avatar_url ?? null,
@@ -3388,6 +3660,7 @@ function createSupabaseAgendaProvider() {
3388
3660
  const { data: person } = await core.from("persons").select("name").eq("id", input.clientId).single();
3389
3661
  contactName = person?.name;
3390
3662
  }
3663
+ const eventTitle = input.title?.trim() || void 0;
3391
3664
  const { data: order, error: orderErr } = await core.from("orders").insert({
3392
3665
  tenant_id: tenantId,
3393
3666
  kind: input.kind ?? "appointment",
@@ -3401,7 +3674,8 @@ function createSupabaseAgendaProvider() {
3401
3674
  metadata: {
3402
3675
  source: "agenda",
3403
3676
  ...serviceNames ? { serviceNames, itemsSummary: serviceNames } : {},
3404
- ...contactName ? { contactName } : {}
3677
+ ...contactName ? { contactName } : {},
3678
+ ...eventTitle ? { title: eventTitle } : {}
3405
3679
  }
3406
3680
  }).select("id").single();
3407
3681
  if (orderErr) throw orderErr;
@@ -3419,7 +3693,8 @@ function createSupabaseAgendaProvider() {
3419
3693
  metadata: {
3420
3694
  source: "agenda",
3421
3695
  ...serviceNames ? { serviceNames, itemsSummary: serviceNames } : {},
3422
- ...contactName ? { contactName } : {}
3696
+ ...contactName ? { contactName } : {},
3697
+ ...eventTitle ? { title: eventTitle } : {}
3423
3698
  }
3424
3699
  }).select("id").single();
3425
3700
  if (bookingErr) throw bookingErr;
@@ -3444,11 +3719,14 @@ function createSupabaseAgendaProvider() {
3444
3719
  },
3445
3720
  async updateBooking(id, data) {
3446
3721
  const { core } = getClients();
3447
- const { data: existing, error: existingErr } = await core.from("bookings").select("id, order_id, starts_at, assignee_id").eq("id", id).single();
3722
+ const { data: existing, error: existingErr } = await core.from("bookings").select("id, order_id, starts_at, assignee_id, metadata").eq("id", id).single();
3448
3723
  if (existingErr) throw existingErr;
3449
3724
  const updates = {};
3450
3725
  if (data.startsAt) updates.starts_at = data.startsAt;
3451
3726
  if (data.endsAt) updates.ends_at = data.endsAt;
3727
+ if (data.title !== void 0) {
3728
+ updates.metadata = { ...existing?.metadata ?? {}, title: data.title };
3729
+ }
3452
3730
  if (data.status) {
3453
3731
  updates.status = data.status;
3454
3732
  }
@@ -3854,10 +4132,11 @@ function createSampleSchedules() {
3854
4132
  }
3855
4133
  return schedules;
3856
4134
  }
3857
- function createMockAgendaProvider() {
3858
- const bookings = createSampleBookings();
3859
- const schedules = createSampleSchedules();
3860
- const professionals = [...SAMPLE_PROFESSIONALS];
4135
+ function createMockAgendaProvider(options) {
4136
+ const seed = options?.seed;
4137
+ const bookings = seed?.bookings ? [...seed.bookings] : createSampleBookings();
4138
+ const schedules = seed?.schedules ? [...seed.schedules] : createSampleSchedules();
4139
+ const professionals = seed?.professionals ? [...seed.professionals] : [...SAMPLE_PROFESSIONALS];
3861
4140
  return {
3862
4141
  async getLocations() {
3863
4142
  return [];
@@ -3892,7 +4171,7 @@ function createMockAgendaProvider() {
3892
4171
  locationId: input.locationId ?? null,
3893
4172
  metadata: {},
3894
4173
  clientId: input.clientId,
3895
- clientName: "New Client",
4174
+ clientName: input.title?.trim() || "New Client",
3896
4175
  clientPhone: null,
3897
4176
  clientEmail: null,
3898
4177
  clientAvatarUrl: null,
@@ -3938,6 +4217,7 @@ function createMockAgendaProvider() {
3938
4217
  ...existing,
3939
4218
  ...data.startsAt && { startsAt: data.startsAt },
3940
4219
  ...data.endsAt && { endsAt: data.endsAt },
4220
+ ...data.title !== void 0 && { clientName: data.title },
3941
4221
  ...data.status && { status: data.status },
3942
4222
  ...data.notes !== void 0 && { notes: data.notes ?? null },
3943
4223
  ...data.professionalId && { professionalId: data.professionalId },
@@ -4246,7 +4526,7 @@ function createAgendaStore(provider2, financialBridge) {
4246
4526
  set({ selectedDate: date });
4247
4527
  },
4248
4528
  openAppointmentModal(mode, options) {
4249
- set({ appointmentModal: { open: true, mode, bookingId: options?.bookingId, prefill: options?.prefill, initialTab: options?.initialTab } });
4529
+ set({ appointmentModal: { open: true, mode, bookingId: options?.bookingId, prefill: options?.prefill, initialTab: options?.initialTab, externalSource: options?.externalSource } });
4250
4530
  },
4251
4531
  closeAppointmentModal() {
4252
4532
  set((state) => ({ appointmentModal: { ...state.appointmentModal, open: false } }));
@@ -4437,8 +4717,12 @@ var en = {
4437
4717
  "agenda.calendar.today": "Today",
4438
4718
  "agenda.calendar.viewAgenda": "Agenda",
4439
4719
  "agenda.calendar.viewDay": "Day",
4720
+ "agenda.calendar.viewList": "List",
4440
4721
  "agenda.calendar.viewMonth": "Month",
4441
4722
  "agenda.calendar.viewWeek": "Week",
4723
+ "agenda.list.today": "Today",
4724
+ "agenda.list.tomorrow": "Tomorrow",
4725
+ "agenda.list.empty": "No upcoming items",
4442
4726
  "agenda.confirmations.confirmed": "Confirmed:",
4443
4727
  "agenda.confirmations.empty": "No pending confirmations",
4444
4728
  "agenda.confirmations.emptySubtitle": "All upcoming appointments have been confirmed",
@@ -4449,6 +4733,10 @@ var en = {
4449
4733
  "agenda.confirmations.call": "Call",
4450
4734
  "agenda.confirmations.markConfirmed": "Mark Confirmed",
4451
4735
  "agenda.confirmations.markCancelled": "Mark Cancelled",
4736
+ "agenda.cancellation.loadingReasons": "Loading reasons...",
4737
+ "agenda.cancellation.notesOptional": "Cancellation notes (optional)",
4738
+ "agenda.cancellation.notesRequired": "Cancellation notes required",
4739
+ "agenda.cancellation.selectReason": "Select cancellation reason",
4452
4740
  "agenda.contextMenu.changeStatus": "Change status",
4453
4741
  "agenda.contextMenu.delete": "Delete",
4454
4742
  "agenda.contextMenu.edit": "Edit",
@@ -4549,7 +4837,11 @@ var en = {
4549
4837
  "agenda.appointment.addNotes": "Add notes",
4550
4838
  "agenda.appointment.duration": "Duration (minutes)",
4551
4839
  "agenda.appointment.price": "Price",
4840
+ "agenda.appointment.title": "Title",
4841
+ "agenda.appointment.addTitle": "Add a title",
4842
+ "agenda.appointment.untitled": "(no title)",
4552
4843
  "agenda.bookingType.appointment": "Appointment",
4844
+ "agenda.bookingType.event": "Event",
4553
4845
  "agenda.bookingType.task": "Task",
4554
4846
  "agenda.bookingType.out_of_office": "Out of Office",
4555
4847
  "agenda.bookingType.block": "Block",
@@ -4645,8 +4937,12 @@ var ptBR = {
4645
4937
  "agenda.calendar.today": "Hoje",
4646
4938
  "agenda.calendar.viewAgenda": "Agenda",
4647
4939
  "agenda.calendar.viewDay": "Dia",
4940
+ "agenda.calendar.viewList": "Lista",
4648
4941
  "agenda.calendar.viewMonth": "M\xEAs",
4649
4942
  "agenda.calendar.viewWeek": "Semana",
4943
+ "agenda.list.today": "Hoje",
4944
+ "agenda.list.tomorrow": "Amanh\xE3",
4945
+ "agenda.list.empty": "Nenhum item pr\xF3ximo",
4650
4946
  "agenda.confirmations.confirmed": "Confirmado:",
4651
4947
  "agenda.confirmations.empty": "Nenhuma confirma\xE7\xE3o pendente",
4652
4948
  "agenda.confirmations.emptySubtitle": "Todos os agendamentos futuros foram confirmados",
@@ -4657,6 +4953,10 @@ var ptBR = {
4657
4953
  "agenda.confirmations.call": "Ligar",
4658
4954
  "agenda.confirmations.markConfirmed": "Marcar Confirmado",
4659
4955
  "agenda.confirmations.markCancelled": "Marcar Cancelado",
4956
+ "agenda.cancellation.loadingReasons": "Carregando motivos...",
4957
+ "agenda.cancellation.notesOptional": "Observa\xE7\xF5es do cancelamento (opcional)",
4958
+ "agenda.cancellation.notesRequired": "Observa\xE7\xF5es do cancelamento obrigat\xF3rias",
4959
+ "agenda.cancellation.selectReason": "Selecionar motivo do cancelamento",
4660
4960
  "agenda.contextMenu.changeStatus": "Alterar status",
4661
4961
  "agenda.contextMenu.delete": "Excluir",
4662
4962
  "agenda.contextMenu.edit": "Editar",
@@ -4757,7 +5057,11 @@ var ptBR = {
4757
5057
  "agenda.appointment.addNotes": "Adicionar observa\xE7\xF5es",
4758
5058
  "agenda.appointment.duration": "Dura\xE7\xE3o (minutos)",
4759
5059
  "agenda.appointment.price": "Pre\xE7o",
5060
+ "agenda.appointment.title": "T\xEDtulo",
5061
+ "agenda.appointment.addTitle": "Adicionar um t\xEDtulo",
5062
+ "agenda.appointment.untitled": "(sem t\xEDtulo)",
4760
5063
  "agenda.bookingType.appointment": "Agendamento",
5064
+ "agenda.bookingType.event": "Evento",
4761
5065
  "agenda.bookingType.task": "Tarefa",
4762
5066
  "agenda.bookingType.out_of_office": "Aus\xEAncia",
4763
5067
  "agenda.bookingType.block": "Bloqueio",
@@ -5039,11 +5343,11 @@ function createGoogleCalendarProvider() {
5039
5343
  }
5040
5344
  var provider = createGoogleCalendarProvider();
5041
5345
  function GoogleCalendarExtraPanel() {
5042
- const [connected, setConnected] = React11.useState(false);
5043
- const [calendarId, setCalendarId] = React11.useState("primary");
5044
- const [syncing, setSyncing] = React11.useState(false);
5045
- const [log, setLog] = React11.useState([]);
5046
- const [loaded, setLoaded] = React11.useState(false);
5346
+ const [connected, setConnected] = React12.useState(false);
5347
+ const [calendarId, setCalendarId] = React12.useState("primary");
5348
+ const [syncing, setSyncing] = React12.useState(false);
5349
+ const [log, setLog] = React12.useState([]);
5350
+ const [loaded, setLoaded] = React12.useState(false);
5047
5351
  async function load() {
5048
5352
  const integ = await provider.getIntegration();
5049
5353
  setConnected(!!integ?.connected);
@@ -5053,7 +5357,7 @@ function GoogleCalendarExtraPanel() {
5053
5357
  }
5054
5358
  setLoaded(true);
5055
5359
  }
5056
- React11.useEffect(() => {
5360
+ React12.useEffect(() => {
5057
5361
  void load();
5058
5362
  }, []);
5059
5363
  if (!loaded || !connected) return null;
@@ -5193,6 +5497,7 @@ function createAgendaPlugin(options) {
5193
5497
  () => createMockAgendaProvider()
5194
5498
  );
5195
5499
  const store = createAgendaStore(provider2, options?.financialBridge);
5500
+ const registries = [...agendaRegistries, ...options?.settingsRegistries ?? []];
5196
5501
  saas.setScheduleBlockConfig({
5197
5502
  defaults: config.scheduleBlockDefaults,
5198
5503
  showServices: !!config.serviceLookup,
@@ -5208,7 +5513,7 @@ function createAgendaPlugin(options) {
5208
5513
  return locations;
5209
5514
  } : void 0
5210
5515
  });
5211
- const PageComponent = () => React11__default.default.createElement(AgendaPage, { config, provider: provider2, store });
5516
+ const PageComponent = () => React12__default.default.createElement(AgendaPage, { config, provider: provider2, store });
5212
5517
  return {
5213
5518
  id: "agenda",
5214
5519
  name: config.labels.pageTitle,
@@ -5308,18 +5613,18 @@ function createAgendaPlugin(options) {
5308
5613
  permission: { feature: "appointments", action: "read" }
5309
5614
  }
5310
5615
  ],
5311
- registries: agendaRegistries,
5616
+ registries,
5312
5617
  settings: [
5313
5618
  {
5314
5619
  id: "agenda",
5315
5620
  label: "Agenda",
5316
5621
  icon: "Calendar",
5317
- // Standard layout: General | Integrations (Google Calendar addon) | Properties (holidays).
5622
+ // Standard layout: General | Integrations (Google Calendar addon) | Properties.
5318
5623
  component: (() => {
5319
- const AgendaSettingsTab = () => React11__default.default.createElement(saas.PluginSettingsPanel, {
5624
+ const AgendaSettingsTab = () => React12__default.default.createElement(saas.PluginSettingsPanel, {
5320
5625
  title: "Agenda",
5321
- generalSettings: React11__default.default.createElement(AgendaGeneralSettings),
5322
- registries: agendaRegistries,
5626
+ generalSettings: React12__default.default.createElement(AgendaGeneralSettings),
5627
+ registries,
5323
5628
  hostPluginId: "agenda",
5324
5629
  routeBase: "/settings/agenda"
5325
5630
  });
@@ -5340,6 +5645,7 @@ exports.createAgendaPlugin = createAgendaPlugin;
5340
5645
  exports.createFinancialBridge = createFinancialBridge;
5341
5646
  exports.createGoogleCalendarPlugin = createGoogleCalendarPlugin;
5342
5647
  exports.createGoogleCalendarProvider = createGoogleCalendarProvider;
5648
+ exports.createMockAgendaProvider = createMockAgendaProvider;
5343
5649
  exports.eventToBookingPatch = eventToBookingPatch;
5344
5650
  exports.googleCalendarConnector = googleCalendarConnector;
5345
5651
  //# sourceMappingURL=index.cjs.map