@fayz-ai/plugin-agenda 0.8.1 → 0.8.4

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 (118) hide show
  1. package/dist/{chunk-DRZF6CV2.js → chunk-Q2PAWBPR.js} +62 -3
  2. package/dist/chunk-Q2PAWBPR.js.map +1 -0
  3. package/dist/{chunk-LFUELUIT.cjs → chunk-RX2HMWXS.cjs} +62 -2
  4. package/dist/chunk-RX2HMWXS.cjs.map +1 -0
  5. package/dist/components/AgendaGeneralSettings.d.ts.map +1 -1
  6. package/dist/components/AppointmentModal.d.ts.map +1 -1
  7. package/dist/components/CalendarToolbar.d.ts.map +1 -1
  8. package/dist/data/supabase.d.ts +5 -1
  9. package/dist/data/supabase.d.ts.map +1 -1
  10. package/dist/index.cjs +1078 -768
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +921 -611
  14. package/dist/index.js.map +1 -1
  15. package/dist/integrations/google-calendar/components/CalendarColorDot.d.ts +6 -0
  16. package/dist/integrations/google-calendar/components/CalendarColorDot.d.ts.map +1 -0
  17. package/dist/integrations/google-calendar/components/ChannelMappingList.d.ts +9 -0
  18. package/dist/integrations/google-calendar/components/ChannelMappingList.d.ts.map +1 -0
  19. package/dist/integrations/google-calendar/components/ImportEventsWizard.d.ts +12 -0
  20. package/dist/integrations/google-calendar/components/ImportEventsWizard.d.ts.map +1 -0
  21. package/dist/integrations/google-calendar/components/useAgendaLookups.d.ts +26 -0
  22. package/dist/integrations/google-calendar/components/useAgendaLookups.d.ts.map +1 -0
  23. package/dist/integrations/google-calendar/connectorDef.d.ts.map +1 -1
  24. package/dist/integrations/google-calendar/data/supabase.d.ts +33 -1
  25. package/dist/integrations/google-calendar/data/supabase.d.ts.map +1 -1
  26. package/dist/integrations/google-calendar/data/tables.d.ts +6 -0
  27. package/dist/integrations/google-calendar/data/tables.d.ts.map +1 -0
  28. package/dist/integrations/google-calendar/index.d.ts +1 -1
  29. package/dist/integrations/google-calendar/index.d.ts.map +1 -1
  30. package/dist/integrations/google-calendar/mapping.d.ts +56 -4
  31. package/dist/integrations/google-calendar/mapping.d.ts.map +1 -1
  32. package/dist/integrations/google-calendar/types.d.ts +101 -0
  33. package/dist/integrations/google-calendar/types.d.ts.map +1 -1
  34. package/dist/locales/en.d.ts.map +1 -1
  35. package/dist/locales/pt-BR.d.ts.map +1 -1
  36. package/dist/migrations/index.d.ts +12 -1
  37. package/dist/migrations/index.d.ts.map +1 -1
  38. package/dist/public/BookingWidget.d.ts.map +1 -1
  39. package/dist/public/context.d.ts +3 -1
  40. package/dist/public/context.d.ts.map +1 -1
  41. package/dist/public/createPublicBookingPlugin.d.ts +7 -1
  42. package/dist/public/createPublicBookingPlugin.d.ts.map +1 -1
  43. package/dist/public/data.d.ts +6 -0
  44. package/dist/public/data.d.ts.map +1 -1
  45. package/dist/public/data.supabase.d.ts.map +1 -1
  46. package/dist/public/format.d.ts +22 -1
  47. package/dist/public/format.d.ts.map +1 -1
  48. package/dist/public/hooks.d.ts.map +1 -1
  49. package/dist/public/index.cjs +31 -63
  50. package/dist/public/index.cjs.map +1 -1
  51. package/dist/public/index.d.ts +1 -1
  52. package/dist/public/index.d.ts.map +1 -1
  53. package/dist/public/index.js +27 -61
  54. package/dist/public/index.js.map +1 -1
  55. package/dist/public/steps/ContactStep.d.ts +4 -2
  56. package/dist/public/steps/ContactStep.d.ts.map +1 -1
  57. package/dist/public/types.d.ts +16 -0
  58. package/dist/public/types.d.ts.map +1 -1
  59. package/dist/views/CalendarView.d.ts.map +1 -1
  60. package/package.json +5 -5
  61. package/src/components/AgendaGeneralSettings.tsx +38 -16
  62. package/src/components/AppointmentModal.tsx +50 -108
  63. package/src/components/CalendarToolbar.tsx +10 -7
  64. package/src/data/supabase.ts +67 -13
  65. package/src/index.ts +56 -8
  66. package/src/integrations/google-calendar/components/CalendarColorDot.tsx +23 -0
  67. package/src/integrations/google-calendar/components/ChannelMappingList.tsx +329 -0
  68. package/src/integrations/google-calendar/components/ImportEventsWizard.tsx +215 -0
  69. package/src/integrations/google-calendar/components/useAgendaLookups.ts +100 -0
  70. package/src/integrations/google-calendar/connectorDef.tsx +123 -40
  71. package/src/integrations/google-calendar/data/supabase.ts +109 -13
  72. package/src/integrations/google-calendar/data/tables.ts +12 -0
  73. package/src/integrations/google-calendar/index.ts +10 -1
  74. package/src/integrations/google-calendar/integration.test.ts +126 -1
  75. package/src/integrations/google-calendar/mapping.ts +149 -9
  76. package/src/integrations/google-calendar/migrations/001_google_calendar.sql +9 -6
  77. package/src/integrations/google-calendar/migrations/002_smart_sync.sql +517 -0
  78. package/src/integrations/google-calendar/migrations/003_plg_rename.sql +273 -0
  79. package/src/integrations/google-calendar/types.ts +111 -0
  80. package/src/locales/en.ts +1 -0
  81. package/src/locales/pt-BR.ts +1 -0
  82. package/src/migrations/001_public_booking.sql +7 -2
  83. package/src/migrations/003_public_client_kind.sql +175 -0
  84. package/src/migrations/004_public_booking_assignee.sql +176 -0
  85. package/src/migrations/005_agent_rpcs.sql +168 -0
  86. package/src/migrations/006_agent_rpc_localtime.sql +181 -0
  87. package/src/migrations/007_schedule_exception_precedence.sql +290 -0
  88. package/src/migrations/008_agent_rpc_exception_precedence.sql +172 -0
  89. package/src/migrations/009_public_booking_location.sql +162 -0
  90. package/src/migrations/010_public_service_curation.sql +31 -0
  91. package/src/migrations/011_default_assignee.sql +167 -0
  92. package/src/migrations/012_drop_legacy_booking_overload.sql +29 -0
  93. package/src/migrations/013_normalize_v_appointments.sql +199 -0
  94. package/src/migrations/index.ts +1791 -3
  95. package/src/public/BookingWidget.tsx +10 -1
  96. package/src/public/context.tsx +3 -0
  97. package/src/public/createPublicBookingPlugin.ts +14 -1
  98. package/src/public/data.supabase.ts +7 -4
  99. package/src/public/data.ts +6 -0
  100. package/src/public/format.ts +36 -1
  101. package/src/public/hooks.ts +7 -2
  102. package/src/public/index.ts +1 -0
  103. package/src/public/steps/ContactStep.tsx +10 -5
  104. package/src/public/types.ts +21 -0
  105. package/src/views/CalendarView.tsx +15 -1
  106. package/dist/chunk-3LJKZQK4.js +0 -12
  107. package/dist/chunk-3LJKZQK4.js.map +0 -1
  108. package/dist/chunk-DRZF6CV2.js.map +0 -1
  109. package/dist/chunk-LFUELUIT.cjs.map +0 -1
  110. package/dist/chunk-SYDBDI3Q.cjs +0 -15
  111. package/dist/chunk-SYDBDI3Q.cjs.map +0 -1
  112. package/dist/components/SearchCombobox.d.ts +0 -38
  113. package/dist/components/SearchCombobox.d.ts.map +0 -1
  114. package/dist/tenant-222EHYUC.cjs +0 -16
  115. package/dist/tenant-222EHYUC.cjs.map +0 -1
  116. package/dist/tenant-LAUWVUYB.js +0 -3
  117. package/dist/tenant-LAUWVUYB.js.map +0 -1
  118. package/src/components/SearchCombobox.tsx +0 -216
package/dist/index.cjs CHANGED
@@ -1,8 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkLFUELUIT_cjs = require('./chunk-LFUELUIT.cjs');
4
- var chunkSYDBDI3Q_cjs = require('./chunk-SYDBDI3Q.cjs');
5
- var React12 = require('react');
3
+ var chunkRX2HMWXS_cjs = require('./chunk-RX2HMWXS.cjs');
4
+ var React13 = require('react');
6
5
  var core = require('@fayz-ai/core');
7
6
  var saas = require('@fayz-ai/saas');
8
7
  var FullCalendar = require('@fullcalendar/react');
@@ -20,7 +19,7 @@ var vanilla = require('zustand/vanilla');
20
19
 
21
20
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
22
21
 
23
- var React12__default = /*#__PURE__*/_interopDefault(React12);
22
+ var React13__default = /*#__PURE__*/_interopDefault(React13);
24
23
  var FullCalendar__default = /*#__PURE__*/_interopDefault(FullCalendar);
25
24
  var dayGridPlugin__default = /*#__PURE__*/_interopDefault(dayGridPlugin);
26
25
  var timeGridPlugin__default = /*#__PURE__*/_interopDefault(timeGridPlugin);
@@ -458,10 +457,10 @@ function toDateLocale(locale) {
458
457
  }
459
458
  function MiniCalendar({ selectedDate, onDateSelect, onMonthChange }) {
460
459
  const locale = core.getCurrentLocale();
461
- const [viewMonth, setViewMonth] = React12__default.default.useState(() => new Date(selectedDate.getFullYear(), selectedDate.getMonth(), 1));
460
+ const [viewMonth, setViewMonth] = React13__default.default.useState(() => new Date(selectedDate.getFullYear(), selectedDate.getMonth(), 1));
462
461
  const monthLabel = viewMonth.toLocaleDateString(toDateLocale(locale), { month: "long", year: "numeric" });
463
462
  const dayLabels = DAY_LABELS[locale] ?? DAY_LABELS.en;
464
- const weeks = React12.useMemo(() => {
463
+ const weeks = React13.useMemo(() => {
465
464
  const year = viewMonth.getFullYear();
466
465
  const month = viewMonth.getMonth();
467
466
  const firstDay = new Date(year, month, 1);
@@ -513,216 +512,6 @@ function MiniCalendar({ selectedDate, onDateSelect, onMonthChange }) {
513
512
  }) }, wi))
514
513
  ] });
515
514
  }
516
- function SearchCombobox({
517
- value,
518
- onChange,
519
- onSelect,
520
- options,
521
- placeholder,
522
- autoFocus,
523
- onBlurEmpty,
524
- renderRight,
525
- allowCreate,
526
- onCreateNew,
527
- createLabel = "Create",
528
- className,
529
- minimal,
530
- inlineLabel,
531
- inlineIcon = "search",
532
- loading,
533
- onFocus: onFocusProp
534
- }) {
535
- const [open, setOpen] = React12.useState(false);
536
- const [activeIndex, setActiveIndex] = React12.useState(-1);
537
- const [expanded, setExpanded] = React12.useState(!inlineLabel);
538
- const [dropRect, setDropRect] = React12.useState(null);
539
- const inputRef = React12.useRef(null);
540
- const wrapperRef = React12.useRef(null);
541
- const listRef = React12.useRef(null);
542
- React12.useEffect(() => {
543
- if (value && inlineLabel) setExpanded(true);
544
- if (!value && inlineLabel && !open) setExpanded(false);
545
- }, [value, open]);
546
- function updateRect() {
547
- if (wrapperRef.current) setDropRect(wrapperRef.current.getBoundingClientRect());
548
- }
549
- const showCreate = open && allowCreate && onCreateNew && value.trim().length > 0 && !options.some((o) => o.label.toLowerCase() === value.trim().toLowerCase());
550
- const totalItems = options.length + (showCreate ? 1 : 0);
551
- React12.useEffect(() => {
552
- setActiveIndex(-1);
553
- }, [options]);
554
- React12.useEffect(() => {
555
- if ((options.length > 0 || showCreate || loading) && document.activeElement === inputRef.current) setOpen(true);
556
- }, [options, showCreate, loading]);
557
- const handleKeyDown = React12.useCallback((e) => {
558
- if (!open || totalItems === 0) {
559
- if (e.key === "ArrowDown" && totalItems > 0) {
560
- e.preventDefault();
561
- setOpen(true);
562
- setActiveIndex(0);
563
- }
564
- return;
565
- }
566
- switch (e.key) {
567
- case "ArrowDown":
568
- e.preventDefault();
569
- setActiveIndex((prev) => (prev + 1) % totalItems);
570
- break;
571
- case "ArrowUp":
572
- e.preventDefault();
573
- setActiveIndex((prev) => prev <= 0 ? totalItems - 1 : prev - 1);
574
- break;
575
- case "Enter":
576
- e.preventDefault();
577
- if (activeIndex >= 0 && activeIndex < options.length) {
578
- onSelect(options[activeIndex]);
579
- setOpen(false);
580
- setActiveIndex(-1);
581
- } else if (showCreate && activeIndex === options.length) {
582
- onCreateNew?.(value.trim());
583
- setOpen(false);
584
- setActiveIndex(-1);
585
- }
586
- break;
587
- case "Escape":
588
- e.preventDefault();
589
- setOpen(false);
590
- setActiveIndex(-1);
591
- break;
592
- }
593
- }, [open, options, totalItems, activeIndex, onSelect, showCreate, onCreateNew, value]);
594
- React12.useEffect(() => {
595
- if (activeIndex < 0 || !listRef.current) return;
596
- const items = listRef.current.children;
597
- if (items[activeIndex]) items[activeIndex].scrollIntoView({ block: "nearest" });
598
- }, [activeIndex]);
599
- function handleBlur() {
600
- setTimeout(() => {
601
- setOpen(false);
602
- setActiveIndex(-1);
603
- if (!value && onBlurEmpty) onBlurEmpty();
604
- if (!value && inlineLabel) setExpanded(false);
605
- }, 200);
606
- }
607
- const hasDropdown = open && (options.length > 0 || showCreate || loading);
608
- if (!expanded && inlineLabel) {
609
- const isPlus = inlineIcon === "plus";
610
- return /* @__PURE__ */ jsxRuntime.jsxs(
611
- "button",
612
- {
613
- type: "button",
614
- onClick: () => {
615
- setExpanded(true);
616
- setTimeout(() => inputRef.current?.focus(), 50);
617
- },
618
- className: `flex items-center gap-1.5 w-full text-left text-sm py-1.5 pl-2 transition-colors ${isPlus ? "text-primary hover:text-primary/80" : "text-muted-foreground hover:text-foreground"} ${className ?? ""}`,
619
- children: [
620
- isPlus && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5 shrink-0" }),
621
- inlineLabel
622
- ]
623
- }
624
- );
625
- }
626
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: wrapperRef, className: `relative ${className ?? ""}`, children: [
627
- !minimal && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground pointer-events-none" }),
628
- /* @__PURE__ */ jsxRuntime.jsx(
629
- "input",
630
- {
631
- ref: inputRef,
632
- type: "text",
633
- value,
634
- onChange: (e) => {
635
- onChange(e.target.value);
636
- updateRect();
637
- setOpen(true);
638
- },
639
- onFocus: () => {
640
- updateRect();
641
- onFocusProp?.();
642
- if (options.length > 0 || showCreate || loading) setOpen(true);
643
- },
644
- onBlur: handleBlur,
645
- onKeyDown: handleKeyDown,
646
- placeholder,
647
- autoFocus,
648
- className: minimal ? "w-full bg-transparent pl-2 pr-3 py-1.5 text-sm placeholder:text-muted-foreground focus:outline-none border-b border-transparent hover:border-border focus:border-foreground transition-colors" : "w-full rounded-lg border bg-background pl-8 pr-3 py-2 text-sm placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring",
649
- role: "combobox",
650
- "aria-expanded": hasDropdown,
651
- "aria-autocomplete": "list",
652
- "aria-activedescendant": activeIndex >= 0 ? `combobox-opt-${activeIndex}` : void 0
653
- }
654
- ),
655
- hasDropdown && dropRect && reactDom.createPortal(
656
- /* @__PURE__ */ jsxRuntime.jsxs(
657
- "div",
658
- {
659
- ref: listRef,
660
- role: "listbox",
661
- "data-modal-passthrough": true,
662
- style: { position: "fixed", left: dropRect.left, top: dropRect.bottom + 4, width: dropRect.width, zIndex: 9999, pointerEvents: "auto" },
663
- className: "fayz-glass-surface max-h-52 overflow-y-auto rounded-lg border bg-popover shadow-lg py-0.5 animate-in fade-in zoom-in-95 duration-100",
664
- children: [
665
- loading && options.length === 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-3 py-2 text-sm text-muted-foreground", children: [
666
- /* @__PURE__ */ jsxRuntime.jsxs("svg", { className: "h-3.5 w-3.5 animate-spin shrink-0", viewBox: "0 0 16 16", fill: "none", children: [
667
- /* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "8", cy: "8", r: "6.5", stroke: "currentColor", strokeWidth: "1.5", opacity: "0.25" }),
668
- /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14.5 8a6.5 6.5 0 00-6.5-6.5", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" })
669
- ] }),
670
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Buscando\u2026" })
671
- ] }),
672
- options.map((opt, i) => /* @__PURE__ */ jsxRuntime.jsxs(
673
- "div",
674
- {
675
- id: `combobox-opt-${i}`,
676
- role: "option",
677
- "aria-selected": i === activeIndex,
678
- onMouseDown: (e) => {
679
- e.preventDefault();
680
- onSelect(opt);
681
- setOpen(false);
682
- },
683
- onMouseEnter: () => setActiveIndex(i),
684
- className: `flex w-full items-center gap-2 px-3 py-2 text-sm cursor-pointer transition-colors ${i === activeIndex ? "bg-primary/10 text-primary" : "hover:bg-muted/50"}`,
685
- children: [
686
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
687
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate font-medium", children: opt.label }),
688
- opt.subtitle && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "truncate text-[11px] text-muted-foreground", children: opt.subtitle })
689
- ] }),
690
- renderRight && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "shrink-0", children: renderRight(opt) })
691
- ]
692
- },
693
- opt.id
694
- )),
695
- showCreate && /* @__PURE__ */ jsxRuntime.jsxs(
696
- "div",
697
- {
698
- id: `combobox-opt-${options.length}`,
699
- role: "option",
700
- "aria-selected": activeIndex === options.length,
701
- onMouseDown: (e) => {
702
- e.preventDefault();
703
- onCreateNew?.(value.trim());
704
- setOpen(false);
705
- },
706
- onMouseEnter: () => setActiveIndex(options.length),
707
- className: `flex w-full items-center gap-2 px-3 py-2 text-sm cursor-pointer transition-colors border-t ${activeIndex === options.length ? "bg-primary/10 text-primary" : "hover:bg-muted/50"}`,
708
- children: [
709
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5 shrink-0" }),
710
- /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
711
- createLabel,
712
- " \u201C",
713
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: value.trim() }),
714
- "\u201D"
715
- ] })
716
- ]
717
- }
718
- )
719
- ]
720
- }
721
- ),
722
- document.body
723
- )
724
- ] });
725
- }
726
515
  var TYPE_ICONS = {
727
516
  cash: lucideReact.Banknote,
728
517
  pix: lucideReact.QrCode,
@@ -748,26 +537,26 @@ var ICON_COLORS = {
748
537
  check: "text-muted-foreground"
749
538
  };
750
539
  function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPaymentChange, formatCurrency: formatCurrency2, inline }) {
751
- const t = core.useTranslation();
540
+ const t2 = core.useTranslation();
752
541
  const config = useAgendaConfig();
753
542
  const bridge = config.financialBridge;
754
- const [visible, setVisible] = React12.useState(false);
755
- const [loading, setLoading] = React12.useState(true);
756
- const [detail, setDetail] = React12.useState(null);
757
- const [selectedTypeId, setSelectedTypeId] = React12.useState("");
758
- const [paymentTypes, setPaymentTypes] = React12.useState([]);
759
- const [bankAccounts, setBankAccounts] = React12.useState([]);
760
- const [filteredMethods, setFilteredMethods] = React12.useState([]);
761
- const [selectedBankId, setSelectedBankId] = React12.useState("");
762
- const [paymentMethodId, setPaymentMethodId] = React12.useState("");
763
- const [cardBrand, setCardBrand] = React12.useState("");
764
- const [cardInstallments, setCardInstallments] = React12.useState(1);
765
- const [processing, setProcessing] = React12.useState(false);
766
- const [justPaid, setJustPaid] = React12.useState(false);
767
- React12.useEffect(() => {
543
+ const [visible, setVisible] = React13.useState(false);
544
+ const [loading, setLoading] = React13.useState(true);
545
+ const [detail, setDetail] = React13.useState(null);
546
+ const [selectedTypeId, setSelectedTypeId] = React13.useState("");
547
+ const [paymentTypes, setPaymentTypes] = React13.useState([]);
548
+ const [bankAccounts, setBankAccounts] = React13.useState([]);
549
+ const [filteredMethods, setFilteredMethods] = React13.useState([]);
550
+ const [selectedBankId, setSelectedBankId] = React13.useState("");
551
+ const [paymentMethodId, setPaymentMethodId] = React13.useState("");
552
+ const [cardBrand, setCardBrand] = React13.useState("");
553
+ const [cardInstallments, setCardInstallments] = React13.useState(1);
554
+ const [processing, setProcessing] = React13.useState(false);
555
+ const [justPaid, setJustPaid] = React13.useState(false);
556
+ React13.useEffect(() => {
768
557
  requestAnimationFrame(() => setVisible(true));
769
558
  }, []);
770
- React12.useEffect(() => {
559
+ React13.useEffect(() => {
771
560
  let cancelled = false;
772
561
  setLoading(true);
773
562
  Promise.all([
@@ -789,7 +578,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
789
578
  setVisible(false);
790
579
  setTimeout(onClose, 200);
791
580
  }
792
- const selectedType = paymentTypes.find((t2) => t2.id === selectedTypeId);
581
+ const selectedType = paymentTypes.find((t3) => t3.id === selectedTypeId);
793
582
  const txType = selectedType?.transactionType ?? "";
794
583
  const needsBank = ["bank_transfer", "pix", "check"].includes(txType);
795
584
  const needsCard = txType === "credit_card" || txType === "debit_card";
@@ -828,7 +617,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
828
617
  loading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-3 py-2", children: [...Array(3)].map((_, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-8 rounded-lg bg-muted animate-pulse" }, i)) }),
829
618
  !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: [
830
619
  /* @__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" }) }),
831
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-success", children: t("agenda.payment.checkoutComplete") }),
620
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-success", children: t2("agenda.payment.checkoutComplete") }),
832
621
  /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: formatCurrency2(detail.totalAmount) })
833
622
  ] }),
834
623
  !loading && detail && detail.status !== "none" && !justPaid && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3 animate-in fade-in duration-300", children: [
@@ -850,14 +639,14 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
850
639
  ] }),
851
640
  /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[10px] text-muted-foreground", children: [
852
641
  mov.paymentDate ?? mov.dueDate,
853
- detail.movements.length > 1 && mov.installmentNumber ? ` \xB7 ${t("agenda.payment.installment")} ${mov.installmentNumber}` : ""
642
+ detail.movements.length > 1 && mov.installmentNumber ? ` \xB7 ${t2("agenda.payment.installment")} ${mov.installmentNumber}` : ""
854
643
  ] })
855
644
  ] }),
856
645
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 shrink-0", children: [
857
646
  isMovPaid && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-success/10 px-2 py-0.5 text-[10px] font-medium text-success", children: [
858
647
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-2.5 w-2.5" }),
859
648
  " ",
860
- t("agenda.payment.paid")
649
+ t2("agenda.payment.paid")
861
650
  ] }),
862
651
  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: [
863
652
  "! ",
@@ -867,14 +656,14 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
867
656
  ] }),
868
657
  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: [
869
658
  "! ",
870
- t("agenda.payment.overdue")
659
+ t2("agenda.payment.overdue")
871
660
  ] }),
872
- 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") }),
661
+ 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: t2("agenda.payment.pending") }),
873
662
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-semibold", children: formatCurrency2(mov.paidAmount || mov.amount) })
874
663
  ] })
875
664
  ] }, mov.id);
876
665
  }) }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm px-1", children: [
877
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: t("agenda.payment.total") }),
666
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: t2("agenda.payment.total") }),
878
667
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold", children: formatCurrency2(detail.totalAmount) })
879
668
  ] }),
880
669
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
@@ -888,7 +677,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
888
677
  href: `#/financial/receivables/detail/${detail.invoiceId}`,
889
678
  className: "inline-flex items-center gap-1.5 text-xs font-medium text-primary hover:underline ml-auto",
890
679
  children: [
891
- t("agenda.payment.viewInvoice"),
680
+ t2("agenda.payment.viewInvoice"),
892
681
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { className: "h-3 w-3" })
893
682
  ]
894
683
  }
@@ -897,22 +686,22 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
897
686
  ] }),
898
687
  !loading && (!detail || detail.movements.length === 0) && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
899
688
  services && services.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
900
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-medium text-muted-foreground mb-2", children: t("agenda.payment.serviceSummary") }),
689
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-medium text-muted-foreground mb-2", children: t2("agenda.payment.serviceSummary") }),
901
690
  /* @__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: [
902
691
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: svc.name }),
903
692
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-muted-foreground", children: formatCurrency2(svc.price) })
904
693
  ] }, i)) })
905
694
  ] }),
906
695
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-sm font-semibold pt-2 border-t", children: [
907
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: t("agenda.payment.total") }),
696
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: t2("agenda.payment.total") }),
908
697
  /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatCurrency2(orderTotal) })
909
698
  ] }),
910
699
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
911
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-medium text-muted-foreground mb-2", children: t("agenda.payment.paymentMethod") }),
700
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-medium text-muted-foreground mb-2", children: t2("agenda.payment.paymentMethod") }),
912
701
  paymentTypes.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-lg border border-dashed border-input bg-muted/30 px-3 py-3 text-center", children: [
913
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: t("agenda.payment.noMethods") }),
702
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: t2("agenda.payment.noMethods") }),
914
703
  /* @__PURE__ */ jsxRuntime.jsxs("a", { href: "#/financial/settings", className: "mt-1.5 inline-block text-xs font-medium text-primary hover:underline", children: [
915
- t("agenda.payment.configureMethods"),
704
+ t2("agenda.payment.configureMethods"),
916
705
  " \u2192"
917
706
  ] })
918
707
  ] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid grid-cols-3 gap-1.5", children: paymentTypes.map((pt) => {
@@ -939,7 +728,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
939
728
  ] }),
940
729
  selectedTypeId && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
941
730
  filteredMethods.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
942
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-[11px] font-medium text-muted-foreground", children: needsCard ? t("agenda.payment.card") : t("agenda.payment.method") }),
731
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-[11px] font-medium text-muted-foreground", children: needsCard ? t2("agenda.payment.card") : t2("agenda.payment.method") }),
943
732
  /* @__PURE__ */ jsxRuntime.jsxs(
944
733
  "select",
945
734
  {
@@ -947,7 +736,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
947
736
  onChange: (e) => setPaymentMethodId(e.target.value),
948
737
  className: "w-full mt-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 focus:outline-none focus:ring-2 focus:ring-ring",
949
738
  children: [
950
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("agenda.payment.select") }),
739
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t2("agenda.payment.select") }),
951
740
  filteredMethods.map((m) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: m.id, children: m.name }, m.id))
952
741
  ]
953
742
  }
@@ -955,7 +744,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
955
744
  ] }),
956
745
  needsCard && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `grid gap-2.5 ${needsCardInstallments ? "grid-cols-2" : "grid-cols-1"}`, children: [
957
746
  /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
958
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-[11px] font-medium text-muted-foreground", children: t("agenda.payment.cardBrand") }),
747
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-[11px] font-medium text-muted-foreground", children: t2("agenda.payment.cardBrand") }),
959
748
  /* @__PURE__ */ jsxRuntime.jsxs(
960
749
  "select",
961
750
  {
@@ -963,14 +752,14 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
963
752
  onChange: (e) => setCardBrand(e.target.value),
964
753
  className: "w-full mt-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 focus:outline-none focus:ring-2 focus:ring-ring",
965
754
  children: [
966
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("agenda.payment.selectBrand") }),
755
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t2("agenda.payment.selectBrand") }),
967
756
  ["Visa", "Mastercard", "American Express", "Elo", "Hipercard", "Diners Club"].map((b) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: b, children: b }, b))
968
757
  ]
969
758
  }
970
759
  )
971
760
  ] }),
972
761
  needsCardInstallments && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
973
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-[11px] font-medium text-muted-foreground", children: t("agenda.payment.cardInstallments") }),
762
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-[11px] font-medium text-muted-foreground", children: t2("agenda.payment.cardInstallments") }),
974
763
  /* @__PURE__ */ jsxRuntime.jsx(
975
764
  "select",
976
765
  {
@@ -987,7 +776,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
987
776
  ] })
988
777
  ] }),
989
778
  needsBank && bankAccounts.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
990
- /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-[11px] font-medium text-muted-foreground", children: t("agenda.payment.bankAccount") }),
779
+ /* @__PURE__ */ jsxRuntime.jsx("label", { className: "text-[11px] font-medium text-muted-foreground", children: t2("agenda.payment.bankAccount") }),
991
780
  /* @__PURE__ */ jsxRuntime.jsxs(
992
781
  "select",
993
782
  {
@@ -995,7 +784,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
995
784
  onChange: (e) => setSelectedBankId(e.target.value),
996
785
  className: "w-full mt-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 focus:outline-none focus:ring-2 focus:ring-ring",
997
786
  children: [
998
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t("agenda.payment.selectAccount") }),
787
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: t2("agenda.payment.selectAccount") }),
999
788
  bankAccounts.map((a) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: a.id, children: a.name }, a.id))
1000
789
  ]
1001
790
  }
@@ -1009,7 +798,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
1009
798
  onClick: handleCheckout,
1010
799
  disabled: processing || !selectedTypeId,
1011
800
  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",
1012
- children: processing ? t("agenda.payment.checkingOut") : `${t("agenda.payment.checkout")} ${formatCurrency2(orderTotal)}`
801
+ children: processing ? t2("agenda.payment.checkingOut") : `${t2("agenda.payment.checkout")} ${formatCurrency2(orderTotal)}`
1013
802
  }
1014
803
  )
1015
804
  ] })
@@ -1030,7 +819,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
1030
819
  onClick: (e) => e.stopPropagation(),
1031
820
  children: [
1032
821
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-5 py-4 border-b sticky top-0 bg-card z-10", children: [
1033
- /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold", children: t("agenda.payment.title") }),
822
+ /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-base font-semibold", children: t2("agenda.payment.title") }),
1034
823
  /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: handleClose, className: "p-1 text-muted-foreground hover:text-foreground transition-colors", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-4 w-4" }) })
1035
824
  ] }),
1036
825
  content
@@ -1070,9 +859,9 @@ function formatCompactDate(dateStr, locale) {
1070
859
  });
1071
860
  }
1072
861
  function InlineNumber({ value, onChange, min = 0, step = 1, className, disabled, tooltip }) {
1073
- const [draft, setDraft] = React12.useState(String(value));
1074
- const [focused, setFocused] = React12.useState(false);
1075
- React12.useEffect(() => {
862
+ const [draft, setDraft] = React13.useState(String(value));
863
+ const [focused, setFocused] = React13.useState(false);
864
+ React13.useEffect(() => {
1076
865
  if (!focused) setDraft(String(value));
1077
866
  }, [value, focused]);
1078
867
  function commit() {
@@ -1116,11 +905,11 @@ function InlineNumber({ value, onChange, min = 0, step = 1, className, disabled,
1116
905
  ] }) });
1117
906
  }
1118
907
  function StatusSelect({ value, onChange, statuses, bookingDate }) {
1119
- const [open, setOpen] = React12.useState(false);
1120
- const ref = React12.useRef(null);
1121
- const dropdownRef = React12.useRef(null);
908
+ const [open, setOpen] = React13.useState(false);
909
+ const ref = React13.useRef(null);
910
+ const dropdownRef = React13.useRef(null);
1122
911
  const current = statuses.find((s) => s.value === value);
1123
- React12.useEffect(() => {
912
+ React13.useEffect(() => {
1124
913
  if (!open) return;
1125
914
  const handler = (e) => {
1126
915
  const target = e.target;
@@ -1131,8 +920,8 @@ function StatusSelect({ value, onChange, statuses, bookingDate }) {
1131
920
  document.addEventListener("mousedown", handler);
1132
921
  return () => document.removeEventListener("mousedown", handler);
1133
922
  }, [open]);
1134
- const btnRef = React12.useRef(null);
1135
- const [dropRect, setDropRect] = React12.useState(null);
923
+ const btnRef = React13.useRef(null);
924
+ const [dropRect, setDropRect] = React13.useState(null);
1136
925
  function handleOpen() {
1137
926
  if (btnRef.current) setDropRect(btnRef.current.getBoundingClientRect());
1138
927
  setOpen((p) => !p);
@@ -1191,11 +980,11 @@ function StatusSelect({ value, onChange, statuses, bookingDate }) {
1191
980
  ] });
1192
981
  }
1193
982
  function ProfessionalSelect({ value, onChange, professionals }) {
1194
- const t = core.useTranslation();
1195
- const [open, setOpen] = React12.useState(false);
1196
- const ref = React12.useRef(null);
983
+ const t2 = core.useTranslation();
984
+ const [open, setOpen] = React13.useState(false);
985
+ const ref = React13.useRef(null);
1197
986
  const current = professionals.find((p) => p.id === value);
1198
- React12.useEffect(() => {
987
+ React13.useEffect(() => {
1199
988
  if (!open) return;
1200
989
  const handler = (e) => {
1201
990
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -1211,7 +1000,7 @@ function ProfessionalSelect({ value, onChange, professionals }) {
1211
1000
  onClick: () => setOpen((p) => !p),
1212
1001
  className: "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-muted/30 transition-colors text-left",
1213
1002
  children: [
1214
- current ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: current.name }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: t("agenda.appointment.selectProfessional") }),
1003
+ current ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: current.name }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: t2("agenda.appointment.selectProfessional") }),
1215
1004
  /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "ml-auto h-3 w-3 text-muted-foreground shrink-0", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 6l4 4 4-4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
1216
1005
  ]
1217
1006
  }
@@ -1232,11 +1021,11 @@ function ProfessionalSelect({ value, onChange, professionals }) {
1232
1021
  ] });
1233
1022
  }
1234
1023
  function LocationSelect({ value, onChange, locations }) {
1235
- const t = core.useTranslation();
1236
- const [open, setOpen] = React12.useState(false);
1237
- const ref = React12.useRef(null);
1024
+ const t2 = core.useTranslation();
1025
+ const [open, setOpen] = React13.useState(false);
1026
+ const ref = React13.useRef(null);
1238
1027
  const current = locations.find((l) => l.id === value);
1239
- React12.useEffect(() => {
1028
+ React13.useEffect(() => {
1240
1029
  if (!open) return;
1241
1030
  const handler = (e) => {
1242
1031
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -1252,7 +1041,7 @@ function LocationSelect({ value, onChange, locations }) {
1252
1041
  onClick: () => setOpen((p) => !p),
1253
1042
  className: "flex w-full items-center gap-2 rounded-md px-2 py-1.5 text-sm hover:bg-muted/30 transition-colors text-left",
1254
1043
  children: [
1255
- current ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: current.name }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: t("agenda.appointment.selectLocation") }),
1044
+ current ? /* @__PURE__ */ jsxRuntime.jsx("span", { children: current.name }) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: t2("agenda.appointment.selectLocation") }),
1256
1045
  /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "ml-auto h-3 w-3 text-muted-foreground shrink-0", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 6l4 4 4-4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) })
1257
1046
  ]
1258
1047
  }
@@ -1273,12 +1062,13 @@ function LocationSelect({ value, onChange, locations }) {
1273
1062
  ] });
1274
1063
  }
1275
1064
  function AppointmentModal({ open, mode, bookingId, prefill, initialTab, externalSource, onClose, onCreated }) {
1276
- const t = core.useTranslation();
1065
+ const t2 = core.useTranslation();
1277
1066
  const locale = core.getCurrentLocale();
1278
1067
  const config = useAgendaConfig();
1279
1068
  const provider2 = useAgendaProvider();
1280
- const initialSnapshot = React12.useRef("");
1069
+ const initialSnapshot = React13.useRef("");
1281
1070
  const createBooking = useAgendaStore((s) => s.createBooking);
1071
+ const guardBookings = saas.useLimitGuard("bookings_month");
1282
1072
  const updateBooking = useAgendaStore((s) => s.updateBooking);
1283
1073
  const deleteBooking = useAgendaStore((s) => s.deleteBooking);
1284
1074
  const fetchBookings = useAgendaStore((s) => s.fetchBookings);
@@ -1286,59 +1076,52 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1286
1076
  const professionals = useAgendaStore((s) => s.professionals);
1287
1077
  const storeLocations = useAgendaStore((s) => s.locations);
1288
1078
  const defaultBookingType = config.bookingTypes[0]?.id ?? "appointment";
1289
- const [loading, setLoading] = React12.useState(mode === "edit");
1290
- const [bookingType, setBookingType] = React12.useState(prefill?.kind ?? defaultBookingType);
1291
- const [title, setTitle] = React12.useState("");
1292
- const [clientId, setClientId] = React12.useState(prefill?.clientId ?? "");
1293
- const [clientSearch, setClientSearch] = React12.useState("");
1294
- const [clientPhone, setClientPhone] = React12.useState("");
1295
- const [professionalId, setProfessionalId] = React12.useState(prefill?.professionalId ?? "");
1296
- const [locationId, setLocationId] = React12.useState(prefill?.locationId ?? "");
1297
- const [services, setServices] = React12.useState([]);
1298
- const [serviceSearch, setServiceSearch] = React12.useState("");
1299
- const [date, setDate] = React12.useState(() => {
1079
+ const [loading, setLoading] = React13.useState(mode === "edit");
1080
+ const [bookingType, setBookingType] = React13.useState(prefill?.kind ?? defaultBookingType);
1081
+ const [title, setTitle] = React13.useState("");
1082
+ const [clientId, setClientId] = React13.useState(prefill?.clientId ?? "");
1083
+ const [clientSearch, setClientSearch] = React13.useState("");
1084
+ const [clientPhone, setClientPhone] = React13.useState("");
1085
+ const [professionalId, setProfessionalId] = React13.useState(prefill?.professionalId ?? "");
1086
+ const [locationId, setLocationId] = React13.useState(prefill?.locationId ?? "");
1087
+ const [services, setServices] = React13.useState([]);
1088
+ const [serviceSearch, setServiceSearch] = React13.useState("");
1089
+ const [date, setDate] = React13.useState(() => {
1300
1090
  if (prefill?.startsAt) return prefill.startsAt.slice(0, 10);
1301
1091
  return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
1302
1092
  });
1303
- const [startTime, setStartTime] = React12.useState(() => {
1093
+ const [startTime, setStartTime] = React13.useState(() => {
1304
1094
  if (prefill?.startsAt) {
1305
1095
  const d = new Date(prefill.startsAt);
1306
1096
  return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
1307
1097
  }
1308
1098
  return "09:00";
1309
1099
  });
1310
- const [status, setStatus] = React12.useState("scheduled");
1311
- const [notes, setNotes] = React12.useState("");
1312
- const [showNotes, setShowNotes] = React12.useState(false);
1313
- const [saving, setSaving] = React12.useState(false);
1314
- const [conflict, setConflict] = React12.useState(false);
1315
- const [quickCreate, setQuickCreate] = React12.useState(false);
1316
- const [newClientName, setNewClientName] = React12.useState("");
1317
- const [newClientPhone, setNewClientPhone] = React12.useState("");
1318
- const [newClientEmail, setNewClientEmail] = React12.useState("");
1319
- const [creatingClient, setCreatingClient] = React12.useState(false);
1320
- const [clientOptions, setClientOptions] = React12.useState([]);
1321
- const [clientSearching, setClientSearching] = React12.useState(false);
1322
- const [serviceOptions, setServiceOptions] = React12.useState([]);
1323
- const [initialServiceOptions, setInitialServiceOptions] = React12.useState([]);
1324
- const [editingDate, setEditingDate] = React12.useState(false);
1325
- const [editingTime, setEditingTime] = React12.useState(false);
1326
- const [confirmingDelete, setConfirmingDelete] = React12.useState(false);
1327
- const [deleting, setDeleting] = React12.useState(false);
1328
- const [editOrderId, setEditOrderId] = React12.useState(null);
1329
- const [editOrderTotal, setEditOrderTotal] = React12.useState(0);
1330
- const [editPaymentStatus, setEditPaymentStatus] = React12.useState("none");
1331
- const [paymentStatusLoading, setPaymentStatusLoading] = React12.useState(false);
1332
- const [modalTab, setModalTab] = React12.useState("appointment");
1333
- const [clientData, setClientData] = React12.useState(null);
1334
- const [clientDataLoading, setClientDataLoading] = React12.useState(false);
1335
- const [clientEditing, setClientEditing] = React12.useState(false);
1336
- const [cancellationReasons, setCancellationReasons] = React12.useState([]);
1337
- const [cancellationReasonId, setCancellationReasonId] = React12.useState("");
1338
- const [cancellationNotes, setCancellationNotes] = React12.useState("");
1339
- const [cancellationReasonsLoading, setCancellationReasonsLoading] = React12.useState(false);
1100
+ const [status, setStatus] = React13.useState("scheduled");
1101
+ const [notes, setNotes] = React13.useState("");
1102
+ const [showNotes, setShowNotes] = React13.useState(false);
1103
+ const [saving, setSaving] = React13.useState(false);
1104
+ const [conflict, setConflict] = React13.useState(false);
1105
+ const [serviceOptions, setServiceOptions] = React13.useState([]);
1106
+ const [initialServiceOptions, setInitialServiceOptions] = React13.useState([]);
1107
+ const [editingDate, setEditingDate] = React13.useState(false);
1108
+ const [editingTime, setEditingTime] = React13.useState(false);
1109
+ const [confirmingDelete, setConfirmingDelete] = React13.useState(false);
1110
+ const [deleting, setDeleting] = React13.useState(false);
1111
+ const [editOrderId, setEditOrderId] = React13.useState(null);
1112
+ const [editOrderTotal, setEditOrderTotal] = React13.useState(0);
1113
+ const [editPaymentStatus, setEditPaymentStatus] = React13.useState("none");
1114
+ const [paymentStatusLoading, setPaymentStatusLoading] = React13.useState(false);
1115
+ const [modalTab, setModalTab] = React13.useState("appointment");
1116
+ const [clientData, setClientData] = React13.useState(null);
1117
+ const [clientDataLoading, setClientDataLoading] = React13.useState(false);
1118
+ const [clientEditing, setClientEditing] = React13.useState(false);
1119
+ const [cancellationReasons, setCancellationReasons] = React13.useState([]);
1120
+ const [cancellationReasonId, setCancellationReasonId] = React13.useState("");
1121
+ const [cancellationNotes, setCancellationNotes] = React13.useState("");
1122
+ const [cancellationReasonsLoading, setCancellationReasonsLoading] = React13.useState(false);
1340
1123
  const activeType = config.bookingTypes.find((bt) => bt.id === bookingType) ?? config.bookingTypes[0];
1341
- React12.useEffect(() => {
1124
+ React13.useEffect(() => {
1342
1125
  if (!open) return;
1343
1126
  setClientId(prefill?.clientId ?? "");
1344
1127
  setClientSearch("");
@@ -1352,11 +1135,6 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1352
1135
  setShowNotes(false);
1353
1136
  setConflict(false);
1354
1137
  setLoading(mode === "edit");
1355
- setQuickCreate(false);
1356
- setNewClientName("");
1357
- setNewClientPhone("");
1358
- setNewClientEmail("");
1359
- setCreatingClient(false);
1360
1138
  setBookingType(prefill?.kind ?? defaultBookingType);
1361
1139
  setTitle("");
1362
1140
  setEditingDate(false);
@@ -1387,7 +1165,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1387
1165
  return "09:00";
1388
1166
  });
1389
1167
  }, [open]);
1390
- React12.useEffect(() => {
1168
+ React13.useEffect(() => {
1391
1169
  if (!open || mode !== "edit" || !bookingId) return;
1392
1170
  setLoading(true);
1393
1171
  provider2.getBookingById(bookingId).then((b) => {
@@ -1444,7 +1222,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1444
1222
  }
1445
1223
  }).catch(() => setLoading(false));
1446
1224
  }, [open, mode, bookingId]);
1447
- React12.useEffect(() => {
1225
+ React13.useEffect(() => {
1448
1226
  if (modalTab !== "client" || !clientId || !config.contactLookup || clientData) return;
1449
1227
  setClientDataLoading(true);
1450
1228
  config.contactLookup.getById(clientId).then((result) => {
@@ -1452,12 +1230,12 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1452
1230
  setClientDataLoading(false);
1453
1231
  }).catch(() => setClientDataLoading(false));
1454
1232
  }, [modalTab, clientId]);
1455
- React12.useEffect(() => {
1233
+ React13.useEffect(() => {
1456
1234
  if (!open || status !== "cancelled" || !config.cancellationDetails || cancellationReasons.length > 0) return;
1457
1235
  setCancellationReasonsLoading(true);
1458
1236
  config.cancellationDetails.listReasons().then((reasons) => setCancellationReasons(reasons)).catch(() => setCancellationReasons([])).finally(() => setCancellationReasonsLoading(false));
1459
1237
  }, [open, status, config.cancellationDetails, cancellationReasons.length]);
1460
- React12.useEffect(() => {
1238
+ React13.useEffect(() => {
1461
1239
  if (!open || !config.serviceLookup) return;
1462
1240
  const lookup = config.serviceLookup;
1463
1241
  const fetch = lookup.list ? () => lookup.list() : () => lookup.search("");
@@ -1466,25 +1244,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1466
1244
  setInitialServiceOptions(opts);
1467
1245
  }).catch(() => setInitialServiceOptions([]));
1468
1246
  }, [open]);
1469
- React12.useEffect(() => {
1470
- if (!config.contactLookup || clientSearch.length < 1 || clientId) {
1471
- setClientOptions([]);
1472
- setClientSearching(false);
1473
- return;
1474
- }
1475
- setClientSearching(true);
1476
- const timer = setTimeout(async () => {
1477
- try {
1478
- const results = await config.contactLookup.search(clientSearch);
1479
- setClientOptions(results.map((r) => ({ id: r.id, label: r.label, subtitle: r.subtitle, price: r.price, data: r.data })));
1480
- } catch {
1481
- setClientOptions([]);
1482
- }
1483
- setClientSearching(false);
1484
- }, 250);
1485
- return () => clearTimeout(timer);
1486
- }, [clientSearch, clientId]);
1487
- React12.useEffect(() => {
1247
+ React13.useEffect(() => {
1488
1248
  if (!config.serviceLookup || serviceSearch.length < 1) {
1489
1249
  setServiceOptions([]);
1490
1250
  return;
@@ -1500,19 +1260,19 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1500
1260
  return () => clearTimeout(timer);
1501
1261
  }, [serviceSearch]);
1502
1262
  const serviceDuration = services.reduce((sum, s) => sum + s.durationMinutes, 0);
1503
- const selectionDuration = React12.useMemo(() => {
1263
+ const selectionDuration = React13.useMemo(() => {
1504
1264
  if (!prefill?.startsAt || !prefill?.endsAt) return 0;
1505
1265
  return Math.round((new Date(prefill.endsAt).getTime() - new Date(prefill.startsAt).getTime()) / 6e4);
1506
1266
  }, [prefill?.startsAt, prefill?.endsAt]);
1507
1267
  const totalDuration = serviceDuration > 0 ? serviceDuration : selectionDuration;
1508
1268
  const totalPrice = services.reduce((sum, s) => sum + s.price, 0);
1509
1269
  const { currency } = config;
1510
- const endTime = React12.useMemo(() => {
1270
+ const endTime = React13.useMemo(() => {
1511
1271
  const [h, m] = startTime.split(":").map(Number);
1512
1272
  const total = h * 60 + m + totalDuration;
1513
1273
  return `${String(Math.floor(total / 60) % 24).padStart(2, "0")}:${String(total % 60).padStart(2, "0")}`;
1514
1274
  }, [startTime, totalDuration]);
1515
- React12.useEffect(() => {
1275
+ React13.useEffect(() => {
1516
1276
  if (!professionalId || totalDuration === 0) {
1517
1277
  setConflict(false);
1518
1278
  return;
@@ -1527,12 +1287,6 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1527
1287
  }, 300);
1528
1288
  return () => clearTimeout(timer);
1529
1289
  }, [professionalId, date, startTime, endTime, bookingId]);
1530
- function selectClient(opt) {
1531
- setClientId(opt.id);
1532
- setClientSearch(opt.label);
1533
- setClientOptions([]);
1534
- setClientPhone(opt.data?.phone ?? "");
1535
- }
1536
1290
  function addService(opt) {
1537
1291
  const duration = opt.data?.duration_minutes || 60;
1538
1292
  setServices((p) => [...p, { serviceId: opt.id, name: opt.label, durationMinutes: duration, price: opt.price ?? 0 }]);
@@ -1545,6 +1299,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1545
1299
  async function handleSubmit(e) {
1546
1300
  e.preventDefault();
1547
1301
  if (!canSubmit) return;
1302
+ if (mode === "create" && await guardBookings() === "blocked") return;
1548
1303
  setSaving(true);
1549
1304
  try {
1550
1305
  const startsAt = (/* @__PURE__ */ new Date(`${date}T${startTime}:00`)).toISOString();
@@ -1559,6 +1314,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1559
1314
  title: activeType.fields.title ? title.trim() : void 0,
1560
1315
  services: services.map((s) => ({ serviceId: s.serviceId, name: s.name, durationMinutes: s.durationMinutes, price: s.price }))
1561
1316
  });
1317
+ saas.invalidateLimit("bookings_month");
1562
1318
  onClose();
1563
1319
  onCreated?.(newBooking.id, externalSource);
1564
1320
  } else if (bookingId) {
@@ -1593,14 +1349,16 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1593
1349
  await deleteBooking(bookingId);
1594
1350
  onClose();
1595
1351
  } catch {
1352
+ setConfirmingDelete(false);
1353
+ } finally {
1354
+ setDeleting(false);
1596
1355
  }
1597
- setDeleting(false);
1598
1356
  }
1599
1357
  const isPaidBooking = editPaymentStatus === "paid";
1600
1358
  const selectedCancellationReason = cancellationReasons.find((reason) => reason.id === cancellationReasonId);
1601
1359
  const needsCancellationReason = mode === "edit" && status === "cancelled" && !!config.cancellationDetails;
1602
1360
  const needsCancellationNotes = needsCancellationReason && !!selectedCancellationReason?.requiresNotes;
1603
- const formSnapshot = React12.useMemo(
1361
+ const formSnapshot = React13.useMemo(
1604
1362
  () => 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 })) }),
1605
1363
  [title, clientId, professionalId, locationId, date, startTime, status, notes, cancellationReasonId, cancellationNotes, services]
1606
1364
  );
@@ -1611,9 +1369,9 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1611
1369
  if (!v) onClose();
1612
1370
  }, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.ModalContent, { size: "lg", noPadding: true, children: [
1613
1371
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-5 pt-4 pb-3 shrink-0 space-y-3", children: [
1614
- /* @__PURE__ */ jsxRuntime.jsx(ui.ModalTitle, { className: "sr-only", children: mode === "create" ? t("agenda.appointment.new") : t("agenda.appointment.edit") }),
1372
+ /* @__PURE__ */ jsxRuntime.jsx(ui.ModalTitle, { className: "sr-only", children: mode === "create" ? t2("agenda.appointment.new") : t2("agenda.appointment.edit") }),
1615
1373
  showTabs && mode === "create" ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-1 border-b fayz-glass-divider", children: config.bookingTypes.map((bt) => {
1616
- const label = t(`agenda.bookingType.${bt.id}`) || bt.label;
1374
+ const label = t2(`agenda.bookingType.${bt.id}`) || bt.label;
1617
1375
  const isActive = bookingType === bt.id;
1618
1376
  const BtIcon = BOOKING_TYPE_ICONS[bt.icon] ?? lucideReact.Calendar;
1619
1377
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1631,9 +1389,9 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1631
1389
  bt.id
1632
1390
  );
1633
1391
  }) }) : mode === "edit" && (config.clientEntityDef && clientId || config.modules.financial && editOrderId ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-1 border-b fayz-glass-divider", children: [
1634
- { id: "appointment", label: t(`agenda.bookingType.${bookingType}`) || activeType.label, icon: lucideReact.Calendar, show: true },
1635
- { id: "client", label: t("agenda.appointment.clientTab"), icon: lucideReact.User, show: !!config.clientEntityDef && !!clientId },
1636
- { id: "financial", label: t("agenda.payment.title"), icon: lucideReact.DollarSign, show: config.modules.financial && !!editOrderId }
1392
+ { id: "appointment", label: t2(`agenda.bookingType.${bookingType}`) || activeType.label, icon: lucideReact.Calendar, show: true },
1393
+ { id: "client", label: t2("agenda.appointment.clientTab"), icon: lucideReact.User, show: !!config.clientEntityDef && !!clientId },
1394
+ { id: "financial", label: t2("agenda.payment.title"), icon: lucideReact.DollarSign, show: config.modules.financial && !!editOrderId }
1637
1395
  ].filter((tab) => tab.show).map((tab) => {
1638
1396
  const isActive = modalTab === tab.id;
1639
1397
  return /* @__PURE__ */ jsxRuntime.jsxs(
@@ -1652,7 +1410,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1652
1410
  },
1653
1411
  tab.id
1654
1412
  );
1655
- }) }) : /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-semibold", children: t(`agenda.bookingType.${bookingType}`) || activeType.label })),
1413
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "text-sm font-semibold", children: t2(`agenda.bookingType.${bookingType}`) || activeType.label })),
1656
1414
  modalTab === "appointment" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1657
1415
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
1658
1416
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
@@ -1710,7 +1468,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1710
1468
  ] }),
1711
1469
  conflict && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 rounded-lg bg-destructive/10 px-3 py-1.5 text-xs text-destructive", children: [
1712
1470
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertTriangle, { className: "h-3.5 w-3.5 shrink-0" }),
1713
- t("agenda.appointment.conflict")
1471
+ t2("agenda.appointment.conflict")
1714
1472
  ] })
1715
1473
  ] })
1716
1474
  ] }),
@@ -1751,7 +1509,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1751
1509
  onBack: () => setModalTab("appointment"),
1752
1510
  onEdit: () => setClientEditing(true)
1753
1511
  }
1754
- ) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground py-4 text-center", children: t("agenda.appointment.noClientData") }) }),
1512
+ ) : /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground py-4 text-center", children: t2("agenda.appointment.noClientData") }) }),
1755
1513
  modalTab === "financial" && editOrderId && config.financialBridge && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1 overflow-y-auto", children: /* @__PURE__ */ jsxRuntime.jsx(
1756
1514
  BookingPaymentPanel,
1757
1515
  {
@@ -1788,7 +1546,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1788
1546
  type: "text",
1789
1547
  value: title,
1790
1548
  onChange: (e) => setTitle(e.target.value),
1791
- placeholder: t("agenda.appointment.addTitle"),
1549
+ placeholder: t2("agenda.appointment.addTitle"),
1792
1550
  autoFocus: mode === "create",
1793
1551
  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"
1794
1552
  }
@@ -1796,133 +1554,25 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1796
1554
  ] }),
1797
1555
  activeType.fields.client && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 py-1", children: [
1798
1556
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.User, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1799
- /* @__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: [
1800
- /* @__PURE__ */ jsxRuntime.jsx(saas.PersonLink, { personId: clientId, name: clientSearch, size: "default", className: "flex-1 min-w-0" }),
1801
- clientPhone && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground shrink-0", children: clientPhone }),
1802
- !isPaidBooking && /* @__PURE__ */ jsxRuntime.jsx(
1803
- "button",
1804
- {
1805
- type: "button",
1806
- onClick: () => {
1807
- setClientId("");
1808
- setClientSearch("");
1809
- setClientPhone("");
1810
- },
1811
- className: "p-0.5 text-muted-foreground hover:text-destructive shrink-0",
1812
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" })
1813
- }
1814
- )
1815
- ] }) : !quickCreate ? /* @__PURE__ */ jsxRuntime.jsx(
1816
- SearchCombobox,
1557
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
1558
+ saas.ContactPicker,
1817
1559
  {
1818
- value: clientSearch,
1819
- onChange: (v) => {
1820
- setClientSearch(v);
1821
- setClientId("");
1822
- },
1823
- onSelect: selectClient,
1824
- options: clientOptions,
1825
- loading: clientSearching,
1826
- placeholder: t("agenda.appointment.searchClient"),
1827
- allowCreate: true,
1828
- createLabel: t("agenda.appointment.newClient"),
1829
- onCreateNew: (name) => {
1830
- setQuickCreate(true);
1831
- setNewClientName(name);
1832
- setNewClientPhone("");
1833
- setNewClientEmail("");
1560
+ value: clientId || clientSearch ? { id: clientId || void 0, name: clientSearch, phone: clientPhone } : null,
1561
+ onChange: (contact) => {
1562
+ setClientId(contact?.id ?? "");
1563
+ setClientSearch(contact?.name ?? "");
1564
+ setClientPhone(contact?.phone ?? "");
1834
1565
  },
1835
- minimal: true,
1836
- inlineLabel: t("agenda.appointment.addClient")
1566
+ kind: config.clientKind,
1567
+ extensionTable: "clients",
1568
+ lookup: config.contactLookup,
1569
+ clearable: !isPaidBooking,
1570
+ variant: "inline",
1571
+ placeholder: t2("agenda.appointment.searchClient"),
1572
+ createLabel: t2("agenda.appointment.newClient"),
1573
+ hint: /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] text-warning", children: t2("agenda.appointment.selectClientHint") })
1837
1574
  }
1838
- ) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] p-2.5 space-y-2", children: [
1839
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
1840
- /* @__PURE__ */ jsxRuntime.jsx(
1841
- "input",
1842
- {
1843
- type: "text",
1844
- value: newClientName,
1845
- onChange: (e) => setNewClientName(e.target.value),
1846
- placeholder: t("agenda.appointment.name"),
1847
- className: "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",
1848
- autoFocus: true
1849
- }
1850
- ),
1851
- /* @__PURE__ */ jsxRuntime.jsx(
1852
- "button",
1853
- {
1854
- type: "button",
1855
- onClick: () => setQuickCreate(false),
1856
- className: "p-1 text-muted-foreground hover:text-foreground shrink-0",
1857
- children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3.5 w-3.5" })
1858
- }
1859
- )
1860
- ] }),
1861
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-2 gap-2", children: [
1862
- /* @__PURE__ */ jsxRuntime.jsx(
1863
- "input",
1864
- {
1865
- type: "tel",
1866
- value: newClientPhone,
1867
- onChange: (e) => setNewClientPhone(e.target.value),
1868
- placeholder: t("agenda.appointment.phone"),
1869
- className: "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 focus:outline-none focus:ring-2 focus:ring-ring"
1870
- }
1871
- ),
1872
- /* @__PURE__ */ jsxRuntime.jsx(
1873
- "input",
1874
- {
1875
- type: "email",
1876
- value: newClientEmail,
1877
- onChange: (e) => setNewClientEmail(e.target.value),
1878
- placeholder: t("agenda.appointment.emailOptional"),
1879
- className: "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 focus:outline-none focus:ring-2 focus:ring-ring"
1880
- }
1881
- )
1882
- ] }),
1883
- /* @__PURE__ */ jsxRuntime.jsx(
1884
- "button",
1885
- {
1886
- type: "button",
1887
- disabled: creatingClient || !newClientName.trim(),
1888
- onClick: async () => {
1889
- setCreatingClient(true);
1890
- try {
1891
- const { getSupabaseClientOptional: getSupabaseClientOptional4 } = await import('@fayz-ai/core');
1892
- const { getAgendaTenantId: getAgendaTenantId2 } = await import('./tenant-222EHYUC.cjs');
1893
- const supabase = getSupabaseClientOptional4();
1894
- const tenantId = getAgendaTenantId2();
1895
- if (!supabase || !tenantId) throw new Error("Not initialized");
1896
- const { data: personRow, error: personError } = await supabase.from("people").insert({
1897
- tenant_id: tenantId,
1898
- kind: config.clientKind,
1899
- name: newClientName.trim(),
1900
- phone: newClientPhone.trim() || null,
1901
- email: newClientEmail.trim() || null
1902
- }).select("id, name").single();
1903
- if (personError) throw personError;
1904
- const { error: clientError } = await supabase.from("clients").insert({
1905
- person_id: personRow.id,
1906
- tenant_id: tenantId
1907
- });
1908
- if (clientError) {
1909
- await supabase.from("people").delete().eq("id", personRow.id);
1910
- throw clientError;
1911
- }
1912
- setClientId(personRow.id);
1913
- setClientSearch(personRow.name);
1914
- setQuickCreate(false);
1915
- } catch (err) {
1916
- const { toast: toast3 } = await import('@fayz-ai/ui');
1917
- toast3.error(t("agenda.appointment.createClientFailed"), { description: err?.message });
1918
- }
1919
- setCreatingClient(false);
1920
- },
1921
- className: "w-full rounded-md bg-primary border border-primary px-3 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 shadow-button-primary active:shadow-button-inset disabled:opacity-40 transition-colors",
1922
- children: creatingClient ? t("agenda.appointment.creating") : t("agenda.appointment.create")
1923
- }
1924
- )
1925
- ] }) })
1575
+ ) })
1926
1576
  ] }),
1927
1577
  activeType.fields.location && config.modules.locationSelection && (storeLocations.length > 0 || config.locations.length > 0) && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 py-1", children: [
1928
1578
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MapPin, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
@@ -1950,7 +1600,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1950
1600
  step: 5,
1951
1601
  className: "w-10 shrink-0",
1952
1602
  disabled: isPaidBooking,
1953
- tooltip: t("agenda.appointment.duration")
1603
+ tooltip: t2("agenda.appointment.duration")
1954
1604
  }
1955
1605
  ),
1956
1606
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] text-muted-foreground", children: "min" })
@@ -1966,20 +1616,20 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1966
1616
  step: 0.01,
1967
1617
  className: "w-14 shrink-0",
1968
1618
  disabled: isPaidBooking,
1969
- tooltip: t("agenda.appointment.price")
1619
+ tooltip: t2("agenda.appointment.price")
1970
1620
  }
1971
1621
  )
1972
1622
  ] }),
1973
1623
  !isPaidBooking && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => removeService(idx), className: "p-0.5 ml-2 text-muted-foreground hover:text-destructive shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" }) })
1974
1624
  ] }, idx)) }),
1975
1625
  !isPaidBooking && /* @__PURE__ */ jsxRuntime.jsx(
1976
- SearchCombobox,
1626
+ ui.SearchCombobox,
1977
1627
  {
1978
1628
  value: serviceSearch,
1979
1629
  onChange: setServiceSearch,
1980
1630
  onSelect: addService,
1981
1631
  options: serviceOptions,
1982
- placeholder: t("agenda.appointment.searchService"),
1632
+ placeholder: t2("agenda.appointment.searchService"),
1983
1633
  onBlurEmpty: () => {
1984
1634
  },
1985
1635
  onFocus: () => {
@@ -1996,7 +1646,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1996
1646
  ] });
1997
1647
  },
1998
1648
  minimal: true,
1999
- inlineLabel: services.length > 0 ? t("agenda.appointment.addService") : t("agenda.appointment.addServiceFirst"),
1649
+ inlineLabel: services.length > 0 ? t2("agenda.appointment.addService") : t2("agenda.appointment.addServiceFirst"),
2000
1650
  inlineIcon: services.length > 0 ? "plus" : "search"
2001
1651
  }
2002
1652
  )
@@ -2017,7 +1667,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
2017
1667
  disabled: cancellationReasonsLoading,
2018
1668
  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",
2019
1669
  children: [
2020
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: cancellationReasonsLoading ? t("agenda.cancellation.loadingReasons") : t("agenda.cancellation.selectReason") }),
1670
+ /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", children: cancellationReasonsLoading ? t2("agenda.cancellation.loadingReasons") : t2("agenda.cancellation.selectReason") }),
2021
1671
  cancellationReasons.map((reason) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: reason.id, children: [
2022
1672
  reason.label,
2023
1673
  reason.requiresNotes ? " *" : ""
@@ -2030,7 +1680,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
2030
1680
  {
2031
1681
  value: cancellationNotes,
2032
1682
  onChange: (event) => setCancellationNotes(event.target.value),
2033
- placeholder: needsCancellationNotes ? t("agenda.cancellation.notesRequired") : t("agenda.cancellation.notesOptional"),
1683
+ placeholder: needsCancellationNotes ? t2("agenda.cancellation.notesRequired") : t2("agenda.cancellation.notesOptional"),
2034
1684
  rows: 2,
2035
1685
  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"
2036
1686
  }
@@ -2045,7 +1695,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
2045
1695
  type: "button",
2046
1696
  onClick: () => setShowNotes(true),
2047
1697
  className: "text-sm text-muted-foreground hover:text-foreground transition-colors text-left py-1.5 pl-2",
2048
- children: t("agenda.appointment.addNotes")
1698
+ children: t2("agenda.appointment.addNotes")
2049
1699
  }
2050
1700
  )
2051
1701
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3 py-1", children: [
@@ -2055,7 +1705,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
2055
1705
  {
2056
1706
  value: notes,
2057
1707
  onChange: (e) => setNotes(e.target.value),
2058
- placeholder: t("agenda.appointment.addDescription"),
1708
+ placeholder: t2("agenda.appointment.addDescription"),
2059
1709
  rows: 2,
2060
1710
  autoFocus: true,
2061
1711
  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 resize-none focus:outline-none focus:ring-2 focus:ring-ring"
@@ -2065,14 +1715,14 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
2065
1715
  ] })
2066
1716
  ] }),
2067
1717
  modalTab === "appointment" && !clientEditing && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-5 py-2.5 border-t fayz-glass-divider bg-transparent shrink-0 sm:rounded-b-2xl", children: confirmingDelete ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2068
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-destructive font-medium", children: t("agenda.appointment.deleteConfirm") }),
1718
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-destructive font-medium", children: t2("agenda.appointment.deleteConfirm") }),
2069
1719
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
2070
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "outline", size: "sm", onClick: () => setConfirmingDelete(false), children: t("agenda.appointment.cancel") }),
2071
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "destructive", size: "sm", onClick: handleDelete, disabled: deleting, children: deleting ? t("agenda.appointment.deleting") : t("agenda.appointment.confirmDelete") })
1720
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "outline", size: "sm", onClick: () => setConfirmingDelete(false), children: t2("agenda.appointment.cancel") }),
1721
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "destructive", size: "sm", onClick: handleDelete, disabled: deleting, children: deleting ? t2("agenda.appointment.deleting") : t2("agenda.appointment.confirmDelete") })
2072
1722
  ] })
2073
1723
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
2074
1724
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
2075
- mode === "edit" && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => setConfirmingDelete(true), className: "text-xs text-destructive font-medium hover:underline", children: t("agenda.appointment.delete") }),
1725
+ mode === "edit" && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => setConfirmingDelete(true), className: "text-xs text-destructive font-medium hover:underline", children: t2("agenda.appointment.delete") }),
2076
1726
  services.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
2077
1727
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Clock, { className: "h-3 w-3" }),
2078
1728
  totalDuration,
@@ -2090,7 +1740,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
2090
1740
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { className: "h-2.5 w-2.5" }),
2091
1741
  fmtCurrency(totalPrice, currency.locale, currency.code),
2092
1742
  " \xB7 ",
2093
- t("agenda.payment.paid")
1743
+ t2("agenda.payment.paid")
2094
1744
  ]
2095
1745
  }
2096
1746
  ) : date <= (/* @__PURE__ */ new Date()).toISOString().slice(0, 10) ? /* @__PURE__ */ jsxRuntime.jsxs(
@@ -2106,7 +1756,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
2106
1756
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.HandCoins, { className: "h-3 w-3" }),
2107
1757
  fmtCurrency(totalPrice, currency.locale, currency.code),
2108
1758
  " \xB7 ",
2109
- t("agenda.payment.collect")
1759
+ t2("agenda.payment.collect")
2110
1760
  ]
2111
1761
  }
2112
1762
  ) : /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
@@ -2119,8 +1769,8 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
2119
1769
  ] })
2120
1770
  ] }),
2121
1771
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
2122
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "outline", size: "sm", onClick: onClose, className: "hidden sm:inline-flex", children: t("agenda.appointment.cancel") }),
2123
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", variant: "default", size: "sm", disabled: !canSubmit || !isDirty, children: saving ? t("agenda.appointment.saving") : mode === "create" ? t("agenda.appointment.save") : t("agenda.appointment.update") })
1772
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "button", variant: "outline", size: "sm", onClick: onClose, className: "hidden sm:inline-flex", children: t2("agenda.appointment.cancel") }),
1773
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { type: "submit", variant: "default", size: "sm", disabled: !canSubmit || !isDirty, children: saving ? t2("agenda.appointment.saving") : mode === "create" ? t2("agenda.appointment.save") : t2("agenda.appointment.update") })
2124
1774
  ] })
2125
1775
  ] }) })
2126
1776
  ] }),
@@ -2135,7 +1785,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
2135
1785
  className: "inline-flex items-center gap-1 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors",
2136
1786
  children: [
2137
1787
  /* @__PURE__ */ jsxRuntime.jsx("svg", { className: "h-3 w-3", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M10 12L6 8l4-4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }),
2138
- t("agenda.payment.backToAppointment")
1788
+ t2("agenda.payment.backToAppointment")
2139
1789
  ]
2140
1790
  }
2141
1791
  ) }) })
@@ -2164,19 +1814,19 @@ function FloatingPanel({
2164
1814
  children,
2165
1815
  zIndex = 100
2166
1816
  }) {
2167
- const ref = React12.useRef(null);
2168
- const [phase, setPhase] = React12.useState("visible");
2169
- const phaseRef = React12.useRef("visible");
1817
+ const ref = React13.useRef(null);
1818
+ const [phase, setPhase] = React13.useState("visible");
1819
+ const phaseRef = React13.useRef("visible");
2170
1820
  phaseRef.current = phase;
2171
- const onCloseRef = React12.useRef(onClose);
1821
+ const onCloseRef = React13.useRef(onClose);
2172
1822
  onCloseRef.current = onClose;
2173
- const expandCallbackRef = React12.useRef(null);
2174
- const dismiss = React12.useCallback(() => {
1823
+ const expandCallbackRef = React13.useRef(null);
1824
+ const dismiss = React13.useCallback(() => {
2175
1825
  if (phaseRef.current !== "visible") return;
2176
1826
  setPhase("dismissing");
2177
1827
  setTimeout(() => onCloseRef.current(), 150);
2178
1828
  }, []);
2179
- const expandThen = React12.useCallback((callback) => {
1829
+ const expandThen = React13.useCallback((callback) => {
2180
1830
  if (phaseRef.current !== "visible") return;
2181
1831
  expandCallbackRef.current = callback;
2182
1832
  setPhase("expanding");
@@ -2185,7 +1835,7 @@ function FloatingPanel({
2185
1835
  onCloseRef.current();
2186
1836
  }, 220);
2187
1837
  }, []);
2188
- React12.useEffect(() => {
1838
+ React13.useEffect(() => {
2189
1839
  function handleClick(e) {
2190
1840
  if (phaseRef.current !== "visible") return;
2191
1841
  if (ref.current && !ref.current.contains(e.target)) {
@@ -2236,10 +1886,10 @@ function fmtCurrency2(amount, locale, code) {
2236
1886
  return new Intl.NumberFormat(locale, { style: "currency", currency: code }).format(amount);
2237
1887
  }
2238
1888
  function PopoverStatusSelect({ value, statuses, bookingStartsAt, onChange }) {
2239
- const [open, setOpen] = React12.useState(false);
2240
- const ref = React12.useRef(null);
1889
+ const [open, setOpen] = React13.useState(false);
1890
+ const ref = React13.useRef(null);
2241
1891
  const current = statuses.find((s) => s.value === value);
2242
- React12.useEffect(() => {
1892
+ React13.useEffect(() => {
2243
1893
  if (!open) return;
2244
1894
  const h = (e) => {
2245
1895
  if (ref.current && !ref.current.contains(e.target)) setOpen(false);
@@ -2287,13 +1937,13 @@ function PopoverStatusSelect({ value, statuses, bookingStartsAt, onChange }) {
2287
1937
  ] });
2288
1938
  }
2289
1939
  function AppointmentPopover({ booking, position, onClose, onEdit }) {
2290
- const t = core.useTranslation();
1940
+ const t2 = core.useTranslation();
2291
1941
  const config = useAgendaConfig();
2292
1942
  const deleteBooking = useAgendaStore((s) => s.deleteBooking);
2293
1943
  const updateStatus = useAgendaStore((s) => s.updateBookingStatus);
2294
1944
  const openModal = useAgendaStore((s) => s.openAppointmentModal);
2295
- const [confirmingDelete, setConfirmingDelete] = React12.useState(false);
2296
- const [deleting, setDeleting] = React12.useState(false);
1945
+ const [confirmingDelete, setConfirmingDelete] = React13.useState(false);
1946
+ const [deleting, setDeleting] = React13.useState(false);
2297
1947
  const statusConfig = config.statuses.find((s) => s.value === booking.status);
2298
1948
  const color = statusConfig?.color ?? "#6b7280";
2299
1949
  const timeRange = `${fmtTime(booking.startsAt)}${booking.endsAt ? " \u2013 " + fmtTime(booking.endsAt) : ""}`;
@@ -2308,13 +1958,13 @@ function AppointmentPopover({ booking, position, onClose, onEdit }) {
2308
1958
  }
2309
1959
  return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: /* @__PURE__ */ jsxRuntime.jsx(FloatingPanel, { position, width: 320, height: 340, onClose, children: (panel) => /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2310
1960
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-end gap-0.5 px-3 pt-3 pb-1", children: confirmingDelete ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2311
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-destructive font-medium mr-auto pl-1", children: t("agenda.appointment.deleteConfirm") }),
1961
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-destructive font-medium mr-auto pl-1", children: t2("agenda.appointment.deleteConfirm") }),
2312
1962
  /* @__PURE__ */ jsxRuntime.jsx(
2313
1963
  "button",
2314
1964
  {
2315
1965
  onClick: () => setConfirmingDelete(false),
2316
1966
  className: "rounded px-2 py-0.5 text-[11px] font-medium hover:bg-muted/50 transition-colors",
2317
- children: t("agenda.appointment.cancel")
1967
+ children: t2("agenda.appointment.cancel")
2318
1968
  }
2319
1969
  ),
2320
1970
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2323,7 +1973,7 @@ function AppointmentPopover({ booking, position, onClose, onEdit }) {
2323
1973
  onClick: handleDelete,
2324
1974
  disabled: deleting,
2325
1975
  className: "rounded bg-destructive border border-destructive px-2 py-0.5 text-[11px] font-medium text-destructive-foreground hover:bg-destructive/90 shadow-button-primary active:shadow-button-inset disabled:opacity-40 transition-colors",
2326
- children: deleting ? t("agenda.appointment.deleting") : t("agenda.appointment.confirmDelete")
1976
+ children: deleting ? t2("agenda.appointment.deleting") : t2("agenda.appointment.confirmDelete")
2327
1977
  }
2328
1978
  )
2329
1979
  ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
@@ -2332,7 +1982,7 @@ function AppointmentPopover({ booking, position, onClose, onEdit }) {
2332
1982
  {
2333
1983
  onClick: () => panel.expandThen(() => onEdit(booking.id)),
2334
1984
  className: "p-1.5 rounded-full hover:bg-muted transition-colors",
2335
- title: t("common.edit"),
1985
+ title: t2("common.edit"),
2336
1986
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-4 w-4 text-muted-foreground" })
2337
1987
  }
2338
1988
  ),
@@ -2341,7 +1991,7 @@ function AppointmentPopover({ booking, position, onClose, onEdit }) {
2341
1991
  {
2342
1992
  onClick: () => setConfirmingDelete(true),
2343
1993
  className: "p-1.5 rounded-full hover:bg-destructive/10 transition-colors",
2344
- title: t("common.delete"),
1994
+ title: t2("common.delete"),
2345
1995
  children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-4 w-4 text-muted-foreground hover:text-destructive" })
2346
1996
  }
2347
1997
  ),
@@ -2351,7 +2001,7 @@ function AppointmentPopover({ booking, position, onClose, onEdit }) {
2351
2001
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-3", children: [
2352
2002
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1.5 h-3 w-3 rounded shrink-0", style: { backgroundColor: color } }),
2353
2003
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0", children: [
2354
- 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") }),
2004
+ 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 || t2("agenda.appointment.untitled") }),
2355
2005
  /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-sm text-muted-foreground mt-0.5", children: [
2356
2006
  fmtDate(booking.startsAt),
2357
2007
  " \xB7 ",
@@ -2389,22 +2039,22 @@ function AppointmentPopover({ booking, position, onClose, onEdit }) {
2389
2039
  const hasFinancial = config.modules.financial && booking.orderId;
2390
2040
  if (!hasFinancial) {
2391
2041
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pt-2 border-t fayz-glass-divider flex items-center justify-between text-sm", children: [
2392
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: t("agenda.appointment.total") }),
2042
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: t2("agenda.appointment.total") }),
2393
2043
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium", children: total })
2394
2044
  ] });
2395
2045
  }
2396
2046
  const badgeConfig = {
2397
- none: { icon: lucideReact.HandCoins, bg: "bg-warning/10 hover:bg-warning/15", text: "text-warning", label: `${total} \xB7 ${t("agenda.payment.collect")}` },
2398
- pending: { icon: lucideReact.Clock, bg: "bg-warning/10 hover:bg-warning/15", text: "text-warning", label: `${total} \xB7 ${t("agenda.payment.pending")}` },
2399
- partial: { icon: lucideReact.DollarSign, bg: "bg-warning/10 hover:bg-warning/15", text: "text-warning", label: `${total} \xB7 ${t("agenda.payment.partial")}` },
2400
- paid: { icon: lucideReact.Check, bg: "bg-success/10 hover:bg-success/15", text: "text-success", label: `${total} \xB7 ${t("agenda.payment.paid")}` },
2401
- overdue: { icon: lucideReact.AlertTriangle, bg: "bg-destructive/10 hover:bg-destructive/15", text: "text-destructive", label: `${total} \xB7 ${t("agenda.payment.overdue")}` },
2402
- cancelled: { icon: lucideReact.X, bg: "bg-muted hover:bg-muted/80", text: "text-muted-foreground", label: `${total} \xB7 ${t("agenda.payment.cancelled")}` }
2047
+ none: { icon: lucideReact.HandCoins, bg: "bg-warning/10 hover:bg-warning/15", text: "text-warning", label: `${total} \xB7 ${t2("agenda.payment.collect")}` },
2048
+ pending: { icon: lucideReact.Clock, bg: "bg-warning/10 hover:bg-warning/15", text: "text-warning", label: `${total} \xB7 ${t2("agenda.payment.pending")}` },
2049
+ partial: { icon: lucideReact.DollarSign, bg: "bg-warning/10 hover:bg-warning/15", text: "text-warning", label: `${total} \xB7 ${t2("agenda.payment.partial")}` },
2050
+ paid: { icon: lucideReact.Check, bg: "bg-success/10 hover:bg-success/15", text: "text-success", label: `${total} \xB7 ${t2("agenda.payment.paid")}` },
2051
+ overdue: { icon: lucideReact.AlertTriangle, bg: "bg-destructive/10 hover:bg-destructive/15", text: "text-destructive", label: `${total} \xB7 ${t2("agenda.payment.overdue")}` },
2052
+ cancelled: { icon: lucideReact.X, bg: "bg-muted hover:bg-muted/80", text: "text-muted-foreground", label: `${total} \xB7 ${t2("agenda.payment.cancelled")}` }
2403
2053
  };
2404
2054
  const cfg = badgeConfig[ps] ?? badgeConfig.none;
2405
2055
  const StatusIcon = cfg.icon;
2406
2056
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pt-2 border-t fayz-glass-divider flex items-center justify-between", children: [
2407
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: t("agenda.appointment.total") }),
2057
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-muted-foreground", children: t2("agenda.appointment.total") }),
2408
2058
  /* @__PURE__ */ jsxRuntime.jsxs(
2409
2059
  "button",
2410
2060
  {
@@ -2451,9 +2101,9 @@ function Tooltip2({ anchor, label }) {
2451
2101
  );
2452
2102
  }
2453
2103
  function StatusDot({ color, label, isActive, disabled, onClick }) {
2454
- const [hover, setHover] = React12.useState(false);
2455
- const btnRef = React12.useRef(null);
2456
- const [rect, setRect] = React12.useState(null);
2104
+ const [hover, setHover] = React13.useState(false);
2105
+ const btnRef = React13.useRef(null);
2106
+ const [rect, setRect] = React13.useState(null);
2457
2107
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2458
2108
  /* @__PURE__ */ jsxRuntime.jsx(
2459
2109
  "button",
@@ -2476,12 +2126,12 @@ function StatusDot({ color, label, isActive, disabled, onClick }) {
2476
2126
  ] });
2477
2127
  }
2478
2128
  function EventContextMenu({ booking, position, onClose, onEdit }) {
2479
- const t = core.useTranslation();
2129
+ const t2 = core.useTranslation();
2480
2130
  const config = useAgendaConfig();
2481
2131
  const updateStatus = useAgendaStore((s) => s.updateBookingStatus);
2482
2132
  const deleteBooking = useAgendaStore((s) => s.deleteBooking);
2483
- const [confirming, setConfirming] = React12.useState(false);
2484
- const [deleting, setDeleting] = React12.useState(false);
2133
+ const [confirming, setConfirming] = React13.useState(false);
2134
+ const [deleting, setDeleting] = React13.useState(false);
2485
2135
  async function handleDelete() {
2486
2136
  setDeleting(true);
2487
2137
  try {
@@ -2501,19 +2151,19 @@ function EventContextMenu({ booking, position, onClose, onEdit }) {
2501
2151
  children: [
2502
2152
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-4 w-4 text-muted-foreground" }),
2503
2153
  " ",
2504
- t("agenda.contextMenu.edit")
2154
+ t2("agenda.contextMenu.edit")
2505
2155
  ]
2506
2156
  }
2507
2157
  ),
2508
2158
  confirming ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between px-4 py-2", children: [
2509
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-destructive font-medium", children: t("agenda.appointment.deleteConfirm") }),
2159
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs text-destructive font-medium", children: t2("agenda.appointment.deleteConfirm") }),
2510
2160
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
2511
2161
  /* @__PURE__ */ jsxRuntime.jsx(
2512
2162
  "button",
2513
2163
  {
2514
2164
  onClick: () => setConfirming(false),
2515
2165
  className: "rounded px-2 py-0.5 text-[11px] font-medium hover:bg-muted/50 transition-colors",
2516
- children: t("agenda.appointment.cancel")
2166
+ children: t2("agenda.appointment.cancel")
2517
2167
  }
2518
2168
  ),
2519
2169
  /* @__PURE__ */ jsxRuntime.jsx(
@@ -2522,7 +2172,7 @@ function EventContextMenu({ booking, position, onClose, onEdit }) {
2522
2172
  onClick: handleDelete,
2523
2173
  disabled: deleting,
2524
2174
  className: "rounded bg-destructive border border-destructive px-2 py-0.5 text-[11px] font-medium text-destructive-foreground hover:bg-destructive/90 shadow-button-primary active:shadow-button-inset disabled:opacity-40 transition-colors",
2525
- children: deleting ? t("agenda.appointment.deleting") : t("agenda.appointment.confirmDelete")
2175
+ children: deleting ? t2("agenda.appointment.deleting") : t2("agenda.appointment.confirmDelete")
2526
2176
  }
2527
2177
  )
2528
2178
  ] })
@@ -2534,20 +2184,20 @@ function EventContextMenu({ booking, position, onClose, onEdit }) {
2534
2184
  children: [
2535
2185
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-4 w-4" }),
2536
2186
  " ",
2537
- t("agenda.contextMenu.delete")
2187
+ t2("agenda.contextMenu.delete")
2538
2188
  ]
2539
2189
  }
2540
2190
  )
2541
2191
  ] }),
2542
2192
  config.capabilities.status && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "border-t px-4 py-3", children: [
2543
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-medium text-muted-foreground mb-2", children: t("agenda.contextMenu.changeStatus") }),
2193
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[11px] font-medium text-muted-foreground mb-2", children: t2("agenda.contextMenu.changeStatus") }),
2544
2194
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center gap-1.5", children: config.statuses.map((s) => {
2545
2195
  const available = isStatusAvailable(s, booking.startsAt);
2546
2196
  return /* @__PURE__ */ jsxRuntime.jsx(
2547
2197
  StatusDot,
2548
2198
  {
2549
2199
  color: s.color,
2550
- label: available ? s.label : `${s.label} ${t("agenda.contextMenu.notAvailable")}`,
2200
+ label: available ? s.label : `${s.label} ${t2("agenda.contextMenu.notAvailable")}`,
2551
2201
  isActive: booking.status === s.value,
2552
2202
  disabled: !available,
2553
2203
  onClick: () => {
@@ -2573,9 +2223,9 @@ function timeLabel(iso) {
2573
2223
  return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
2574
2224
  }
2575
2225
  function AgendaMobileList({ bookings, statusColors, currencyCode, onSelect }) {
2576
- const t = core.useTranslation();
2226
+ const t2 = core.useTranslation();
2577
2227
  const locale = core.getCurrentLocale() === "pt-BR" ? "pt-BR" : "en";
2578
- const groups = React12.useMemo(() => {
2228
+ const groups = React13.useMemo(() => {
2579
2229
  const todayStart = startOfDay(/* @__PURE__ */ new Date()).getTime();
2580
2230
  const sorted = bookings.filter((b) => !!b.startsAt).slice().sort((a, b) => a.startsAt < b.startsAt ? -1 : a.startsAt > b.startsAt ? 1 : 0);
2581
2231
  const map = /* @__PURE__ */ new Map();
@@ -2592,12 +2242,12 @@ function AgendaMobileList({ bookings, statusColors, currencyCode, onSelect }) {
2592
2242
  function dayLabel(date) {
2593
2243
  const today = startOfDay(/* @__PURE__ */ new Date());
2594
2244
  const diff = Math.round((startOfDay(date).getTime() - today.getTime()) / 864e5);
2595
- if (diff === 0) return t("agenda.list.today");
2596
- if (diff === 1) return t("agenda.list.tomorrow");
2245
+ if (diff === 0) return t2("agenda.list.today");
2246
+ if (diff === 1) return t2("agenda.list.tomorrow");
2597
2247
  return date.toLocaleDateString(locale, { weekday: "long", day: "numeric", month: "long" });
2598
2248
  }
2599
2249
  if (groups.length === 0) {
2600
- 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") }) });
2250
+ 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: t2("agenda.list.empty") }) });
2601
2251
  }
2602
2252
  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: [
2603
2253
  /* @__PURE__ */ jsxRuntime.jsx("h3", { className: "px-1 pb-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: dayLabel(g.date) }),
@@ -2653,8 +2303,8 @@ function generateAvailabilityEvents(schedules, professionals, dateRange, busines
2653
2303
  list.push(s);
2654
2304
  byAssignee.set(aid, list);
2655
2305
  }
2656
- function timeToMinutes(t) {
2657
- const [h, m] = t.split(":").map(Number);
2306
+ function timeToMinutes(t2) {
2307
+ const [h, m] = t2.split(":").map(Number);
2658
2308
  return h * 60 + m;
2659
2309
  }
2660
2310
  function minutesToTime(m) {
@@ -2766,8 +2416,8 @@ function viewForResources(view) {
2766
2416
  return view;
2767
2417
  }
2768
2418
  function useIsMobile() {
2769
- const [isMobile, setIsMobile] = React12.useState(() => typeof window !== "undefined" && window.matchMedia("(max-width: 767px)").matches);
2770
- React12.useEffect(() => {
2419
+ const [isMobile, setIsMobile] = React13.useState(() => typeof window !== "undefined" && window.matchMedia("(max-width: 767px)").matches);
2420
+ React13.useEffect(() => {
2771
2421
  if (typeof window === "undefined") return;
2772
2422
  const mq = window.matchMedia("(max-width: 767px)");
2773
2423
  const onChange = () => setIsMobile(mq.matches);
@@ -2779,10 +2429,10 @@ function useIsMobile() {
2779
2429
  function CalendarView() {
2780
2430
  injectCalendarStyles();
2781
2431
  const config = useAgendaConfig();
2782
- const t = core.useTranslation();
2432
+ const t2 = core.useTranslation();
2783
2433
  const currentLocale = core.getCurrentLocale();
2784
2434
  const fcLocale = currentLocale === "pt-BR" ? ptBrLocale__default.default : void 0;
2785
- const calendarRef = React12.useRef(null);
2435
+ const calendarRef = React13.useRef(null);
2786
2436
  const prefs = saas.usePluginPrefs("agenda", { calendarView: config.defaultCalendarView ?? "resourceTimeGridWeek" });
2787
2437
  const agendaSettings = useAgendaSettings();
2788
2438
  const effectiveBusinessHours = {
@@ -2811,8 +2461,8 @@ function CalendarView() {
2811
2461
  const isMobile = useIsMobile();
2812
2462
  const resourceCapable = professionals.length > 0;
2813
2463
  const viewOptions = resourceCapable ? RESOURCE_VIEW_OPTIONS : PLAIN_VIEW_OPTIONS;
2814
- const [mobileView, setMobileView] = React12.useState(MOBILE_LIST_VIEW);
2815
- const mobileViewOptions = React12.useMemo(
2464
+ const [mobileView, setMobileView] = React13.useState(MOBILE_LIST_VIEW);
2465
+ const mobileViewOptions = React13.useMemo(
2816
2466
  () => [
2817
2467
  ...viewOptions.filter((o) => o.key !== "listMonth"),
2818
2468
  { key: MOBILE_LIST_VIEW, labelKey: "agenda.calendar.viewList" }
@@ -2820,28 +2470,28 @@ function CalendarView() {
2820
2470
  [viewOptions]
2821
2471
  );
2822
2472
  const mobileGridActive = isMobile && mobileView !== MOBILE_LIST_VIEW;
2823
- const [selectedBooking, setSelectedBooking] = React12.useState(null);
2824
- const [popoverAnchor, setPopoverAnchor] = React12.useState(null);
2825
- const [popoverVisible, _setPopoverVisible] = React12.useState(false);
2826
- const popoverVisibleRef = React12.useRef(false);
2827
- const setPopoverVisible = React12.useCallback((v) => {
2473
+ const [selectedBooking, setSelectedBooking] = React13.useState(null);
2474
+ const [popoverAnchor, setPopoverAnchor] = React13.useState(null);
2475
+ const [popoverVisible, _setPopoverVisible] = React13.useState(false);
2476
+ const popoverVisibleRef = React13.useRef(false);
2477
+ const setPopoverVisible = React13.useCallback((v) => {
2828
2478
  popoverVisibleRef.current = v;
2829
2479
  _setPopoverVisible(v);
2830
2480
  }, []);
2831
- const [contextMenu, setContextMenu] = React12.useState(null);
2832
- const [contextMenuVisible, _setContextMenuVisible] = React12.useState(false);
2833
- const contextMenuVisibleRef = React12.useRef(false);
2834
- const setContextMenuVisible = React12.useCallback((v) => {
2481
+ const [contextMenu, setContextMenu] = React13.useState(null);
2482
+ const [contextMenuVisible, _setContextMenuVisible] = React13.useState(false);
2483
+ const contextMenuVisibleRef = React13.useRef(false);
2484
+ const setContextMenuVisible = React13.useCallback((v) => {
2835
2485
  contextMenuVisibleRef.current = v;
2836
2486
  _setContextMenuVisible(v);
2837
2487
  }, []);
2838
- const [scheduleConfirm, setScheduleConfirm] = React12.useState(null);
2839
- const [calendarTitle, setCalendarTitle] = React12.useState("");
2840
- const [selectedDate, setSelectedDate] = React12.useState(/* @__PURE__ */ new Date());
2841
- const [profSectionOpen, setProfSectionOpen] = React12.useState(true);
2842
- const [statusSectionOpen, setStatusSectionOpen] = React12.useState(false);
2843
- const [visibleRange, setVisibleRange] = React12.useState(null);
2844
- const openModal = React12.useCallback((...args) => {
2488
+ const [scheduleConfirm, setScheduleConfirm] = React13.useState(null);
2489
+ const [calendarTitle, setCalendarTitle] = React13.useState("");
2490
+ const [selectedDate, setSelectedDate] = React13.useState(/* @__PURE__ */ new Date());
2491
+ const [profSectionOpen, setProfSectionOpen] = React13.useState(true);
2492
+ const [statusSectionOpen, setStatusSectionOpen] = React13.useState(false);
2493
+ const [visibleRange, setVisibleRange] = React13.useState(null);
2494
+ const openModal = React13.useCallback((...args) => {
2845
2495
  setPopoverVisible(false);
2846
2496
  setSelectedBooking(null);
2847
2497
  setPopoverAnchor(null);
@@ -2849,17 +2499,17 @@ function CalendarView() {
2849
2499
  setContextMenu(null);
2850
2500
  _openModal(...args);
2851
2501
  }, [_openModal]);
2852
- const statusColors = React12.useMemo(() => {
2502
+ const statusColors = React13.useMemo(() => {
2853
2503
  const map = {};
2854
2504
  for (const s of config.statuses) map[s.value] = s.color;
2855
2505
  return map;
2856
2506
  }, [config.statuses]);
2857
- React12.useEffect(() => {
2507
+ React13.useEffect(() => {
2858
2508
  fetchProfessionals();
2859
2509
  fetchSchedules();
2860
2510
  if (config.modules.locationSelection) fetchLocations();
2861
2511
  }, []);
2862
- React12.useEffect(() => {
2512
+ React13.useEffect(() => {
2863
2513
  const api = calendarRef.current?.getApi();
2864
2514
  if (!api) return;
2865
2515
  if (!resourceCapable && currentView.startsWith("resource")) {
@@ -2868,7 +2518,7 @@ function CalendarView() {
2868
2518
  api.changeView(viewForResources(currentView));
2869
2519
  }
2870
2520
  }, [resourceCapable, currentView]);
2871
- React12.useEffect(() => {
2521
+ React13.useEffect(() => {
2872
2522
  if (!isMobile) return;
2873
2523
  const start = /* @__PURE__ */ new Date();
2874
2524
  start.setHours(0, 0, 0, 0);
@@ -2877,7 +2527,19 @@ function CalendarView() {
2877
2527
  end.setDate(end.getDate() + 91);
2878
2528
  fetchBookings({ start: start.toISOString(), end: end.toISOString() });
2879
2529
  }, [isMobile, fetchBookings]);
2880
- const resources = React12.useMemo(() => {
2530
+ core.useDataChanged({}, () => {
2531
+ if (visibleRange) {
2532
+ fetchBookings(visibleRange);
2533
+ return;
2534
+ }
2535
+ const start = /* @__PURE__ */ new Date();
2536
+ start.setHours(0, 0, 0, 0);
2537
+ start.setDate(start.getDate() - 1);
2538
+ const end = new Date(start);
2539
+ end.setDate(end.getDate() + 91);
2540
+ fetchBookings({ start: start.toISOString(), end: end.toISOString() });
2541
+ }, [visibleRange, fetchBookings]);
2542
+ const resources = React13.useMemo(() => {
2881
2543
  const filtered = selectedProfIds.length > 0 ? professionals.filter((p) => selectedProfIds.includes(p.id)) : professionals;
2882
2544
  return filtered.map((p) => ({
2883
2545
  id: p.id,
@@ -2885,31 +2547,31 @@ function CalendarView() {
2885
2547
  extendedProps: { avatarUrl: p.avatarUrl, locationName: p.locationName }
2886
2548
  }));
2887
2549
  }, [professionals, selectedProfIds]);
2888
- const bookingEvents = React12.useMemo(() => bookings.map((b) => bookingToEvent(b, statusColors)), [bookings, statusColors]);
2889
- const allLocations = React12.useMemo(() => {
2550
+ const bookingEvents = React13.useMemo(() => bookings.map((b) => bookingToEvent(b, statusColors)), [bookings, statusColors]);
2551
+ const allLocations = React13.useMemo(() => {
2890
2552
  const map = /* @__PURE__ */ new Map();
2891
2553
  for (const loc of config.locations) map.set(loc.id, loc.name);
2892
2554
  for (const loc of storeLocations) map.set(loc.id, loc.name);
2893
2555
  return map;
2894
2556
  }, [config.locations, storeLocations]);
2895
- const locationMap = React12.useMemo(() => {
2557
+ const locationMap = React13.useMemo(() => {
2896
2558
  if (!config.modules.locationSelection || allLocations.size === 0) return void 0;
2897
2559
  return allLocations;
2898
2560
  }, [config.modules.locationSelection, allLocations]);
2899
- const availabilityEvents = React12.useMemo(
2561
+ const availabilityEvents = React13.useMemo(
2900
2562
  () => generateAvailabilityEvents(schedules, professionals, visibleRange, effectiveBusinessHours, locationMap),
2901
2563
  [schedules, professionals, visibleRange, effectiveBusinessHours, locationMap]
2902
2564
  );
2903
- const events = React12.useMemo(() => [...bookingEvents, ...availabilityEvents], [bookingEvents, availabilityEvents]);
2565
+ const events = React13.useMemo(() => [...bookingEvents, ...availabilityEvents], [bookingEvents, availabilityEvents]);
2904
2566
  const storedView = prefs.get("calendarView");
2905
2567
  const effectiveInitialView = resourceCapable ? viewForResources(storedView) : viewForNoResources(storedView);
2906
- const handleDatesSet = React12.useCallback((arg) => {
2568
+ const handleDatesSet = React13.useCallback((arg) => {
2907
2569
  setCalendarTitle(arg.view.title);
2908
2570
  setView(arg.view.type);
2909
2571
  setVisibleRange({ start: arg.startStr, end: arg.endStr });
2910
2572
  fetchBookings({ start: arg.startStr, end: arg.endStr });
2911
2573
  }, [fetchBookings, setView]);
2912
- const handleEventDidMount = React12.useCallback((arg) => {
2574
+ const handleEventDidMount = React13.useCallback((arg) => {
2913
2575
  const booking = arg.event.extendedProps?.booking;
2914
2576
  if (booking) arg.el.setAttribute("data-booking-id", booking.id);
2915
2577
  arg.el.addEventListener("dblclick", (e) => {
@@ -2936,9 +2598,9 @@ function CalendarView() {
2936
2598
  setPopoverAnchor(null);
2937
2599
  });
2938
2600
  }, []);
2939
- const skipClickRef = React12.useRef(false);
2940
- const hoverTimerRef = React12.useRef(null);
2941
- const handleEventMouseEnter = React12.useCallback((arg) => {
2601
+ const skipClickRef = React13.useRef(false);
2602
+ const hoverTimerRef = React13.useRef(null);
2603
+ const handleEventMouseEnter = React13.useCallback((arg) => {
2942
2604
  if (arg.event.display === "background") return;
2943
2605
  if (arg.event.extendedProps?.isLocationLabel) return;
2944
2606
  const booking = arg.event.extendedProps.booking;
@@ -2957,13 +2619,13 @@ function CalendarView() {
2957
2619
  setPopoverVisible(true);
2958
2620
  }, 3e3);
2959
2621
  }, []);
2960
- const handleEventMouseLeave = React12.useCallback(() => {
2622
+ const handleEventMouseLeave = React13.useCallback(() => {
2961
2623
  if (hoverTimerRef.current) {
2962
2624
  clearTimeout(hoverTimerRef.current);
2963
2625
  hoverTimerRef.current = null;
2964
2626
  }
2965
2627
  }, []);
2966
- const handleEventClick = React12.useCallback((arg) => {
2628
+ const handleEventClick = React13.useCallback((arg) => {
2967
2629
  if (skipClickRef.current) {
2968
2630
  skipClickRef.current = false;
2969
2631
  return;
@@ -2986,7 +2648,7 @@ function CalendarView() {
2986
2648
  setPopoverAnchor({ x, y });
2987
2649
  setPopoverVisible(true);
2988
2650
  }, []);
2989
- const findScheduleForSlot = React12.useCallback((professionalId, dateTimeStr) => {
2651
+ const findScheduleForSlot = React13.useCallback((professionalId, dateTimeStr) => {
2990
2652
  if (!professionalId) return null;
2991
2653
  const dt = new Date(dateTimeStr);
2992
2654
  const dateStr = dt.toISOString().slice(0, 10);
@@ -3003,7 +2665,7 @@ function CalendarView() {
3003
2665
  }
3004
2666
  return null;
3005
2667
  }, [schedules]);
3006
- const handleSelect = React12.useCallback((arg) => {
2668
+ const handleSelect = React13.useCallback((arg) => {
3007
2669
  calendarRef.current?.getApi().unselect();
3008
2670
  if (popoverVisibleRef.current || contextMenuVisibleRef.current) {
3009
2671
  setPopoverVisible(false);
@@ -3025,7 +2687,7 @@ function CalendarView() {
3025
2687
  }
3026
2688
  proceed();
3027
2689
  }, [openModal, findScheduleForSlot, config.modules.workingHours]);
3028
- const handleEventDrop = React12.useCallback(async (arg) => {
2690
+ const handleEventDrop = React13.useCallback(async (arg) => {
3029
2691
  const booking = arg.event.extendedProps.booking;
3030
2692
  try {
3031
2693
  await rescheduleBooking(booking.id, arg.event.startStr, arg.event.endStr, arg.newResource?.id ?? void 0);
@@ -3033,19 +2695,19 @@ function CalendarView() {
3033
2695
  arg.revert();
3034
2696
  }
3035
2697
  }, [rescheduleBooking]);
3036
- const handleViewChange = React12.useCallback((v) => {
2698
+ const handleViewChange = React13.useCallback((v) => {
3037
2699
  calendarRef.current?.getApi().changeView(v);
3038
2700
  prefs.set("calendarView", v);
3039
2701
  }, [prefs]);
3040
- const handleMobileViewChange = React12.useCallback((v) => {
2702
+ const handleMobileViewChange = React13.useCallback((v) => {
3041
2703
  setMobileView(v);
3042
2704
  if (v === MOBILE_LIST_VIEW) return;
3043
2705
  setView(v);
3044
2706
  calendarRef.current?.getApi()?.changeView(v);
3045
2707
  }, [setView]);
3046
- const handlePrev = React12.useCallback(() => calendarRef.current?.getApi().prev(), []);
3047
- const handleNext = React12.useCallback(() => calendarRef.current?.getApi().next(), []);
3048
- const handleToday = React12.useCallback(() => {
2708
+ const handlePrev = React13.useCallback(() => calendarRef.current?.getApi().prev(), []);
2709
+ const handleNext = React13.useCallback(() => calendarRef.current?.getApi().next(), []);
2710
+ const handleToday = React13.useCallback(() => {
3049
2711
  calendarRef.current?.getApi().today();
3050
2712
  setSelectedDate(/* @__PURE__ */ new Date());
3051
2713
  }, []);
@@ -3061,9 +2723,9 @@ function CalendarView() {
3061
2723
  const next = selectedStatuses.includes(value) ? selectedStatuses.filter((s) => s !== value) : [...selectedStatuses, value];
3062
2724
  setFilters({ statuses: next });
3063
2725
  }
3064
- const wrapperRef = React12.useRef(null);
3065
- const [dayHeaderCompact, setDayHeaderCompact] = React12.useState(false);
3066
- React12.useEffect(() => {
2726
+ const wrapperRef = React13.useRef(null);
2727
+ const [dayHeaderCompact, setDayHeaderCompact] = React13.useState(false);
2728
+ React13.useEffect(() => {
3067
2729
  const container = wrapperRef.current;
3068
2730
  if (!container) return;
3069
2731
  let frame = 0;
@@ -3106,7 +2768,7 @@ function CalendarView() {
3106
2768
  ro.disconnect();
3107
2769
  };
3108
2770
  }, [resources, currentView, visibleRange]);
3109
- const applyResourceStriping = React12.useCallback(() => {
2771
+ const applyResourceStriping = React13.useCallback(() => {
3110
2772
  const container = wrapperRef.current;
3111
2773
  if (!container) return;
3112
2774
  const resourceIds = resources.map((r) => r.id);
@@ -3117,16 +2779,16 @@ function CalendarView() {
3117
2779
  el.classList.toggle("fc-resource-alt", idx % 2 === 1);
3118
2780
  });
3119
2781
  }, [resources]);
3120
- React12.useEffect(() => {
2782
+ React13.useEffect(() => {
3121
2783
  applyResourceStriping();
3122
2784
  const t1 = setTimeout(applyResourceStriping, 100);
3123
- const t2 = setTimeout(applyResourceStriping, 300);
2785
+ const t22 = setTimeout(applyResourceStriping, 300);
3124
2786
  return () => {
3125
2787
  clearTimeout(t1);
3126
- clearTimeout(t2);
2788
+ clearTimeout(t22);
3127
2789
  };
3128
2790
  }, [applyResourceStriping, currentView, visibleRange, bookings]);
3129
- React12.useEffect(() => {
2791
+ React13.useEffect(() => {
3130
2792
  function handleOpenBooking(e) {
3131
2793
  const detail = e.detail ?? {};
3132
2794
  const externalSource = detail.source ? { type: String(detail.source), id: detail.waitlistId, waitlistId: detail.waitlistId } : void 0;
@@ -3147,7 +2809,7 @@ function CalendarView() {
3147
2809
  window.addEventListener("agenda:open-booking", handleOpenBooking);
3148
2810
  return () => window.removeEventListener("agenda:open-booking", handleOpenBooking);
3149
2811
  }, [openModal]);
3150
- const dayHeaderContent = React12.useCallback((arg) => {
2812
+ const dayHeaderContent = React13.useCallback((arg) => {
3151
2813
  const date = arg.date;
3152
2814
  const dateLocale = currentLocale === "pt-BR" ? "pt-BR" : "en";
3153
2815
  const wrapStyle = { display: "flex", flexDirection: "column", alignItems: "center", lineHeight: 1.15 };
@@ -3165,7 +2827,7 @@ function CalendarView() {
3165
2827
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: dateStyle, children: `${date.getMonth() + 1}/${date.getDate()}` })
3166
2828
  ] });
3167
2829
  }, [dayHeaderCompact, currentLocale]);
3168
- const resourceLabelContent = React12.useCallback((arg) => {
2830
+ const resourceLabelContent = React13.useCallback((arg) => {
3169
2831
  const { id, title, extendedProps } = arg.resource;
3170
2832
  const initials = title.split(" ").map((n) => n[0]).join("").slice(0, 2);
3171
2833
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 px-2 py-1.5", children: [
@@ -3185,7 +2847,7 @@ function CalendarView() {
3185
2847
  ] })
3186
2848
  ] });
3187
2849
  }, []);
3188
- const eventContent = React12.useCallback((arg) => {
2850
+ const eventContent = React13.useCallback((arg) => {
3189
2851
  if (arg.event.extendedProps?.isLocationLabel) {
3190
2852
  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 }) });
3191
2853
  }
@@ -3211,7 +2873,7 @@ function CalendarView() {
3211
2873
  className: "flex items-center gap-2 rounded-button bg-primary text-primary-foreground border border-primary pl-3 pr-4 py-1.5 text-sm font-semibold hover:bg-primary/90 shadow-button-primary active:shadow-button-inset transition-all mb-6 w-fit",
3212
2874
  children: [
3213
2875
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-4 w-4" }),
3214
- t("agenda.sidebar.create")
2876
+ t2("agenda.sidebar.create")
3215
2877
  ]
3216
2878
  }
3217
2879
  ),
@@ -3286,7 +2948,7 @@ function CalendarView() {
3286
2948
  {
3287
2949
  onClick: handleToday,
3288
2950
  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",
3289
- children: t("agenda.toolbar.today")
2951
+ children: t2("agenda.toolbar.today")
3290
2952
  }
3291
2953
  ),
3292
2954
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-0.5", children: [
@@ -3294,7 +2956,7 @@ function CalendarView() {
3294
2956
  /* @__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" }) })
3295
2957
  ] })
3296
2958
  ] }),
3297
- /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-base sm:text-lg font-normal truncate", children: isMobile && !mobileGridActive ? t("agenda.calendar.viewAgenda") : calendarTitle })
2959
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { className: "text-base sm:text-lg font-normal truncate", children: isMobile && !mobileGridActive ? t2("agenda.calendar.viewAgenda") : calendarTitle })
3298
2960
  ] }),
3299
2961
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
3300
2962
  /* @__PURE__ */ jsxRuntime.jsx("div", { className: "hidden md:flex items-center gap-1", children: viewOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(
@@ -3302,7 +2964,7 @@ function CalendarView() {
3302
2964
  {
3303
2965
  onClick: () => handleViewChange(opt.key),
3304
2966
  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"}`,
3305
- children: t(opt.labelKey)
2967
+ children: t2(opt.labelKey)
3306
2968
  },
3307
2969
  opt.key
3308
2970
  )) }),
@@ -3324,7 +2986,7 @@ function CalendarView() {
3324
2986
  {
3325
2987
  onClick: () => handleMobileViewChange(opt.key),
3326
2988
  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"}`,
3327
- children: t(opt.labelKey)
2989
+ children: t2(opt.labelKey)
3328
2990
  },
3329
2991
  opt.key
3330
2992
  )) })
@@ -3415,9 +3077,9 @@ function CalendarView() {
3415
3077
  {
3416
3078
  open: !!scheduleConfirm,
3417
3079
  variant: "warning",
3418
- title: t("agenda.calendar.noScheduleConfirm"),
3419
- confirmLabel: t("common.confirm"),
3420
- cancelLabel: t("common.cancel"),
3080
+ title: t2("agenda.calendar.noScheduleConfirm"),
3081
+ confirmLabel: t2("common.confirm"),
3082
+ cancelLabel: t2("common.cancel"),
3421
3083
  onConfirm: () => {
3422
3084
  const cb = scheduleConfirm?.onConfirm;
3423
3085
  setScheduleConfirm(null);
@@ -3464,17 +3126,26 @@ function CalendarView() {
3464
3126
  )
3465
3127
  ] });
3466
3128
  }
3129
+
3130
+ // src/lib/tenant.ts
3131
+ var t;
3132
+ function setAgendaTenantId(id) {
3133
+ t = id;
3134
+ }
3135
+ function getAgendaTenantId() {
3136
+ return t;
3137
+ }
3467
3138
  function AgendaPage({ config, provider: provider2, store }) {
3468
3139
  const runtime = core.usePluginRuntimeOptional();
3469
3140
  const tenantId = runtime?.context.tenant?.id;
3470
- React12__default.default.useEffect(() => {
3471
- chunkSYDBDI3Q_cjs.setAgendaTenantId(tenantId);
3472
- return () => chunkSYDBDI3Q_cjs.setAgendaTenantId(void 0);
3141
+ React13__default.default.useEffect(() => {
3142
+ setAgendaTenantId(tenantId);
3143
+ return () => setAgendaTenantId(void 0);
3473
3144
  }, [tenantId]);
3474
3145
  return /* @__PURE__ */ jsxRuntime.jsx(AgendaContextProvider, { config, provider: provider2, store, children: /* @__PURE__ */ jsxRuntime.jsx(CalendarView, {}) });
3475
3146
  }
3476
3147
  function getTenantId() {
3477
- return chunkSYDBDI3Q_cjs.getAgendaTenantId() ?? core.getActiveTenantId();
3148
+ return getAgendaTenantId() ?? core.getActiveTenantId();
3478
3149
  }
3479
3150
  function snakeToCamel(obj) {
3480
3151
  const result = {};
@@ -3582,8 +3253,10 @@ async function hydrateCanonicalBookings(core, bookings) {
3582
3253
  locationId: booking.location_id,
3583
3254
  metadata: booking.metadata ?? {},
3584
3255
  clientId: booking.party_id,
3585
- // Simple events (no party) fall back to the title stored in metadata.
3586
- clientName: client?.name ?? booking.metadata?.title ?? null,
3256
+ // Simple events (no party) fall back to the title stored in metadata;
3257
+ // partyless blocks/tasks (e.g. imported Google events) get their notes or
3258
+ // a kind label so the calendar never renders an unnamed chip.
3259
+ clientName: client?.name ?? booking.metadata?.title ?? (booking.kind === "appointment" ? null : booking.notes || (booking.kind === "task" ? "Tarefa" : "Bloqueio")),
3587
3260
  clientPhone: client?.phone ?? null,
3588
3261
  clientEmail: client?.email ?? null,
3589
3262
  clientAvatarUrl: client?.avatar_url ?? null,
@@ -3623,7 +3296,8 @@ async function getCanonicalBookingById(core, id) {
3623
3296
  const [booking] = await hydrateCanonicalBookings(core, [data]);
3624
3297
  return booking ?? null;
3625
3298
  }
3626
- function createSupabaseAgendaProvider() {
3299
+ function createSupabaseAgendaProvider(providerOptions) {
3300
+ const professionalKind = providerOptions?.professionalKind ?? "staff";
3627
3301
  return {
3628
3302
  // v_appointments is in public schema (PostgREST default)
3629
3303
  async getBookings(query) {
@@ -3644,7 +3318,26 @@ function createSupabaseAgendaProvider() {
3644
3318
  const { core, pub } = getClients();
3645
3319
  const { data, error } = await pub.from("v_appointments").select("*").eq("id", id).maybeSingle();
3646
3320
  if (error) throw error;
3647
- if (data) return mapBooking(data);
3321
+ if (data) {
3322
+ const booking = mapBooking(data);
3323
+ if (booking.orderId) {
3324
+ const { data: items } = await core.from("order_items").select("id, service_id, name, duration_minutes, unit_price, assignee_id, sort_order").eq("order_id", booking.orderId).order("sort_order", { ascending: true });
3325
+ if (items && items.length) {
3326
+ const services = items.map((item) => ({
3327
+ id: item.id,
3328
+ serviceId: item.service_id,
3329
+ name: item.name,
3330
+ durationMinutes: item.duration_minutes,
3331
+ price: item.unit_price,
3332
+ assigneeId: item.assignee_id
3333
+ }));
3334
+ booking.services = services;
3335
+ const itemDuration = services.reduce((sum, s) => sum + (s.durationMinutes ?? 0), 0);
3336
+ if (itemDuration) booking.totalDurationMinutes = itemDuration;
3337
+ }
3338
+ }
3339
+ return booking;
3340
+ }
3648
3341
  return getCanonicalBookingById(core, id);
3649
3342
  },
3650
3343
  async createBooking(input) {
@@ -3667,7 +3360,7 @@ function createSupabaseAgendaProvider() {
3667
3360
  kind: input.kind ?? "appointment",
3668
3361
  status: "scheduled",
3669
3362
  party_id: input.clientId || null,
3670
- assignee_id: input.professionalId,
3363
+ assignee_id: input.professionalId || null,
3671
3364
  location_id: input.locationId ?? null,
3672
3365
  subtotal: totalPrice,
3673
3366
  total: totalPrice,
@@ -3684,7 +3377,7 @@ function createSupabaseAgendaProvider() {
3684
3377
  tenant_id: tenantId,
3685
3378
  kind: input.kind ?? "appointment",
3686
3379
  party_id: input.clientId || null,
3687
- assignee_id: input.professionalId,
3380
+ assignee_id: input.professionalId || null,
3688
3381
  location_id: input.locationId ?? null,
3689
3382
  order_id: order.id,
3690
3383
  starts_at: input.startsAt,
@@ -3709,7 +3402,7 @@ function createSupabaseAgendaProvider() {
3709
3402
  total: s.price,
3710
3403
  sort_order: i,
3711
3404
  duration_minutes: s.durationMinutes,
3712
- assignee_id: s.assigneeId ?? input.professionalId
3405
+ assignee_id: s.assigneeId || input.professionalId || null
3713
3406
  }));
3714
3407
  const { error: oiErr } = await core.from("order_items").insert(orderItems);
3715
3408
  if (oiErr) throw oiErr;
@@ -3789,9 +3482,12 @@ function createSupabaseAgendaProvider() {
3789
3482
  const { data: booking, error: bookingErr } = await core.from("appointments").select("order_id").eq("id", id).single();
3790
3483
  if (bookingErr) throw bookingErr;
3791
3484
  const orderId = booking?.order_id;
3792
- const { count, error: movementErr } = orderId ? await pub.from("plg_financial_movements").select("id", { count: "exact", head: true }).eq("invoice_id", orderId) : { count: 0, error: null };
3793
- if (movementErr) throw movementErr;
3794
- if (orderId && (count ?? 0) > 0) {
3485
+ let movementCount = 0;
3486
+ if (orderId) {
3487
+ const { count, error: movementErr } = await pub.from("plg_financial_movements").select("id", { count: "exact", head: true }).eq("invoice_id", orderId);
3488
+ if (!movementErr) movementCount = count ?? 0;
3489
+ }
3490
+ if (orderId && movementCount > 0) {
3795
3491
  await pub.from("plg_financial_movements").update({ status: "cancelled" }).eq("invoice_id", orderId);
3796
3492
  await core.from("orders").update({ status: "cancelled" }).eq("id", orderId);
3797
3493
  const { error } = await core.from("appointments").update({ status: "cancelled" }).eq("id", id);
@@ -3868,7 +3564,7 @@ function createSupabaseAgendaProvider() {
3868
3564
  },
3869
3565
  async getProfessionals() {
3870
3566
  const { core } = getClients();
3871
- const { data, error } = await core.from("people").select("id, name, avatar_url, is_active").eq("kind", "staff").eq("is_active", true).order("name");
3567
+ const { data, error } = await core.from("people").select("id, name, avatar_url, is_active").eq("kind", professionalKind).eq("is_active", true).order("name");
3872
3568
  if (error) throw error;
3873
3569
  return (data ?? []).map((p) => ({
3874
3570
  id: p.id,
@@ -4150,40 +3846,49 @@ var HOUR_OPTIONS = Array.from({ length: 18 }, (_, i) => {
4150
3846
  const h = (5 + i).toString().padStart(2, "0");
4151
3847
  return { value: `${h}:00`, label: `${h}:00` };
4152
3848
  });
3849
+ var AGENDA_TOGGLE_DEFAULTS = {
3850
+ conflictDetection: true,
3851
+ dragDrop: true,
3852
+ autoCreateOrder: true,
3853
+ enableLocation: false,
3854
+ enableWorkingHours: true,
3855
+ blockOutside: false,
3856
+ enableConfirmations: true,
3857
+ autoConfirm: false,
3858
+ reminder: true,
3859
+ showCancelled: false,
3860
+ showNoShow: true,
3861
+ compact: false,
3862
+ newBookingNotif: true,
3863
+ cancelAlerts: true,
3864
+ noShowTracking: false
3865
+ };
4153
3866
  function AgendaGeneralSettings() {
4154
- const t = core.useTranslation();
3867
+ const t2 = core.useTranslation();
4155
3868
  const { startTime, endTime, setStartTime, setEndTime } = useAgendaSettings();
3869
+ const s = saas.useTenantPluginSettings("agenda", AGENDA_TOGGLE_DEFAULTS);
4156
3870
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
4157
- /* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t("agenda.settings.scheduling"), description: t("agenda.settings.schedulingDesc"), children: [
4158
- /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t("agenda.settings.conflictDetection"), description: t("agenda.settings.conflictDetectionDesc"), checked: true, onChange: () => {
4159
- } }),
4160
- /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t("agenda.settings.dragDrop"), description: t("agenda.settings.dragDropDesc"), checked: true, onChange: () => {
4161
- } }),
4162
- /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t("agenda.settings.autoCreateOrder"), description: t("agenda.settings.autoCreateOrderDesc"), checked: true, onChange: () => {
4163
- } })
3871
+ /* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t2("agenda.settings.scheduling"), description: t2("agenda.settings.schedulingDesc"), children: [
3872
+ /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("agenda.settings.conflictDetection"), description: t2("agenda.settings.conflictDetectionDesc"), checked: s.get("conflictDetection"), onChange: (v) => s.set("conflictDetection", v) }),
3873
+ /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("agenda.settings.dragDrop"), description: t2("agenda.settings.dragDropDesc"), checked: s.get("dragDrop"), onChange: (v) => s.set("dragDrop", v) }),
3874
+ /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("agenda.settings.autoCreateOrder"), description: t2("agenda.settings.autoCreateOrderDesc"), checked: s.get("autoCreateOrder"), onChange: (v) => s.set("autoCreateOrder", v) })
4164
3875
  ] }),
4165
- /* @__PURE__ */ jsxRuntime.jsx(saas.SettingsGroup, { title: t("agenda.settings.locations"), description: t("agenda.settings.locationsDesc"), children: /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t("agenda.settings.enableLocation"), description: t("agenda.settings.enableLocationDesc"), checked: false, onChange: () => {
4166
- } }) }),
4167
- /* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t("agenda.settings.workingHours"), description: t("agenda.settings.workingHoursDesc"), children: [
4168
- /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t("agenda.settings.enableWorkingHours"), description: t("agenda.settings.enableWorkingHoursDesc"), checked: true, onChange: () => {
4169
- } }),
4170
- /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t("agenda.settings.blockOutside"), description: t("agenda.settings.blockOutsideDesc"), checked: false, onChange: () => {
4171
- } })
3876
+ /* @__PURE__ */ jsxRuntime.jsx(saas.SettingsGroup, { title: t2("agenda.settings.locations"), description: t2("agenda.settings.locationsDesc"), children: /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("agenda.settings.enableLocation"), description: t2("agenda.settings.enableLocationDesc"), checked: s.get("enableLocation"), onChange: (v) => s.set("enableLocation", v) }) }),
3877
+ /* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t2("agenda.settings.workingHours"), description: t2("agenda.settings.workingHoursDesc"), children: [
3878
+ /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("agenda.settings.enableWorkingHours"), description: t2("agenda.settings.enableWorkingHoursDesc"), checked: s.get("enableWorkingHours"), onChange: (v) => s.set("enableWorkingHours", v) }),
3879
+ /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("agenda.settings.blockOutside"), description: t2("agenda.settings.blockOutsideDesc"), checked: s.get("blockOutside"), onChange: (v) => s.set("blockOutside", v) })
4172
3880
  ] }),
4173
- /* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t("agenda.settings.confirmations"), description: t("agenda.settings.confirmationsDesc"), children: [
4174
- /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t("agenda.settings.enableConfirmations"), description: t("agenda.settings.enableConfirmationsDesc"), checked: true, onChange: () => {
4175
- } }),
4176
- /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t("agenda.settings.autoConfirm"), description: t("agenda.settings.autoConfirmDesc"), checked: false, onChange: () => {
4177
- } }),
4178
- /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t("agenda.settings.reminder"), description: t("agenda.settings.reminderDesc"), checked: true, onChange: () => {
4179
- } })
3881
+ /* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t2("agenda.settings.confirmations"), description: t2("agenda.settings.confirmationsDesc"), children: [
3882
+ /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("agenda.settings.enableConfirmations"), description: t2("agenda.settings.enableConfirmationsDesc"), checked: s.get("enableConfirmations"), onChange: (v) => s.set("enableConfirmations", v) }),
3883
+ /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("agenda.settings.autoConfirm"), description: t2("agenda.settings.autoConfirmDesc"), checked: s.get("autoConfirm"), onChange: (v) => s.set("autoConfirm", v) }),
3884
+ /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("agenda.settings.reminder"), description: t2("agenda.settings.reminderDesc"), checked: s.get("reminder"), onChange: (v) => s.set("reminder", v) })
4180
3885
  ] }),
4181
- /* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t("agenda.settings.calendarDisplay"), description: t("agenda.settings.calendarDisplayDesc"), children: [
3886
+ /* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t2("agenda.settings.calendarDisplay"), description: t2("agenda.settings.calendarDisplayDesc"), children: [
4182
3887
  /* @__PURE__ */ jsxRuntime.jsx(
4183
3888
  saas.SelectRow,
4184
3889
  {
4185
- label: t("agenda.settings.dayStartTime"),
4186
- description: t("agenda.settings.dayStartTimeDesc"),
3890
+ label: t2("agenda.settings.dayStartTime"),
3891
+ description: t2("agenda.settings.dayStartTimeDesc"),
4187
3892
  value: startTime,
4188
3893
  options: HOUR_OPTIONS,
4189
3894
  onChange: setStartTime
@@ -4192,27 +3897,21 @@ function AgendaGeneralSettings() {
4192
3897
  /* @__PURE__ */ jsxRuntime.jsx(
4193
3898
  saas.SelectRow,
4194
3899
  {
4195
- label: t("agenda.settings.dayEndTime"),
4196
- description: t("agenda.settings.dayEndTimeDesc"),
3900
+ label: t2("agenda.settings.dayEndTime"),
3901
+ description: t2("agenda.settings.dayEndTimeDesc"),
4197
3902
  value: endTime,
4198
3903
  options: HOUR_OPTIONS,
4199
3904
  onChange: setEndTime
4200
3905
  }
4201
3906
  ),
4202
- /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t("agenda.settings.showCancelled"), description: t("agenda.settings.showCancelledDesc"), checked: false, onChange: () => {
4203
- } }),
4204
- /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t("agenda.settings.showNoShow"), description: t("agenda.settings.showNoShowDesc"), checked: true, onChange: () => {
4205
- } }),
4206
- /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t("agenda.settings.compact"), description: t("agenda.settings.compactDesc"), checked: false, onChange: () => {
4207
- } })
3907
+ /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("agenda.settings.showCancelled"), description: t2("agenda.settings.showCancelledDesc"), checked: s.get("showCancelled"), onChange: (v) => s.set("showCancelled", v) }),
3908
+ /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("agenda.settings.showNoShow"), description: t2("agenda.settings.showNoShowDesc"), checked: s.get("showNoShow"), onChange: (v) => s.set("showNoShow", v) }),
3909
+ /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("agenda.settings.compact"), description: t2("agenda.settings.compactDesc"), checked: s.get("compact"), onChange: (v) => s.set("compact", v) })
4208
3910
  ] }),
4209
- /* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t("agenda.settings.notifications"), description: t("agenda.settings.notificationsDesc"), children: [
4210
- /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t("agenda.settings.newBookingNotif"), description: t("agenda.settings.newBookingNotifDesc"), checked: true, onChange: () => {
4211
- } }),
4212
- /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t("agenda.settings.cancelAlerts"), description: t("agenda.settings.cancelAlertsDesc"), checked: true, onChange: () => {
4213
- } }),
4214
- /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t("agenda.settings.noShowTracking"), description: t("agenda.settings.noShowTrackingDesc"), checked: false, onChange: () => {
4215
- } })
3911
+ /* @__PURE__ */ jsxRuntime.jsxs(saas.SettingsGroup, { title: t2("agenda.settings.notifications"), description: t2("agenda.settings.notificationsDesc"), children: [
3912
+ /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("agenda.settings.newBookingNotif"), description: t2("agenda.settings.newBookingNotifDesc"), checked: s.get("newBookingNotif"), onChange: (v) => s.set("newBookingNotif", v) }),
3913
+ /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("agenda.settings.cancelAlerts"), description: t2("agenda.settings.cancelAlertsDesc"), checked: s.get("cancelAlerts"), onChange: (v) => s.set("cancelAlerts", v) }),
3914
+ /* @__PURE__ */ jsxRuntime.jsx(saas.ToggleRow, { label: t2("agenda.settings.noShowTracking"), description: t2("agenda.settings.noShowTrackingDesc"), checked: s.get("noShowTracking"), onChange: (v) => s.set("noShowTracking", v) })
4216
3915
  ] })
4217
3916
  ] });
4218
3917
  }
@@ -4248,6 +3947,7 @@ var en = {
4248
3947
  "agenda.appointment.searchService": "Search service to add...",
4249
3948
  "agenda.appointment.selectLocation": "Select location...",
4250
3949
  "agenda.appointment.selectProfessional": "Select professional...",
3950
+ "agenda.appointment.selectClientHint": "Select the client from the list or use New client to enable saving.",
4251
3951
  "agenda.appointment.services": "Services",
4252
3952
  "agenda.appointment.status": "Status",
4253
3953
  "agenda.appointment.to": "to",
@@ -4468,6 +4168,7 @@ var ptBR = {
4468
4168
  "agenda.appointment.searchService": "Buscar servi\xE7o para adicionar...",
4469
4169
  "agenda.appointment.selectLocation": "Selecionar local...",
4470
4170
  "agenda.appointment.selectProfessional": "Selecionar profissional...",
4171
+ "agenda.appointment.selectClientHint": "Selecione o cliente na lista ou use Novo cliente para habilitar o salvamento.",
4471
4172
  "agenda.appointment.services": "Servi\xE7os",
4472
4173
  "agenda.appointment.status": "Status",
4473
4174
  "agenda.appointment.to": "at\xE9",
@@ -4710,7 +4411,10 @@ CREATE INDEX IF NOT EXISTS appointments_assignee_time
4710
4411
  WHERE status NOT IN ('cancelled', 'no_show');
4711
4412
 
4712
4413
  -- \xA72 \u2014 admin read model (security_invoker \u2192 underlying RLS applies; anon sees 0 rows)
4713
- CREATE OR REPLACE VIEW public.v_appointments
4414
+ -- DROP first: 013 widens this view and CREATE OR REPLACE cannot narrow a live
4415
+ -- view on replay ("cannot drop columns from view"). 013 replays right after.
4416
+ DROP VIEW IF EXISTS public.v_appointments;
4417
+ CREATE VIEW public.v_appointments
4714
4418
  WITH (security_invoker = true) AS
4715
4419
  SELECT
4716
4420
  b.id, b.tenant_id, b.kind, b.starts_at, b.ends_at, b.status, b.notes,
@@ -4738,7 +4442,9 @@ GRANT SELECT ON public.v_appointments TO authenticated, service_role;
4738
4442
  REVOKE SELECT ON public.v_appointments FROM anon;
4739
4443
 
4740
4444
  -- \xA73 \u2014 anon-readable public catalog (owner-rights view = deliberate, whitelisted)
4741
- CREATE OR REPLACE VIEW public.v_public_services AS
4445
+ -- DROP first: 010 widens this view (curation columns) \u2014 same replay rule as \xA72.
4446
+ DROP VIEW IF EXISTS public.v_public_services;
4447
+ CREATE VIEW public.v_public_services AS
4742
4448
  SELECT
4743
4449
  s.id, s.tenant_id, s.name, s.description, s.price, s.currency,
4744
4450
  COALESCE(s.duration_minutes, 30) AS duration_minutes,
@@ -5022,7 +4728,7 @@ function createFinancialBridge(provider2) {
5022
4728
  let typeNames = {};
5023
4729
  if (typeIds.length > 0) {
5024
4730
  const { data: types } = await supabase.from("plg_financial_payment_method_types").select("id, name").in("id", typeIds);
5025
- typeNames = Object.fromEntries((types ?? []).map((t) => [t.id, t.name]));
4731
+ typeNames = Object.fromEntries((types ?? []).map((t2) => [t2.id, t2.name]));
5026
4732
  }
5027
4733
  const movements = (movRes.data ?? []).map((m) => ({
5028
4734
  id: String(m.id),
@@ -5123,7 +4829,7 @@ function createFinancialBridge(provider2) {
5123
4829
  },
5124
4830
  async getPaymentMethodTypes() {
5125
4831
  const types = await provider2.getPaymentMethodTypes();
5126
- return types.map((t) => ({ id: t.id, name: t.name, transactionType: t.transactionType }));
4832
+ return types.map((t2) => ({ id: t2.id, name: t2.name, transactionType: t2.transactionType }));
5127
4833
  },
5128
4834
  async getBankAccounts() {
5129
4835
  const accounts = await provider2.getBankAccounts();
@@ -5165,6 +4871,65 @@ function mapInvoiceStatus(status) {
5165
4871
  return "pending";
5166
4872
  }
5167
4873
  }
4874
+
4875
+ // src/integrations/google-calendar/mapping.ts
4876
+ function bookingSummary(b) {
4877
+ const services = (b.services ?? []).map((s) => s.name).filter(Boolean).join(", ");
4878
+ const who = b.clientName ?? "";
4879
+ if (services && who) return `${services} \u2014 ${who}`;
4880
+ return services || who || "Appointment";
4881
+ }
4882
+ function bookingToEvent2(b, timeZone) {
4883
+ if (!b.startsAt || !b.endsAt) return null;
4884
+ const notes = [b.notes, b.professionalName ? `Pro: ${b.professionalName}` : "", b.locationName ?? ""].filter(Boolean).join("\n");
4885
+ return {
4886
+ summary: bookingSummary(b),
4887
+ description: notes || void 0,
4888
+ start: { dateTime: b.startsAt, timeZone },
4889
+ end: { dateTime: b.endsAt, timeZone }
4890
+ };
4891
+ }
4892
+ function eventToBookingPatch(ev) {
4893
+ if (ev.status === "cancelled") return { status: "cancelled" };
4894
+ const t2 = eventTimes(ev);
4895
+ return { startsAt: t2.startsAt, endsAt: t2.endsAt };
4896
+ }
4897
+ function eventTimes(ev) {
4898
+ const allDay = !ev.start?.dateTime && !!ev.start?.date;
4899
+ const startsAt = ev.start?.dateTime ?? ev.start?.date ?? "";
4900
+ const endsAt = ev.end?.dateTime ?? ev.end?.date ?? startsAt;
4901
+ return { startsAt, endsAt, allDay };
4902
+ }
4903
+ function channelRowToChannel(r) {
4904
+ return {
4905
+ id: r.id,
4906
+ integrationId: r.integration_id,
4907
+ tenantId: r.tenant_id,
4908
+ googleCalendarId: r.google_calendar_id,
4909
+ summary: r.summary ?? null,
4910
+ color: r.color ?? null,
4911
+ direction: r.direction,
4912
+ targetKind: r.target_kind ?? null,
4913
+ targetId: r.target_id ?? null,
4914
+ importMode: r.import_mode ?? "appointment",
4915
+ syncToken: r.sync_token ?? null,
4916
+ channelId: r.channel_id ?? null,
4917
+ resourceId: r.resource_id ?? null,
4918
+ channelExpiresAt: r.channel_expires_at ?? null,
4919
+ isActive: r.is_active ?? true,
4920
+ createdAt: r.created_at ?? "",
4921
+ updatedAt: r.updated_at ?? ""
4922
+ };
4923
+ }
4924
+
4925
+ // src/integrations/google-calendar/data/tables.ts
4926
+ var GCAL_TABLES = {
4927
+ integrations: "plg_calendar_integrations",
4928
+ channels: "plg_calendar_channels",
4929
+ syncLog: "plg_calendar_sync_log"
4930
+ };
4931
+
4932
+ // src/integrations/google-calendar/data/supabase.ts
5168
4933
  var FUNCTION = "google-calendar-sync";
5169
4934
  function sb() {
5170
4935
  const supabase = core.getSupabaseClientOptional();
@@ -5174,7 +4939,7 @@ function sb() {
5174
4939
  function createGoogleCalendarProvider() {
5175
4940
  return {
5176
4941
  async getIntegration() {
5177
- const { data } = await sb().from("calendar_integrations").select("*").eq("provider", "google").limit(1).maybeSingle();
4942
+ const { data } = await sb().from(GCAL_TABLES.integrations).select("*").eq("provider", "google").limit(1).maybeSingle();
5178
4943
  if (!data) return null;
5179
4944
  return {
5180
4945
  id: data.id,
@@ -5187,24 +4952,24 @@ function createGoogleCalendarProvider() {
5187
4952
  },
5188
4953
  async getConnectUrl(redirectTo) {
5189
4954
  const { data, error } = await sb().functions.invoke(FUNCTION, {
5190
- body: { action: "oauth_start", redirectTo: redirectTo ?? window.location.href }
4955
+ body: { action: "oauth_start", tenantId: core.getActiveTenantId(), redirectTo: redirectTo ?? window.location.href }
5191
4956
  });
5192
4957
  if (error) throw new Error(error.message);
5193
4958
  return data.url;
5194
4959
  },
5195
4960
  async setCalendar(calendarId) {
5196
- await sb().from("calendar_integrations").update({ calendar_id: calendarId, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("provider", "google");
4961
+ await sb().from(GCAL_TABLES.integrations).update({ calendar_id: calendarId, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("provider", "google");
5197
4962
  },
5198
4963
  async disconnect() {
5199
- await sb().from("calendar_integrations").update({ active: false, oauth_refresh_token: null, oauth_access_token: null }).eq("provider", "google");
4964
+ await sb().from(GCAL_TABLES.integrations).update({ active: false, oauth_refresh_token: null, oauth_access_token: null }).eq("provider", "google");
5200
4965
  },
5201
4966
  async syncNow() {
5202
- const { data, error } = await sb().functions.invoke(FUNCTION, { body: { action: "pull_events", trigger: "manual" } });
4967
+ const { data, error } = await sb().functions.invoke(FUNCTION, { body: { action: "pull_events", tenantId: core.getActiveTenantId(), trigger: "manual" } });
5203
4968
  if (error) throw new Error(error.message);
5204
4969
  return data;
5205
4970
  },
5206
4971
  async getSyncLog() {
5207
- const { data } = await sb().from("calendar_sync_log").select("*").order("created_at", { ascending: false }).limit(20);
4972
+ const { data } = await sb().from(GCAL_TABLES.syncLog).select("*").order("created_at", { ascending: false }).limit(20);
5208
4973
  return (data ?? []).map((r) => ({
5209
4974
  id: r.id,
5210
4975
  direction: r.direction,
@@ -5215,32 +4980,523 @@ function createGoogleCalendarProvider() {
5215
4980
  error: r.error ?? void 0,
5216
4981
  createdAt: r.created_at
5217
4982
  }));
4983
+ },
4984
+ async listGoogleCalendars() {
4985
+ const { data, error } = await sb().functions.invoke(FUNCTION, { body: { action: "list_calendars", tenantId: core.getActiveTenantId() } });
4986
+ if (error) throw new Error(error.message);
4987
+ return data.calendars ?? [];
4988
+ },
4989
+ async listChannels() {
4990
+ const { data } = await sb().from(GCAL_TABLES.channels).select("*").order("created_at", { ascending: true });
4991
+ return (data ?? []).map(channelRowToChannel);
4992
+ },
4993
+ async saveChannel(input) {
4994
+ const tenantId = core.getActiveTenantId();
4995
+ let integrationId = input.integrationId;
4996
+ if (!integrationId) {
4997
+ const { data } = await sb().from(GCAL_TABLES.integrations).select("id").eq("provider", "google").limit(1).maybeSingle();
4998
+ integrationId = data?.id;
4999
+ }
5000
+ const row = {
5001
+ integration_id: integrationId,
5002
+ tenant_id: tenantId,
5003
+ google_calendar_id: input.googleCalendarId,
5004
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
5005
+ };
5006
+ if (input.id !== void 0) row.id = input.id;
5007
+ if (input.summary !== void 0) row.summary = input.summary;
5008
+ if (input.color !== void 0) row.color = input.color;
5009
+ if (input.direction !== void 0) row.direction = input.direction;
5010
+ if (input.targetKind !== void 0) row.target_kind = input.targetKind;
5011
+ if (input.targetId !== void 0) row.target_id = input.targetId;
5012
+ if (input.importMode !== void 0) row.import_mode = input.importMode;
5013
+ if (input.isActive !== void 0) row.is_active = input.isActive;
5014
+ const { error } = await sb().from(GCAL_TABLES.channels).upsert(row, { onConflict: "integration_id,google_calendar_id" });
5015
+ if (error) throw new Error(error.message);
5016
+ },
5017
+ async deleteChannel(id) {
5018
+ const { error } = await sb().from(GCAL_TABLES.channels).delete().eq("id", id);
5019
+ if (error) throw new Error(error.message);
5020
+ },
5021
+ async listExternalEvents(params) {
5022
+ const { data, error } = await sb().functions.invoke(FUNCTION, {
5023
+ body: { action: "list_external_events", tenantId: core.getActiveTenantId(), channelId: params?.channelId, timeMin: params?.timeMin }
5024
+ });
5025
+ if (error) throw new Error(error.message);
5026
+ return data.events ?? [];
5027
+ },
5028
+ async importEvents(items) {
5029
+ const { data, error } = await sb().functions.invoke(FUNCTION, {
5030
+ body: { action: "import_events", tenantId: core.getActiveTenantId(), items }
5031
+ });
5032
+ if (error) throw new Error(error.message);
5033
+ return data;
5218
5034
  }
5219
5035
  };
5220
5036
  }
5037
+ function CalendarColorDot({
5038
+ color,
5039
+ className = ""
5040
+ }) {
5041
+ return /* @__PURE__ */ jsxRuntime.jsx(
5042
+ "span",
5043
+ {
5044
+ className: `inline-block h-2.5 w-2.5 shrink-0 rounded-full border border-black/10 ${className}`,
5045
+ style: { backgroundColor: color || "hsl(var(--muted-foreground))" },
5046
+ "aria-hidden": true
5047
+ }
5048
+ );
5049
+ }
5050
+ var EMPTY = { assignee: [], service: [], location: [] };
5051
+ function useAgendaLookups(enabled) {
5052
+ const [lookups, setLookups] = React13.useState(EMPTY);
5053
+ const [loading, setLoading] = React13.useState(false);
5054
+ React13.useEffect(() => {
5055
+ let cancelled = false;
5056
+ setLoading(true);
5057
+ void (async () => {
5058
+ const agenda = createSupabaseAgendaProvider();
5059
+ const tenantId = core.getActiveTenantId();
5060
+ const publicProvider = tenantId ? chunkRX2HMWXS_cjs.createSupabasePublicBookingProvider({ tenantId }) : null;
5061
+ const [assignee, location, service] = await Promise.all([
5062
+ agenda.getProfessionals().then(
5063
+ (rows) => rows.map((p) => ({ id: p.id, name: p.name })),
5064
+ () => []
5065
+ ),
5066
+ (agenda.getLocations?.() ?? Promise.resolve([])).then(
5067
+ (rows) => rows.map((l) => ({ id: l.id, name: l.name })),
5068
+ () => []
5069
+ ),
5070
+ (publicProvider?.getServices() ?? Promise.resolve([])).then(
5071
+ (rows) => rows.map((s) => ({ id: s.id, name: s.name })),
5072
+ () => []
5073
+ )
5074
+ ]);
5075
+ if (cancelled) return;
5076
+ setLookups({ assignee, service, location });
5077
+ setLoading(false);
5078
+ })();
5079
+ return () => {
5080
+ cancelled = true;
5081
+ };
5082
+ }, [enabled]);
5083
+ const hasAny = lookups.assignee.length > 0 || lookups.service.length > 0 || lookups.location.length > 0;
5084
+ return { lookups, loading, hasAny };
5085
+ }
5086
+ function optionsForKind(lookups, kind) {
5087
+ if (kind === "assignee") return lookups.assignee;
5088
+ if (kind === "service") return lookups.service;
5089
+ if (kind === "location") return lookups.location;
5090
+ return [];
5091
+ }
5092
+ var DIRECTION_LABELS = {
5093
+ bidirectional: "Two-way",
5094
+ inbound: "Import only",
5095
+ outbound: "Export only",
5096
+ off: "Off"
5097
+ };
5098
+ var IMPORT_MODE_LABELS = {
5099
+ block: "Block",
5100
+ appointment: "Appointment"
5101
+ };
5102
+ var TARGET_KIND_LABELS = {
5103
+ agenda: "Whole agenda",
5104
+ assignee: "Professional",
5105
+ service: "Service",
5106
+ location: "Location"
5107
+ };
5108
+ var NO_TARGET = "__none__";
5109
+ function defaultChannel() {
5110
+ return { direction: "bidirectional", targetKind: null, targetId: null, importMode: "block" };
5111
+ }
5112
+ function ChannelMappingList({
5113
+ provider: provider2,
5114
+ onChannelsChange
5115
+ }) {
5116
+ const [calendars, setCalendars] = React13.useState([]);
5117
+ const [byGcalId, setByGcalId] = React13.useState({});
5118
+ const [loading, setLoading] = React13.useState(true);
5119
+ const [error, setError] = React13.useState(null);
5120
+ const [savingId, setSavingId] = React13.useState(null);
5121
+ const { lookups, hasAny: hasLookups } = useAgendaLookups(true);
5122
+ const load = React13.useCallback(async () => {
5123
+ setLoading(true);
5124
+ setError(null);
5125
+ try {
5126
+ const [cals, channels] = await Promise.all([
5127
+ provider2.listGoogleCalendars(),
5128
+ provider2.listChannels()
5129
+ ]);
5130
+ setCalendars(cals);
5131
+ const map = {};
5132
+ for (const ch of channels) map[ch.googleCalendarId] = ch;
5133
+ setByGcalId(map);
5134
+ onChannelsChange?.(channels);
5135
+ } catch (e) {
5136
+ setError(e?.message ?? "Could not load Google calendars");
5137
+ } finally {
5138
+ setLoading(false);
5139
+ }
5140
+ }, [provider2, onChannelsChange]);
5141
+ React13.useEffect(() => {
5142
+ void load();
5143
+ }, [load]);
5144
+ const persist = React13.useCallback(
5145
+ async (cal, patch) => {
5146
+ const existing = byGcalId[cal.id];
5147
+ const base = existing ?? { ...defaultChannel(), isActive: true };
5148
+ setSavingId(cal.id);
5149
+ try {
5150
+ await provider2.saveChannel({
5151
+ id: existing?.id,
5152
+ googleCalendarId: cal.id,
5153
+ summary: cal.summary,
5154
+ color: cal.backgroundColor ?? null,
5155
+ direction: patch.direction ?? base.direction,
5156
+ targetKind: patch.targetKind !== void 0 ? patch.targetKind : base.targetKind,
5157
+ targetId: patch.targetId !== void 0 ? patch.targetId : base.targetId ?? null,
5158
+ importMode: patch.importMode ?? base.importMode,
5159
+ isActive: patch.isActive !== void 0 ? patch.isActive : base.isActive
5160
+ });
5161
+ const channels = await provider2.listChannels();
5162
+ const map = {};
5163
+ for (const ch of channels) map[ch.googleCalendarId] = ch;
5164
+ setByGcalId(map);
5165
+ onChannelsChange?.(channels);
5166
+ } catch (e) {
5167
+ ui.toast.error(e?.message ?? "Could not save calendar mapping");
5168
+ } finally {
5169
+ setSavingId(null);
5170
+ }
5171
+ },
5172
+ [byGcalId, provider2, onChannelsChange]
5173
+ );
5174
+ if (loading) {
5175
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-6 text-center text-sm text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin inline" }) });
5176
+ }
5177
+ if (error) {
5178
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-md border border-warning/40 bg-warning-soft/40 px-3 py-3 text-sm", children: [
5179
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-warning-soft-foreground", children: [
5180
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-4 w-4" }),
5181
+ " ",
5182
+ error
5183
+ ] }),
5184
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "outline", size: "sm", className: "mt-2", onClick: () => void load(), children: [
5185
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: "h-3.5 w-3.5" }),
5186
+ " Retry"
5187
+ ] })
5188
+ ] });
5189
+ }
5190
+ if (calendars.length === 0) {
5191
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "py-6 text-center text-sm text-muted-foreground", children: "No Google calendars found on this account." });
5192
+ }
5193
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-md border divide-y", children: [
5194
+ calendars.map((cal) => {
5195
+ const ch = byGcalId[cal.id];
5196
+ const active = !!ch?.isActive;
5197
+ const saving = savingId === cal.id;
5198
+ const direction = ch?.direction ?? "bidirectional";
5199
+ const targetKind = ch?.targetKind ?? null;
5200
+ const importMode = ch?.importMode ?? "block";
5201
+ const canImport = direction === "inbound" || direction === "bidirectional";
5202
+ const kindOptions = optionsForKind(lookups, targetKind);
5203
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "px-3 py-3 space-y-3", children: [
5204
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5", children: [
5205
+ /* @__PURE__ */ jsxRuntime.jsx(
5206
+ ui.Checkbox,
5207
+ {
5208
+ checked: active,
5209
+ disabled: saving,
5210
+ onChange: (next) => void persist(cal, { isActive: next })
5211
+ }
5212
+ ),
5213
+ /* @__PURE__ */ jsxRuntime.jsx(CalendarColorDot, { color: cal.backgroundColor }),
5214
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium truncate", children: cal.summary }),
5215
+ cal.primary && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { variant: "outline", className: "shrink-0", children: "Primary" }),
5216
+ saving && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "ml-auto h-3.5 w-3.5 animate-spin text-muted-foreground" })
5217
+ ] }),
5218
+ active && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-2 pl-[26px] sm:grid-cols-2 lg:grid-cols-4", children: [
5219
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
5220
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-medium text-muted-foreground", children: "Direction" }),
5221
+ /* @__PURE__ */ jsxRuntime.jsxs(
5222
+ ui.Select,
5223
+ {
5224
+ value: direction,
5225
+ onValueChange: (v) => void persist(cal, { direction: v }),
5226
+ children: [
5227
+ /* @__PURE__ */ jsxRuntime.jsx(ui.SelectTrigger, { className: "mt-1 h-8 text-sm", children: /* @__PURE__ */ jsxRuntime.jsx(ui.SelectValue, {}) }),
5228
+ /* @__PURE__ */ jsxRuntime.jsx(ui.SelectContent, { children: Object.keys(DIRECTION_LABELS).map((d) => /* @__PURE__ */ jsxRuntime.jsx(ui.SelectItem, { value: d, children: DIRECTION_LABELS[d] }, d)) })
5229
+ ]
5230
+ }
5231
+ )
5232
+ ] }),
5233
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
5234
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-medium text-muted-foreground", children: "Target" }),
5235
+ /* @__PURE__ */ jsxRuntime.jsxs(
5236
+ ui.Select,
5237
+ {
5238
+ value: targetKind ?? "agenda",
5239
+ onValueChange: (v) => void persist(cal, {
5240
+ targetKind: v === "agenda" ? null : v,
5241
+ // Reset the id whenever the kind changes.
5242
+ targetId: null
5243
+ }),
5244
+ children: [
5245
+ /* @__PURE__ */ jsxRuntime.jsx(ui.SelectTrigger, { className: "mt-1 h-8 text-sm", children: /* @__PURE__ */ jsxRuntime.jsx(ui.SelectValue, {}) }),
5246
+ /* @__PURE__ */ jsxRuntime.jsx(ui.SelectContent, { children: Object.keys(TARGET_KIND_LABELS).map(
5247
+ (k) => /* @__PURE__ */ jsxRuntime.jsx(ui.SelectItem, { value: k, children: TARGET_KIND_LABELS[k] }, k)
5248
+ ) })
5249
+ ]
5250
+ }
5251
+ )
5252
+ ] }),
5253
+ targetKind && /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
5254
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-medium text-muted-foreground", children: TARGET_KIND_LABELS[targetKind] }),
5255
+ kindOptions.length > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
5256
+ ui.Select,
5257
+ {
5258
+ value: ch?.targetId ?? NO_TARGET,
5259
+ onValueChange: (v) => void persist(cal, { targetId: v === NO_TARGET ? null : v }),
5260
+ children: [
5261
+ /* @__PURE__ */ jsxRuntime.jsx(ui.SelectTrigger, { className: "mt-1 h-8 text-sm", children: /* @__PURE__ */ jsxRuntime.jsx(ui.SelectValue, { placeholder: "Select\u2026" }) }),
5262
+ /* @__PURE__ */ jsxRuntime.jsx(ui.SelectContent, { children: kindOptions.map((o) => /* @__PURE__ */ jsxRuntime.jsx(ui.SelectItem, { value: o.id, children: o.name }, o.id)) })
5263
+ ]
5264
+ }
5265
+ ) : /* @__PURE__ */ jsxRuntime.jsx(
5266
+ "input",
5267
+ {
5268
+ defaultValue: ch?.targetId ?? "",
5269
+ placeholder: "uuid",
5270
+ onBlur: (e) => void persist(cal, { targetId: e.target.value.trim() || null }),
5271
+ className: "w-full mt-1 h-8 rounded-input border border-input bg-card px-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
5272
+ }
5273
+ )
5274
+ ] }),
5275
+ canImport && /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block", children: [
5276
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[11px] font-medium text-muted-foreground", children: "Import as" }),
5277
+ /* @__PURE__ */ jsxRuntime.jsxs(
5278
+ ui.Select,
5279
+ {
5280
+ value: importMode,
5281
+ onValueChange: (v) => void persist(cal, { importMode: v }),
5282
+ children: [
5283
+ /* @__PURE__ */ jsxRuntime.jsx(ui.SelectTrigger, { className: "mt-1 h-8 text-sm", children: /* @__PURE__ */ jsxRuntime.jsx(ui.SelectValue, {}) }),
5284
+ /* @__PURE__ */ jsxRuntime.jsx(ui.SelectContent, { children: Object.keys(IMPORT_MODE_LABELS).map((m) => /* @__PURE__ */ jsxRuntime.jsx(ui.SelectItem, { value: m, children: IMPORT_MODE_LABELS[m] }, m)) })
5285
+ ]
5286
+ }
5287
+ )
5288
+ ] })
5289
+ ] })
5290
+ ] }, cal.id);
5291
+ }),
5292
+ !hasLookups && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "px-3 py-2 text-[11px] text-muted-foreground", children: "No professional/service/location lookups available \u2014 target ids fall back to manual uuid entry." })
5293
+ ] });
5294
+ }
5295
+ function formatWhen(ev) {
5296
+ const start = new Date(ev.startsAt);
5297
+ if (ev.allDay) {
5298
+ return start.toLocaleDateString("pt-BR", { day: "2-digit", month: "short", year: "numeric" });
5299
+ }
5300
+ return start.toLocaleString("pt-BR", {
5301
+ day: "2-digit",
5302
+ month: "short",
5303
+ hour: "2-digit",
5304
+ minute: "2-digit"
5305
+ });
5306
+ }
5307
+ function ImportEventsWizard({
5308
+ provider: provider2,
5309
+ channels,
5310
+ onClose,
5311
+ onImported
5312
+ }) {
5313
+ const [events, setEvents] = React13.useState([]);
5314
+ const [selected, setSelected] = React13.useState(/* @__PURE__ */ new Set());
5315
+ const [loading, setLoading] = React13.useState(true);
5316
+ const [error, setError] = React13.useState(null);
5317
+ const [importing, setImporting] = React13.useState(false);
5318
+ const [result, setResult] = React13.useState(null);
5319
+ const channelById = React13.useMemo(() => {
5320
+ const m = {};
5321
+ for (const c of channels) m[c.id] = c;
5322
+ return m;
5323
+ }, [channels]);
5324
+ const load = React13.useCallback(async () => {
5325
+ setLoading(true);
5326
+ setError(null);
5327
+ setResult(null);
5328
+ try {
5329
+ const rows = await provider2.listExternalEvents();
5330
+ setEvents(rows);
5331
+ setSelected(new Set(rows.map((r) => r.eventId)));
5332
+ } catch (e) {
5333
+ setError(e?.message ?? "Could not load upcoming events");
5334
+ } finally {
5335
+ setLoading(false);
5336
+ }
5337
+ }, [provider2]);
5338
+ React13.useEffect(() => {
5339
+ void load();
5340
+ }, [load]);
5341
+ const toggle = (eventId) => setSelected((prev) => {
5342
+ const next = new Set(prev);
5343
+ if (next.has(eventId)) next.delete(eventId);
5344
+ else next.add(eventId);
5345
+ return next;
5346
+ });
5347
+ const allSelected = events.length > 0 && selected.size === events.length;
5348
+ const toggleAll = () => setSelected(allSelected ? /* @__PURE__ */ new Set() : new Set(events.map((e) => e.eventId)));
5349
+ async function handleImport() {
5350
+ const items = events.filter((e) => selected.has(e.eventId)).map((e) => ({ channelId: e.channelId, eventId: e.eventId }));
5351
+ if (items.length === 0) return;
5352
+ setImporting(true);
5353
+ try {
5354
+ const res = await provider2.importEvents(items);
5355
+ setResult(res);
5356
+ onImported?.(res);
5357
+ ui.toast.success(`Imported ${res.imported} \xB7 skipped ${res.skipped}`);
5358
+ await load();
5359
+ } catch (e) {
5360
+ ui.toast.error(e?.message ?? "Import failed");
5361
+ } finally {
5362
+ setImporting(false);
5363
+ }
5364
+ }
5365
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-md border bg-card", children: [
5366
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border-b px-3 py-2", children: [
5367
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CalendarPlus, { className: "h-4 w-4 text-muted-foreground" }),
5368
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-semibold", children: "Import future events" }),
5369
+ /* @__PURE__ */ jsxRuntime.jsx(
5370
+ "button",
5371
+ {
5372
+ onClick: onClose,
5373
+ className: "ml-auto rounded p-1 text-muted-foreground hover:bg-muted/50",
5374
+ "aria-label": "Close",
5375
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3.5 w-3.5" })
5376
+ }
5377
+ )
5378
+ ] }),
5379
+ loading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-8 text-center text-sm text-muted-foreground", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin inline" }) }) : error ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "p-3", children: [
5380
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-sm text-warning-soft-foreground", children: [
5381
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-4 w-4" }),
5382
+ " ",
5383
+ error
5384
+ ] }),
5385
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "outline", size: "sm", className: "mt-2", onClick: () => void load(), children: [
5386
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: "h-3.5 w-3.5" }),
5387
+ " Retry"
5388
+ ] })
5389
+ ] }) : events.length === 0 ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "py-8 px-4 text-center space-y-1", children: [
5390
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium", children: "You're all caught up" }),
5391
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: "No upcoming Google events left to import on your active calendars." }),
5392
+ result && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-success", children: [
5393
+ "Imported ",
5394
+ result.imported,
5395
+ " \xB7 skipped ",
5396
+ result.skipped,
5397
+ result.errors.length > 0 ? ` \xB7 ${result.errors.length} errors` : ""
5398
+ ] })
5399
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
5400
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2.5 border-b px-3 py-2", children: [
5401
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Checkbox, { checked: allSelected, onChange: toggleAll }),
5402
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
5403
+ selected.size,
5404
+ " of ",
5405
+ events.length,
5406
+ " selected"
5407
+ ] })
5408
+ ] }),
5409
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-[320px] overflow-y-auto divide-y", children: events.map((ev) => {
5410
+ const ch = channelById[ev.channelId];
5411
+ return /* @__PURE__ */ jsxRuntime.jsxs(
5412
+ "label",
5413
+ {
5414
+ className: "flex items-start gap-2.5 px-3 py-2.5 cursor-pointer hover:bg-muted/30",
5415
+ children: [
5416
+ /* @__PURE__ */ jsxRuntime.jsx(
5417
+ ui.Checkbox,
5418
+ {
5419
+ checked: selected.has(ev.eventId),
5420
+ onChange: () => toggle(ev.eventId)
5421
+ }
5422
+ ),
5423
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1 space-y-1", children: [
5424
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
5425
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm font-medium truncate", children: ev.summary || "(no title)" }),
5426
+ ev.allDay && /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { variant: "secondary", className: "shrink-0", children: "All-day" })
5427
+ ] }),
5428
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-xs text-muted-foreground", children: [
5429
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: formatWhen(ev) }),
5430
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "inline-flex items-center gap-1", children: [
5431
+ /* @__PURE__ */ jsxRuntime.jsx(CalendarColorDot, { color: ch?.color }),
5432
+ ch?.summary ?? ev.calendarId
5433
+ ] })
5434
+ ] })
5435
+ ] })
5436
+ ]
5437
+ },
5438
+ ev.eventId
5439
+ );
5440
+ }) }),
5441
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 border-t px-3 py-2", children: [
5442
+ result && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs text-muted-foreground", children: [
5443
+ "Last run: ",
5444
+ result.imported,
5445
+ " imported",
5446
+ result.errors.length > 0 ? `, ${result.errors.length} errors` : ""
5447
+ ] }),
5448
+ /* @__PURE__ */ jsxRuntime.jsxs(
5449
+ ui.Button,
5450
+ {
5451
+ size: "sm",
5452
+ className: "ml-auto",
5453
+ disabled: importing || selected.size === 0,
5454
+ onClick: handleImport,
5455
+ children: [
5456
+ importing ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "h-3.5 w-3.5" }),
5457
+ "Import ",
5458
+ selected.size > 0 ? selected.size : ""
5459
+ ]
5460
+ }
5461
+ )
5462
+ ] })
5463
+ ] })
5464
+ ] });
5465
+ }
5221
5466
  var provider = createGoogleCalendarProvider();
5467
+ function inboundChannels(channels) {
5468
+ return channels.filter(
5469
+ (c) => c.isActive && (c.direction === "inbound" || c.direction === "bidirectional")
5470
+ );
5471
+ }
5222
5472
  function GoogleCalendarExtraPanel() {
5223
- const [connected, setConnected] = React12.useState(false);
5224
- const [calendarId, setCalendarId] = React12.useState("primary");
5225
- const [syncing, setSyncing] = React12.useState(false);
5226
- const [log, setLog] = React12.useState([]);
5227
- const [loaded, setLoaded] = React12.useState(false);
5473
+ const [connected, setConnected] = React13.useState(false);
5474
+ const [lastSyncAt, setLastSyncAt] = React13.useState();
5475
+ const [syncing, setSyncing] = React13.useState(false);
5476
+ const [log, setLog] = React13.useState([]);
5477
+ const [loaded, setLoaded] = React13.useState(false);
5478
+ const [channels, setChannels] = React13.useState([]);
5479
+ const [showMapping, setShowMapping] = React13.useState(false);
5480
+ const [showWizard, setShowWizard] = React13.useState(false);
5228
5481
  async function load() {
5229
5482
  const integ = await provider.getIntegration();
5230
5483
  setConnected(!!integ?.connected);
5231
5484
  if (integ) {
5232
- setCalendarId(integ.calendarId);
5485
+ setLastSyncAt(integ.lastSyncAt);
5233
5486
  void provider.getSyncLog().then(setLog);
5487
+ void provider.listChannels().then((ch) => {
5488
+ setChannels(ch);
5489
+ if (ch.length > 0) setShowMapping(true);
5490
+ });
5234
5491
  }
5235
5492
  setLoaded(true);
5236
5493
  }
5237
- React12.useEffect(() => {
5494
+ React13.useEffect(() => {
5238
5495
  void load();
5239
5496
  }, []);
5240
- if (!loaded || !connected) return null;
5241
- async function handleSaveCalendar() {
5242
- await provider.setCalendar(calendarId);
5243
- ui.toast.success("Saved");
5497
+ if (!loaded) return null;
5498
+ if (!connected) {
5499
+ return /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground border-t pt-4", children: "Connect a Google account above to map your calendars and import events." });
5244
5500
  }
5245
5501
  async function handleSyncNow() {
5246
5502
  setSyncing(true);
@@ -5248,48 +5504,81 @@ function GoogleCalendarExtraPanel() {
5248
5504
  const r = await provider.syncNow();
5249
5505
  ui.toast.success(`Synced \u2014 ${r.written} updated`);
5250
5506
  void provider.getSyncLog().then(setLog);
5507
+ void provider.getIntegration().then((i) => setLastSyncAt(i?.lastSyncAt));
5251
5508
  } catch (e) {
5252
5509
  ui.toast.error(e?.message ?? "Sync failed");
5253
5510
  } finally {
5254
5511
  setSyncing(false);
5255
5512
  }
5256
5513
  }
5257
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 border-t pt-4", children: [
5258
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-end gap-3", children: [
5259
- /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "block flex-1 min-w-[200px]", children: [
5260
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-medium text-muted-foreground", children: "Calendar ID" }),
5261
- /* @__PURE__ */ jsxRuntime.jsx(
5262
- "input",
5263
- {
5264
- value: calendarId,
5265
- onChange: (e) => setCalendarId(e.target.value),
5266
- placeholder: "primary",
5267
- className: "w-full mt-1 rounded-input border border-input bg-card px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
5268
- }
5269
- )
5514
+ const canImport = inboundChannels(channels).length > 0;
5515
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-5 border-t pt-4", children: [
5516
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-2", children: [
5517
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
5518
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CalendarCog, { className: "h-4 w-4 text-muted-foreground" }),
5519
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-semibold", children: "Calendars" })
5270
5520
  ] }),
5271
- /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { variant: "outline", size: "sm", onClick: handleSaveCalendar, children: "Save" }),
5272
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "outline", size: "sm", onClick: handleSyncNow, disabled: syncing, children: [
5273
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: `h-3.5 w-3.5 ${syncing ? "animate-spin" : ""}` }),
5274
- " Sync now"
5521
+ showMapping ? /* @__PURE__ */ jsxRuntime.jsx(ChannelMappingList, { provider, onChannelsChange: setChannels }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rounded-md border border-dashed px-4 py-5 text-center space-y-2", children: [
5522
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground", children: "Map each Google calendar to what it syncs \u2014 a professional, service, location, or your whole agenda." }),
5523
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "outline", size: "sm", onClick: () => setShowMapping(true), children: [
5524
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CalendarCog, { className: "h-3.5 w-3.5" }),
5525
+ " Configure calendars"
5526
+ ] })
5275
5527
  ] })
5276
5528
  ] }),
5277
- log.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
5278
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
5279
- /* @__PURE__ */ jsxRuntime.jsx(lucideReact.History, { className: "h-4 w-4 text-muted-foreground" }),
5280
- /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-semibold", children: "History" })
5529
+ showMapping && /* @__PURE__ */ jsxRuntime.jsx("section", { className: "space-y-2", children: showWizard ? /* @__PURE__ */ jsxRuntime.jsx(
5530
+ ImportEventsWizard,
5531
+ {
5532
+ provider,
5533
+ channels: inboundChannels(channels),
5534
+ onClose: () => setShowWizard(false),
5535
+ onImported: () => {
5536
+ void provider.getSyncLog().then(setLog);
5537
+ void provider.getIntegration().then((i) => setLastSyncAt(i?.lastSyncAt));
5538
+ }
5539
+ }
5540
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(
5541
+ ui.Button,
5542
+ {
5543
+ variant: "outline",
5544
+ size: "sm",
5545
+ disabled: !canImport,
5546
+ title: canImport ? void 0 : "Enable an importing calendar first",
5547
+ onClick: () => setShowWizard(true),
5548
+ children: [
5549
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CalendarPlus, { className: "h-3.5 w-3.5" }),
5550
+ " Import future events"
5551
+ ]
5552
+ }
5553
+ ) }),
5554
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { className: "space-y-2", children: [
5555
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [
5556
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "outline", size: "sm", onClick: handleSyncNow, disabled: syncing, children: [
5557
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { className: `h-3.5 w-3.5 ${syncing ? "animate-spin" : ""}` }),
5558
+ " Sync now"
5559
+ ] }),
5560
+ lastSyncAt && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[11px] text-muted-foreground", children: [
5561
+ "Last sync: ",
5562
+ new Date(lastSyncAt).toLocaleString()
5563
+ ] })
5281
5564
  ] }),
5282
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border divide-y text-sm", children: log.map((r) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 px-3 py-2", children: [
5283
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-xs", children: new Date(r.createdAt).toLocaleString() }),
5284
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] uppercase tracking-wide px-1 py-px rounded bg-muted text-muted-foreground", children: r.direction }),
5285
- /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-auto text-xs", children: [
5286
- r.written,
5287
- " written / ",
5288
- r.fetched,
5289
- " fetched"
5565
+ log.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-2", children: [
5566
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2", children: [
5567
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.History, { className: "h-4 w-4 text-muted-foreground" }),
5568
+ /* @__PURE__ */ jsxRuntime.jsx("h4", { className: "text-sm font-semibold", children: "History" })
5290
5569
  ] }),
5291
- r.status === "success" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "h-3.5 w-3.5 text-success" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-3.5 w-3.5 text-warning" })
5292
- ] }, r.id)) })
5570
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border divide-y text-sm", children: log.map((r) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 px-3 py-2", children: [
5571
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground text-xs", children: new Date(r.createdAt).toLocaleString() }),
5572
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] uppercase tracking-wide px-1 py-px rounded bg-muted text-muted-foreground", children: r.direction }),
5573
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-auto text-xs", children: [
5574
+ r.written,
5575
+ " written / ",
5576
+ r.fetched,
5577
+ " fetched"
5578
+ ] }),
5579
+ r.status === "success" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCircle2, { className: "h-3.5 w-3.5 text-success" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "h-3.5 w-3.5 text-warning" })
5580
+ ] }, r.id)) })
5581
+ ] })
5293
5582
  ] })
5294
5583
  ] });
5295
5584
  }
@@ -5324,31 +5613,6 @@ var googleCalendarConnector = {
5324
5613
  ]
5325
5614
  };
5326
5615
 
5327
- // src/integrations/google-calendar/mapping.ts
5328
- function bookingSummary(b) {
5329
- const services = (b.services ?? []).map((s) => s.name).filter(Boolean).join(", ");
5330
- const who = b.clientName ?? "";
5331
- if (services && who) return `${services} \u2014 ${who}`;
5332
- return services || who || "Appointment";
5333
- }
5334
- function bookingToEvent2(b, timeZone) {
5335
- if (!b.startsAt || !b.endsAt) return null;
5336
- const notes = [b.notes, b.professionalName ? `Pro: ${b.professionalName}` : "", b.locationName ?? ""].filter(Boolean).join("\n");
5337
- return {
5338
- summary: bookingSummary(b),
5339
- description: notes || void 0,
5340
- start: { dateTime: b.startsAt, timeZone },
5341
- end: { dateTime: b.endsAt, timeZone }
5342
- };
5343
- }
5344
- function eventToBookingPatch(ev) {
5345
- if (ev.status === "cancelled") return { status: "cancelled" };
5346
- return {
5347
- startsAt: ev.start?.dateTime,
5348
- endsAt: ev.end?.dateTime
5349
- };
5350
- }
5351
-
5352
5616
  // src/integrations/google-calendar/index.ts
5353
5617
  function createGoogleCalendarPlugin() {
5354
5618
  return {
@@ -5370,8 +5634,8 @@ function createAgendaPlugin(options) {
5370
5634
  const config = resolveConfig(options);
5371
5635
  core.registerTranslations(agendaLocales);
5372
5636
  const provider2 = options?.dataProvider ?? core.createSafeDataProvider(
5373
- () => createSupabaseAgendaProvider(),
5374
- () => chunkLFUELUIT_cjs.createMockAgendaProvider()
5637
+ () => createSupabaseAgendaProvider({ professionalKind: config.professionalKind }),
5638
+ () => chunkRX2HMWXS_cjs.createMockAgendaProvider()
5375
5639
  );
5376
5640
  const store = createAgendaStore(provider2, options?.financialBridge);
5377
5641
  const registries = [...agendaRegistries, ...options?.settingsRegistries ?? []];
@@ -5390,7 +5654,7 @@ function createAgendaPlugin(options) {
5390
5654
  return locations;
5391
5655
  } : void 0
5392
5656
  });
5393
- const PageComponent = () => React12__default.default.createElement(AgendaPage, { config, provider: provider2, store });
5657
+ const PageComponent = () => React13__default.default.createElement(AgendaPage, { config, provider: provider2, store });
5394
5658
  return {
5395
5659
  id: "agenda",
5396
5660
  name: config.labels.pageTitle,
@@ -5407,6 +5671,48 @@ function createAgendaPlugin(options) {
5407
5671
  { id: "appointments", label: config.labels.pageTitle, group: config.labels.pageTitle },
5408
5672
  { id: "agenda.schedules", label: "Schedule Management", group: config.labels.pageTitle }
5409
5673
  ],
5674
+ // Recurring monthly quota — counts appointment rows created this month.
5675
+ // (Clients/patients are the app's CRUD entity, so their cap is declared by
5676
+ // the app's EntityDef.limitKey, not here.)
5677
+ declaredLimits: [
5678
+ { key: "bookings_month", label: "Appointments this month", table: "appointments", period: "month" }
5679
+ ],
5680
+ queryEntities: [
5681
+ {
5682
+ key: "agenda:appointments",
5683
+ entity: {
5684
+ name: "Appointment",
5685
+ namePlural: "Appointments",
5686
+ icon: "Calendar",
5687
+ permission: { feature: "appointments", action: "read" },
5688
+ // Keys are the PROVIDER's output shape (camelCase) — what filters/
5689
+ // dateField/groupBy match against, not the raw DB columns.
5690
+ fields: [
5691
+ { key: "startsAt", label: "Starts at", type: "text" },
5692
+ { key: "endsAt", label: "Ends at", type: "text" },
5693
+ { key: "status", label: "Status", type: "text" },
5694
+ { key: "clientName", label: "Client", type: "text", searchable: true },
5695
+ { key: "professionalName", label: "Professional", type: "text", searchable: true },
5696
+ { key: "orderTotal", label: "Order total", type: "number" },
5697
+ { key: "totalDurationMinutes", label: "Duration (min)", type: "number" },
5698
+ { key: "createdAt", label: "Created at", type: "text" }
5699
+ ],
5700
+ data: {
5701
+ table: "v_appointments",
5702
+ tenantScoped: true,
5703
+ searchColumns: ["client_name", "professional_name"]
5704
+ }
5705
+ }
5706
+ }
5707
+ ],
5708
+ declaredRpcs: [
5709
+ {
5710
+ name: "agent_agenda_create_appointment",
5711
+ kind: "write",
5712
+ description: "Guarded appointment create: agent_guard (role\u2192plan\u2192bookings_month cap), working hours, race-safe conflict check, audited.",
5713
+ audits: true
5714
+ }
5715
+ ],
5410
5716
  navigation: [
5411
5717
  {
5412
5718
  section: options?.navSection ?? "main",
@@ -5421,7 +5727,9 @@ function createAgendaPlugin(options) {
5421
5727
  {
5422
5728
  path: "/agenda",
5423
5729
  component: PageComponent,
5424
- permission: { feature: "appointments", action: "read" }
5730
+ permission: { feature: "appointments", action: "read" },
5731
+ // Matches the ref agent_agenda_create_appointment returns.
5732
+ entityRoutes: ["schedule:appointment"]
5425
5733
  }
5426
5734
  ],
5427
5735
  widgets: [],
@@ -5450,20 +5758,22 @@ function createAgendaPlugin(options) {
5450
5758
  {
5451
5759
  id: "agenda.create-appointment",
5452
5760
  name: "createAppointment",
5453
- description: "Creates a new appointment for a client with a specific professional and service.",
5761
+ description: "Creates a new appointment. Takes IDS, not names: resolve the client/service/professional ids first via findAnything/searchRecords, then call this with an ISO start time in the tenant timezone. The user confirms before booking.",
5454
5762
  icon: "CalendarPlus",
5455
5763
  mode: "persist",
5764
+ limitKey: "bookings_month",
5765
+ execution: { plane: "server", kind: "rpc", rpc: "agent_agenda_create_appointment" },
5456
5766
  category: "Scheduling",
5457
5767
  parameters: {
5458
5768
  type: "object",
5459
5769
  properties: {
5460
- client: { type: "string", description: "Client name" },
5461
- professional: { type: "string", description: "Professional name" },
5462
- service: { type: "string", description: "Service name" },
5463
- date: { type: "string", description: "Date (YYYY-MM-DD)" },
5464
- time: { type: "string", description: "Time (HH:MM)" }
5770
+ client_id: { type: "string", description: "Client id (from a search)" },
5771
+ service_id: { type: "string", description: "Service id (from a search)" },
5772
+ professional_id: { type: "string", description: "Professional id (optional \u2014 auto-assigned when omitted)" },
5773
+ starts_at: { type: "string", description: "LOCAL date-time of the business, NO timezone suffix \u2014 e.g. '2026-07-27T10:00' for 10h. The system applies the tenant timezone." },
5774
+ notes: { type: "string", description: "Optional notes" }
5465
5775
  },
5466
- required: ["client", "professional", "service", "date", "time"]
5776
+ required: ["client_id", "service_id", "starts_at"]
5467
5777
  },
5468
5778
  suggestions: [
5469
5779
  { label: "Book a haircut for Sarah tomorrow at 10am" }
@@ -5501,9 +5811,9 @@ function createAgendaPlugin(options) {
5501
5811
  icon: "Calendar",
5502
5812
  // Standard layout: General | Integrations (Google Calendar addon) | Properties.
5503
5813
  component: (() => {
5504
- const AgendaSettingsTab = () => React12__default.default.createElement(saas.PluginSettingsPanel, {
5814
+ const AgendaSettingsTab = () => React13__default.default.createElement(saas.PluginSettingsPanel, {
5505
5815
  title: "Agenda",
5506
- generalSettings: React12__default.default.createElement(AgendaGeneralSettings),
5816
+ generalSettings: React13__default.default.createElement(AgendaGeneralSettings),
5507
5817
  registries,
5508
5818
  hostPluginId: "agenda",
5509
5819
  routeBase: "/settings/agenda"
@@ -5529,7 +5839,7 @@ function createAgendaPlugin(options) {
5529
5839
 
5530
5840
  Object.defineProperty(exports, "createMockAgendaProvider", {
5531
5841
  enumerable: true,
5532
- get: function () { return chunkLFUELUIT_cjs.createMockAgendaProvider; }
5842
+ get: function () { return chunkRX2HMWXS_cjs.createMockAgendaProvider; }
5533
5843
  });
5534
5844
  exports.bookingToEvent = bookingToEvent2;
5535
5845
  exports.computeCommissionAmount = computeCommissionAmount;