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