@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.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { setAgendaTenantId, getAgendaTenantId } from './chunk-3LJKZQK4.js';
|
|
2
|
-
import
|
|
3
|
-
import { getSupabaseClientOptional, registerTranslations, createSafeDataProvider, usePluginRuntimeOptional, useTranslation, getActiveTenantId, getCurrentLocale } from '@fayz-ai/core';
|
|
2
|
+
import React12, { useState, useEffect, useRef, useMemo, useCallback } from 'react';
|
|
3
|
+
import { getSupabaseClientOptional, registerTranslations, createSafeDataProvider, usePluginRuntimeOptional, useTranslation, getActiveTenantId, getCurrentLocale, formatCurrency } from '@fayz-ai/core';
|
|
4
4
|
import { createPluginContext, setScheduleBlockConfig, getScheduleBlockConfig, PluginSettingsPanel, dedup, SettingsGroup, ToggleRow, SelectRow, usePluginPrefs, PersonLink, CrudFormPage, CrudDetailPage } from '@fayz-ai/saas';
|
|
5
5
|
import FullCalendar from '@fullcalendar/react';
|
|
6
6
|
import dayGridPlugin from '@fullcalendar/daygrid';
|
|
@@ -67,23 +67,47 @@ var DEFAULT_BOOKING_TYPES = [
|
|
|
67
67
|
requiresClient: false
|
|
68
68
|
}
|
|
69
69
|
];
|
|
70
|
+
var SIMPLE_EVENT_BOOKING_TYPES = [
|
|
71
|
+
{
|
|
72
|
+
id: "event",
|
|
73
|
+
label: "Event",
|
|
74
|
+
icon: "Calendar",
|
|
75
|
+
color: "#6366f1",
|
|
76
|
+
fields: { title: true, client: false, professional: false, services: false, location: false, status: false },
|
|
77
|
+
requiresServices: false,
|
|
78
|
+
requiresClient: false
|
|
79
|
+
}
|
|
80
|
+
];
|
|
70
81
|
function resolveConfig(options) {
|
|
82
|
+
const simple = options?.eventMode === "simple";
|
|
83
|
+
const bookingTypes = options?.bookingTypes ?? (simple ? SIMPLE_EVENT_BOOKING_TYPES : DEFAULT_BOOKING_TYPES);
|
|
84
|
+
const capabilities = {
|
|
85
|
+
title: bookingTypes.some((bt) => bt.fields.title === true),
|
|
86
|
+
client: bookingTypes.some((bt) => bt.fields.client),
|
|
87
|
+
professional: bookingTypes.some((bt) => bt.fields.professional),
|
|
88
|
+
services: bookingTypes.some((bt) => bt.fields.services),
|
|
89
|
+
location: bookingTypes.some((bt) => bt.fields.location),
|
|
90
|
+
status: bookingTypes.some((bt) => bt.fields.status)
|
|
91
|
+
};
|
|
71
92
|
return {
|
|
72
93
|
modules: {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
94
|
+
// Off-unless-explicit in simple mode (`?? !simple`); default-on in full.
|
|
95
|
+
workingHours: options?.modules?.workingHours ?? !simple,
|
|
96
|
+
confirmations: options?.modules?.confirmations ?? !simple,
|
|
97
|
+
conflictDetection: options?.modules?.conflictDetection ?? !simple,
|
|
76
98
|
dragAndDrop: options?.modules?.dragAndDrop !== false,
|
|
77
99
|
locationSelection: options?.modules?.locationSelection === true,
|
|
78
100
|
financial: options?.modules?.financial ?? !!options?.financialBridge
|
|
79
101
|
},
|
|
102
|
+
simpleMode: simple,
|
|
103
|
+
capabilities,
|
|
80
104
|
labels: { ...DEFAULT_LABELS, ...options?.labels },
|
|
81
105
|
currency: { ...DEFAULT_CURRENCY, ...options?.currency },
|
|
82
106
|
bookingKind: options?.bookingKind ?? "appointment",
|
|
83
107
|
orderKind: options?.orderKind ?? "service_order",
|
|
84
108
|
scheduleKind: options?.scheduleKind ?? "working_hours",
|
|
85
109
|
statuses: options?.statuses ?? DEFAULT_STATUSES,
|
|
86
|
-
bookingTypes
|
|
110
|
+
bookingTypes,
|
|
87
111
|
businessHours: options?.businessHours ?? { startTime: "07:00", endTime: "21:00" },
|
|
88
112
|
slotDuration: options?.slotDuration ?? 30,
|
|
89
113
|
professionalKind: options?.professionalKind ?? "staff",
|
|
@@ -91,6 +115,8 @@ function resolveConfig(options) {
|
|
|
91
115
|
autoCreateOrder: options?.autoCreateOrder !== false,
|
|
92
116
|
locations: options?.locations ?? [],
|
|
93
117
|
confirmationChannels: options?.confirmationChannels ?? [],
|
|
118
|
+
cancellationDetails: options?.cancellationDetails,
|
|
119
|
+
onBookingCreated: options?.onBookingCreated,
|
|
94
120
|
contactLookup: options?.contactLookup,
|
|
95
121
|
clientEntityDef: options?.clientEntityDef,
|
|
96
122
|
serviceLookup: options?.serviceLookup,
|
|
@@ -418,7 +444,7 @@ function toDateLocale(locale) {
|
|
|
418
444
|
}
|
|
419
445
|
function MiniCalendar({ selectedDate, onDateSelect, onMonthChange }) {
|
|
420
446
|
const locale = getCurrentLocale();
|
|
421
|
-
const [viewMonth, setViewMonth] =
|
|
447
|
+
const [viewMonth, setViewMonth] = React12.useState(() => new Date(selectedDate.getFullYear(), selectedDate.getMonth(), 1));
|
|
422
448
|
const monthLabel = viewMonth.toLocaleDateString(toDateLocale(locale), { month: "long", year: "numeric" });
|
|
423
449
|
const dayLabels = DAY_LABELS[locale] ?? DAY_LABELS.en;
|
|
424
450
|
const weeks = useMemo(() => {
|
|
@@ -707,7 +733,7 @@ var ICON_COLORS = {
|
|
|
707
733
|
bank_transfer: "text-warning",
|
|
708
734
|
check: "text-muted-foreground"
|
|
709
735
|
};
|
|
710
|
-
function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPaymentChange, formatCurrency, inline }) {
|
|
736
|
+
function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPaymentChange, formatCurrency: formatCurrency2, inline }) {
|
|
711
737
|
const t = useTranslation();
|
|
712
738
|
const config = useAgendaConfig();
|
|
713
739
|
const bridge = config.financialBridge;
|
|
@@ -789,7 +815,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
|
|
|
789
815
|
!loading && isPaid && justPaid && /* @__PURE__ */ jsxs("div", { className: "text-center py-8 space-y-2 animate-in fade-in zoom-in-95 duration-300", children: [
|
|
790
816
|
/* @__PURE__ */ 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__ */ jsx(Check, { className: "h-7 w-7 text-success" }) }),
|
|
791
817
|
/* @__PURE__ */ jsx("p", { className: "text-sm font-semibold text-success", children: t("agenda.payment.checkoutComplete") }),
|
|
792
|
-
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children:
|
|
818
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: formatCurrency2(detail.totalAmount) })
|
|
793
819
|
] }),
|
|
794
820
|
!loading && detail && detail.status !== "none" && !justPaid && /* @__PURE__ */ jsxs("div", { className: "space-y-3 animate-in fade-in duration-300", children: [
|
|
795
821
|
detail.movements.length > 0 ? /* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: detail.movements.map((mov) => {
|
|
@@ -821,21 +847,21 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
|
|
|
821
847
|
] }),
|
|
822
848
|
isPartial && /* @__PURE__ */ 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: [
|
|
823
849
|
"! ",
|
|
824
|
-
|
|
850
|
+
formatCurrency2(mov.paidAmount),
|
|
825
851
|
"/",
|
|
826
|
-
|
|
852
|
+
formatCurrency2(mov.amount)
|
|
827
853
|
] }),
|
|
828
854
|
isOverdue && /* @__PURE__ */ 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: [
|
|
829
855
|
"! ",
|
|
830
856
|
t("agenda.payment.overdue")
|
|
831
857
|
] }),
|
|
832
858
|
mov.status === "pending" && /* @__PURE__ */ 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") }),
|
|
833
|
-
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children:
|
|
859
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-semibold", children: formatCurrency2(mov.paidAmount || mov.amount) })
|
|
834
860
|
] })
|
|
835
861
|
] }, mov.id);
|
|
836
862
|
}) }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm px-1", children: [
|
|
837
863
|
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: t("agenda.payment.total") }),
|
|
838
|
-
/* @__PURE__ */ jsx("span", { className: "font-semibold", children:
|
|
864
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold", children: formatCurrency2(detail.totalAmount) })
|
|
839
865
|
] }),
|
|
840
866
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
841
867
|
detail.referenceNumber && /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-mono text-muted-foreground", children: [
|
|
@@ -860,12 +886,12 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
|
|
|
860
886
|
/* @__PURE__ */ jsx("p", { className: "text-[11px] font-medium text-muted-foreground mb-2", children: t("agenda.payment.serviceSummary") }),
|
|
861
887
|
/* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: services.map((svc, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm py-1", children: [
|
|
862
888
|
/* @__PURE__ */ jsx("span", { className: "truncate", children: svc.name }),
|
|
863
|
-
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground", children:
|
|
889
|
+
/* @__PURE__ */ jsx("span", { className: "shrink-0 text-muted-foreground", children: formatCurrency2(svc.price) })
|
|
864
890
|
] }, i)) })
|
|
865
891
|
] }),
|
|
866
892
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between text-sm font-semibold pt-2 border-t", children: [
|
|
867
893
|
/* @__PURE__ */ jsx("span", { children: t("agenda.payment.total") }),
|
|
868
|
-
/* @__PURE__ */ jsx("span", { children:
|
|
894
|
+
/* @__PURE__ */ jsx("span", { children: formatCurrency2(orderTotal) })
|
|
869
895
|
] }),
|
|
870
896
|
/* @__PURE__ */ jsxs("div", { children: [
|
|
871
897
|
/* @__PURE__ */ jsx("p", { className: "text-[11px] font-medium text-muted-foreground mb-2", children: t("agenda.payment.paymentMethod") }),
|
|
@@ -940,7 +966,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
|
|
|
940
966
|
children: Array.from({ length: 12 }, (_, i) => i + 1).map((n) => /* @__PURE__ */ jsxs("option", { value: n, children: [
|
|
941
967
|
n,
|
|
942
968
|
"x ",
|
|
943
|
-
n > 1 ?
|
|
969
|
+
n > 1 ? formatCurrency2(orderTotal / n) : ""
|
|
944
970
|
] }, n))
|
|
945
971
|
}
|
|
946
972
|
)
|
|
@@ -969,7 +995,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
|
|
|
969
995
|
onClick: handleCheckout,
|
|
970
996
|
disabled: processing || !selectedTypeId,
|
|
971
997
|
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",
|
|
972
|
-
children: processing ? t("agenda.payment.checkingOut") : `${t("agenda.payment.checkout")} ${
|
|
998
|
+
children: processing ? t("agenda.payment.checkingOut") : `${t("agenda.payment.checkout")} ${formatCurrency2(orderTotal)}`
|
|
973
999
|
}
|
|
974
1000
|
)
|
|
975
1001
|
] })
|
|
@@ -1232,7 +1258,7 @@ function LocationSelect({ value, onChange, locations }) {
|
|
|
1232
1258
|
)) })
|
|
1233
1259
|
] });
|
|
1234
1260
|
}
|
|
1235
|
-
function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose, onCreated }) {
|
|
1261
|
+
function AppointmentModal({ open, mode, bookingId, prefill, initialTab, externalSource, onClose, onCreated }) {
|
|
1236
1262
|
const t = useTranslation();
|
|
1237
1263
|
const locale = getCurrentLocale();
|
|
1238
1264
|
const config = useAgendaConfig();
|
|
@@ -1245,8 +1271,10 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
|
|
|
1245
1271
|
const agendaDateRange = useAgendaStore((s) => s.dateRange);
|
|
1246
1272
|
const professionals = useAgendaStore((s) => s.professionals);
|
|
1247
1273
|
const storeLocations = useAgendaStore((s) => s.locations);
|
|
1274
|
+
const defaultBookingType = config.bookingTypes[0]?.id ?? "appointment";
|
|
1248
1275
|
const [loading, setLoading] = useState(mode === "edit");
|
|
1249
|
-
const [bookingType, setBookingType] = useState(prefill?.kind ??
|
|
1276
|
+
const [bookingType, setBookingType] = useState(prefill?.kind ?? defaultBookingType);
|
|
1277
|
+
const [title, setTitle] = useState("");
|
|
1250
1278
|
const [clientId, setClientId] = useState(prefill?.clientId ?? "");
|
|
1251
1279
|
const [clientSearch, setClientSearch] = useState("");
|
|
1252
1280
|
const [clientPhone, setClientPhone] = useState("");
|
|
@@ -1291,6 +1319,10 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
|
|
|
1291
1319
|
const [clientData, setClientData] = useState(null);
|
|
1292
1320
|
const [clientDataLoading, setClientDataLoading] = useState(false);
|
|
1293
1321
|
const [clientEditing, setClientEditing] = useState(false);
|
|
1322
|
+
const [cancellationReasons, setCancellationReasons] = useState([]);
|
|
1323
|
+
const [cancellationReasonId, setCancellationReasonId] = useState("");
|
|
1324
|
+
const [cancellationNotes, setCancellationNotes] = useState("");
|
|
1325
|
+
const [cancellationReasonsLoading, setCancellationReasonsLoading] = useState(false);
|
|
1294
1326
|
const activeType = config.bookingTypes.find((bt) => bt.id === bookingType) ?? config.bookingTypes[0];
|
|
1295
1327
|
useEffect(() => {
|
|
1296
1328
|
if (!open) return;
|
|
@@ -1311,7 +1343,8 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
|
|
|
1311
1343
|
setNewClientPhone("");
|
|
1312
1344
|
setNewClientEmail("");
|
|
1313
1345
|
setCreatingClient(false);
|
|
1314
|
-
setBookingType(prefill?.kind ??
|
|
1346
|
+
setBookingType(prefill?.kind ?? defaultBookingType);
|
|
1347
|
+
setTitle("");
|
|
1315
1348
|
setEditingDate(false);
|
|
1316
1349
|
setEditingTime(false);
|
|
1317
1350
|
setConfirmingDelete(false);
|
|
@@ -1323,6 +1356,9 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
|
|
|
1323
1356
|
setClientData(null);
|
|
1324
1357
|
setClientDataLoading(false);
|
|
1325
1358
|
setClientEditing(false);
|
|
1359
|
+
setCancellationReasonId("");
|
|
1360
|
+
setCancellationNotes("");
|
|
1361
|
+
setCancellationReasonsLoading(false);
|
|
1326
1362
|
setModalTab(initialTab || "appointment");
|
|
1327
1363
|
if (!prefill?.professionalId && professionals.length === 1) setProfessionalId(professionals[0].id);
|
|
1328
1364
|
setDate(() => {
|
|
@@ -1345,7 +1381,8 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
|
|
|
1345
1381
|
setLoading(false);
|
|
1346
1382
|
return;
|
|
1347
1383
|
}
|
|
1348
|
-
setBookingType(b.kind ??
|
|
1384
|
+
setBookingType(b.kind ?? defaultBookingType);
|
|
1385
|
+
setTitle(b.clientName ?? "");
|
|
1349
1386
|
setClientId(b.clientId ?? "");
|
|
1350
1387
|
setClientSearch(b.clientName ?? "");
|
|
1351
1388
|
setProfessionalId(b.professionalId ?? "");
|
|
@@ -1367,6 +1404,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
|
|
|
1367
1404
|
setEditPaymentStatus(b.paymentStatus ?? "none");
|
|
1368
1405
|
const svcSnap = b.services ? b.services.map((s) => ({ id: s.serviceId ?? "", dur: s.durationMinutes, price: s.price })) : [];
|
|
1369
1406
|
initialSnapshot.current = JSON.stringify({
|
|
1407
|
+
title: b.clientName ?? "",
|
|
1370
1408
|
clientId: b.clientId ?? "",
|
|
1371
1409
|
professionalId: b.professionalId ?? "",
|
|
1372
1410
|
locationId: b.locationId ?? "",
|
|
@@ -1374,6 +1412,8 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
|
|
|
1374
1412
|
startTime: `${String(new Date(b.startsAt).getHours()).padStart(2, "0")}:${String(new Date(b.startsAt).getMinutes()).padStart(2, "0")}`,
|
|
1375
1413
|
status: b.status,
|
|
1376
1414
|
notes: b.notes ?? "",
|
|
1415
|
+
cancellationReasonId: "",
|
|
1416
|
+
cancellationNotes: "",
|
|
1377
1417
|
services: svcSnap
|
|
1378
1418
|
});
|
|
1379
1419
|
setLoading(false);
|
|
@@ -1398,6 +1438,11 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
|
|
|
1398
1438
|
setClientDataLoading(false);
|
|
1399
1439
|
}).catch(() => setClientDataLoading(false));
|
|
1400
1440
|
}, [modalTab, clientId]);
|
|
1441
|
+
useEffect(() => {
|
|
1442
|
+
if (!open || status !== "cancelled" || !config.cancellationDetails || cancellationReasons.length > 0) return;
|
|
1443
|
+
setCancellationReasonsLoading(true);
|
|
1444
|
+
config.cancellationDetails.listReasons().then((reasons) => setCancellationReasons(reasons)).catch(() => setCancellationReasons([])).finally(() => setCancellationReasonsLoading(false));
|
|
1445
|
+
}, [open, status, config.cancellationDetails, cancellationReasons.length]);
|
|
1401
1446
|
useEffect(() => {
|
|
1402
1447
|
if (!open || !config.serviceLookup) return;
|
|
1403
1448
|
const lookup = config.serviceLookup;
|
|
@@ -1497,10 +1542,11 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
|
|
|
1497
1542
|
locationId: locationId || void 0,
|
|
1498
1543
|
startsAt,
|
|
1499
1544
|
notes: notes || void 0,
|
|
1545
|
+
title: activeType.fields.title ? title.trim() : void 0,
|
|
1500
1546
|
services: services.map((s) => ({ serviceId: s.serviceId, name: s.name, durationMinutes: s.durationMinutes, price: s.price }))
|
|
1501
1547
|
});
|
|
1502
1548
|
onClose();
|
|
1503
|
-
onCreated?.(newBooking.id);
|
|
1549
|
+
onCreated?.(newBooking.id, externalSource);
|
|
1504
1550
|
} else if (bookingId) {
|
|
1505
1551
|
await updateBooking(bookingId, {
|
|
1506
1552
|
clientId,
|
|
@@ -1509,8 +1555,17 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
|
|
|
1509
1555
|
startsAt,
|
|
1510
1556
|
notes: notes || void 0,
|
|
1511
1557
|
status,
|
|
1558
|
+
title: activeType.fields.title ? title.trim() : void 0,
|
|
1512
1559
|
services: services.map((s) => ({ serviceId: s.serviceId, name: s.name, durationMinutes: s.durationMinutes, price: s.price }))
|
|
1513
1560
|
});
|
|
1561
|
+
if (status === "cancelled" && config.cancellationDetails && cancellationReasonId) {
|
|
1562
|
+
await config.cancellationDetails.saveCancellationDetails({
|
|
1563
|
+
bookingId,
|
|
1564
|
+
reasonId: cancellationReasonId,
|
|
1565
|
+
notes: cancellationNotes.trim() || void 0,
|
|
1566
|
+
cancelledAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1514
1569
|
onClose();
|
|
1515
1570
|
}
|
|
1516
1571
|
} catch {
|
|
@@ -1528,12 +1583,15 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
|
|
|
1528
1583
|
setDeleting(false);
|
|
1529
1584
|
}
|
|
1530
1585
|
const isPaidBooking = editPaymentStatus === "paid";
|
|
1586
|
+
const selectedCancellationReason = cancellationReasons.find((reason) => reason.id === cancellationReasonId);
|
|
1587
|
+
const needsCancellationReason = mode === "edit" && status === "cancelled" && !!config.cancellationDetails;
|
|
1588
|
+
const needsCancellationNotes = needsCancellationReason && !!selectedCancellationReason?.requiresNotes;
|
|
1531
1589
|
const formSnapshot = useMemo(
|
|
1532
|
-
() => JSON.stringify({ clientId, professionalId, locationId, date, startTime, status, notes, services: services.map((s) => ({ id: s.serviceId, dur: s.durationMinutes, price: s.price })) }),
|
|
1533
|
-
[clientId, professionalId, locationId, date, startTime, status, notes, services]
|
|
1590
|
+
() => 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 })) }),
|
|
1591
|
+
[title, clientId, professionalId, locationId, date, startTime, status, notes, cancellationReasonId, cancellationNotes, services]
|
|
1534
1592
|
);
|
|
1535
1593
|
const isDirty = mode === "create" || formSnapshot !== initialSnapshot.current;
|
|
1536
|
-
const canSubmit = (!activeType.requiresClient || clientId) && (!activeType.fields.professional || professionalId) && (!activeType.requiresServices || services.length > 0) && !conflict && !saving;
|
|
1594
|
+
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;
|
|
1537
1595
|
const showTabs = config.bookingTypes.length > 1;
|
|
1538
1596
|
return /* @__PURE__ */ jsx(Modal, { open, onOpenChange: (v) => {
|
|
1539
1597
|
if (!v) onClose();
|
|
@@ -1708,6 +1766,20 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
|
|
|
1708
1766
|
/* @__PURE__ */ jsx("div", { className: "h-8 flex-1 rounded-lg bg-muted animate-pulse" })
|
|
1709
1767
|
] }, i)) }),
|
|
1710
1768
|
!loading && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1769
|
+
activeType.fields.title && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1", children: [
|
|
1770
|
+
/* @__PURE__ */ jsx(Calendar, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
|
|
1771
|
+
/* @__PURE__ */ jsx(
|
|
1772
|
+
"input",
|
|
1773
|
+
{
|
|
1774
|
+
type: "text",
|
|
1775
|
+
value: title,
|
|
1776
|
+
onChange: (e) => setTitle(e.target.value),
|
|
1777
|
+
placeholder: t("agenda.appointment.addTitle"),
|
|
1778
|
+
autoFocus: mode === "create",
|
|
1779
|
+
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"
|
|
1780
|
+
}
|
|
1781
|
+
)
|
|
1782
|
+
] }),
|
|
1711
1783
|
activeType.fields.client && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1", children: [
|
|
1712
1784
|
/* @__PURE__ */ jsx(User, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
|
|
1713
1785
|
/* @__PURE__ */ jsx("div", { className: "flex-1", children: clientId && !quickCreate ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-md bg-muted/30 px-2.5 py-1.5 text-sm", children: [
|
|
@@ -1920,6 +1992,37 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, onClose,
|
|
|
1920
1992
|
/* @__PURE__ */ jsx(CircleDot, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
|
|
1921
1993
|
/* @__PURE__ */ jsx(StatusSelect, { value: status, onChange: setStatus, statuses: config.statuses, bookingDate: date })
|
|
1922
1994
|
] }),
|
|
1995
|
+
needsCancellationReason && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 py-1", children: [
|
|
1996
|
+
/* @__PURE__ */ jsx(Ban, { className: "h-4 w-4 mt-2 text-muted-foreground shrink-0" }),
|
|
1997
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-2", children: [
|
|
1998
|
+
/* @__PURE__ */ jsxs(
|
|
1999
|
+
"select",
|
|
2000
|
+
{
|
|
2001
|
+
value: cancellationReasonId,
|
|
2002
|
+
onChange: (event) => setCancellationReasonId(event.target.value),
|
|
2003
|
+
disabled: cancellationReasonsLoading,
|
|
2004
|
+
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",
|
|
2005
|
+
children: [
|
|
2006
|
+
/* @__PURE__ */ jsx("option", { value: "", children: cancellationReasonsLoading ? t("agenda.cancellation.loadingReasons") : t("agenda.cancellation.selectReason") }),
|
|
2007
|
+
cancellationReasons.map((reason) => /* @__PURE__ */ jsxs("option", { value: reason.id, children: [
|
|
2008
|
+
reason.label,
|
|
2009
|
+
reason.requiresNotes ? " *" : ""
|
|
2010
|
+
] }, reason.id))
|
|
2011
|
+
]
|
|
2012
|
+
}
|
|
2013
|
+
),
|
|
2014
|
+
/* @__PURE__ */ jsx(
|
|
2015
|
+
"textarea",
|
|
2016
|
+
{
|
|
2017
|
+
value: cancellationNotes,
|
|
2018
|
+
onChange: (event) => setCancellationNotes(event.target.value),
|
|
2019
|
+
placeholder: needsCancellationNotes ? t("agenda.cancellation.notesRequired") : t("agenda.cancellation.notesOptional"),
|
|
2020
|
+
rows: 2,
|
|
2021
|
+
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"
|
|
2022
|
+
}
|
|
2023
|
+
)
|
|
2024
|
+
] })
|
|
2025
|
+
] }),
|
|
1923
2026
|
!showNotes ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1", children: [
|
|
1924
2027
|
/* @__PURE__ */ jsx(FileText, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
|
|
1925
2028
|
/* @__PURE__ */ jsx(
|
|
@@ -2234,7 +2337,7 @@ function AppointmentPopover({ booking, position, onClose, onEdit }) {
|
|
|
2234
2337
|
/* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3", children: [
|
|
2235
2338
|
/* @__PURE__ */ jsx("span", { className: "mt-1.5 h-3 w-3 rounded shrink-0", style: { backgroundColor: color } }),
|
|
2236
2339
|
/* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
|
|
2237
|
-
/* @__PURE__ */ jsx(PersonLink, { personId: booking.clientId, name: booking.clientName ?? "Unknown", className: "text-base" }),
|
|
2340
|
+
config.capabilities.client ? /* @__PURE__ */ jsx(PersonLink, { personId: booking.clientId, name: booking.clientName ?? "Unknown", className: "text-base" }) : /* @__PURE__ */ jsx("p", { className: "text-base font-medium truncate", children: booking.clientName || t("agenda.appointment.untitled") }),
|
|
2238
2341
|
/* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground mt-0.5", children: [
|
|
2239
2342
|
fmtDate(booking.startsAt),
|
|
2240
2343
|
" \xB7 ",
|
|
@@ -2254,7 +2357,7 @@ function AppointmentPopover({ booking, position, onClose, onEdit }) {
|
|
|
2254
2357
|
] })
|
|
2255
2358
|
] }, svc.id)) })
|
|
2256
2359
|
] }),
|
|
2257
|
-
booking.professionalName && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
2360
|
+
config.capabilities.professional && booking.professionalName && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
2258
2361
|
/* @__PURE__ */ jsx(User, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
|
|
2259
2362
|
/* @__PURE__ */ jsx("span", { className: "text-sm", children: booking.professionalName })
|
|
2260
2363
|
] }),
|
|
@@ -2305,7 +2408,7 @@ function AppointmentPopover({ booking, position, onClose, onEdit }) {
|
|
|
2305
2408
|
)
|
|
2306
2409
|
] });
|
|
2307
2410
|
})(),
|
|
2308
|
-
/* @__PURE__ */ jsx("div", { className: "pt-2 border-t fayz-glass-divider", children: /* @__PURE__ */ jsx(
|
|
2411
|
+
config.capabilities.status && /* @__PURE__ */ jsx("div", { className: "pt-2 border-t fayz-glass-divider", children: /* @__PURE__ */ jsx(
|
|
2309
2412
|
PopoverStatusSelect,
|
|
2310
2413
|
{
|
|
2311
2414
|
value: booking.status,
|
|
@@ -2422,7 +2525,7 @@ function EventContextMenu({ booking, position, onClose, onEdit }) {
|
|
|
2422
2525
|
}
|
|
2423
2526
|
)
|
|
2424
2527
|
] }),
|
|
2425
|
-
/* @__PURE__ */ jsxs("div", { className: "border-t px-4 py-3", children: [
|
|
2528
|
+
config.capabilities.status && /* @__PURE__ */ jsxs("div", { className: "border-t px-4 py-3", children: [
|
|
2426
2529
|
/* @__PURE__ */ jsx("p", { className: "text-[11px] font-medium text-muted-foreground mb-2", children: t("agenda.contextMenu.changeStatus") }),
|
|
2427
2530
|
/* @__PURE__ */ jsx("div", { className: "flex items-center gap-1.5", children: config.statuses.map((s) => {
|
|
2428
2531
|
const available = isStatusAvailable(s, booking.startsAt);
|
|
@@ -2446,6 +2549,66 @@ function EventContextMenu({ booking, position, onClose, onEdit }) {
|
|
|
2446
2549
|
] })
|
|
2447
2550
|
] }) });
|
|
2448
2551
|
}
|
|
2552
|
+
function startOfDay(d) {
|
|
2553
|
+
const x = new Date(d);
|
|
2554
|
+
x.setHours(0, 0, 0, 0);
|
|
2555
|
+
return x;
|
|
2556
|
+
}
|
|
2557
|
+
function timeLabel(iso) {
|
|
2558
|
+
const d = new Date(iso);
|
|
2559
|
+
return `${String(d.getHours()).padStart(2, "0")}:${String(d.getMinutes()).padStart(2, "0")}`;
|
|
2560
|
+
}
|
|
2561
|
+
function AgendaMobileList({ bookings, statusColors, currencyCode, onSelect }) {
|
|
2562
|
+
const t = useTranslation();
|
|
2563
|
+
const locale = getCurrentLocale() === "pt-BR" ? "pt-BR" : "en";
|
|
2564
|
+
const groups = useMemo(() => {
|
|
2565
|
+
const todayStart = startOfDay(/* @__PURE__ */ new Date()).getTime();
|
|
2566
|
+
const sorted = bookings.filter((b) => !!b.startsAt).slice().sort((a, b) => a.startsAt < b.startsAt ? -1 : a.startsAt > b.startsAt ? 1 : 0);
|
|
2567
|
+
const map = /* @__PURE__ */ new Map();
|
|
2568
|
+
for (const b of sorted) {
|
|
2569
|
+
const day = startOfDay(new Date(b.startsAt));
|
|
2570
|
+
if (day.getTime() < todayStart) continue;
|
|
2571
|
+
const key = day.toISOString().slice(0, 10);
|
|
2572
|
+
const list = map.get(key) ?? [];
|
|
2573
|
+
list.push(b);
|
|
2574
|
+
map.set(key, list);
|
|
2575
|
+
}
|
|
2576
|
+
return Array.from(map.entries()).map(([key, items]) => ({ key, date: /* @__PURE__ */ new Date(key + "T00:00:00"), items }));
|
|
2577
|
+
}, [bookings]);
|
|
2578
|
+
function dayLabel(date) {
|
|
2579
|
+
const today = startOfDay(/* @__PURE__ */ new Date());
|
|
2580
|
+
const diff = Math.round((startOfDay(date).getTime() - today.getTime()) / 864e5);
|
|
2581
|
+
if (diff === 0) return t("agenda.list.today");
|
|
2582
|
+
if (diff === 1) return t("agenda.list.tomorrow");
|
|
2583
|
+
return date.toLocaleDateString(locale, { weekday: "long", day: "numeric", month: "long" });
|
|
2584
|
+
}
|
|
2585
|
+
if (groups.length === 0) {
|
|
2586
|
+
return /* @__PURE__ */ jsx("div", { className: "flex-1 flex flex-col items-center justify-center px-6 py-16 text-center text-muted-foreground", children: /* @__PURE__ */ jsx("p", { className: "text-sm", children: t("agenda.list.empty") }) });
|
|
2587
|
+
}
|
|
2588
|
+
return /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto px-3 py-3 space-y-5", children: groups.map((g) => /* @__PURE__ */ jsxs("div", { children: [
|
|
2589
|
+
/* @__PURE__ */ jsx("h3", { className: "px-1 pb-1.5 text-xs font-semibold uppercase tracking-wide text-muted-foreground", children: dayLabel(g.date) }),
|
|
2590
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-1.5", children: g.items.map((b) => {
|
|
2591
|
+
const color = statusColors[b.status] ?? "#6b7280";
|
|
2592
|
+
const hasAmount = typeof b.orderTotal === "number" && b.orderTotal > 0;
|
|
2593
|
+
return /* @__PURE__ */ jsxs(
|
|
2594
|
+
"button",
|
|
2595
|
+
{
|
|
2596
|
+
onClick: () => onSelect(b),
|
|
2597
|
+
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",
|
|
2598
|
+
children: [
|
|
2599
|
+
/* @__PURE__ */ jsx("span", { className: "h-2.5 w-2.5 shrink-0 rounded-full", style: { backgroundColor: color } }),
|
|
2600
|
+
/* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
2601
|
+
/* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-foreground", children: b.clientName ?? "\u2014" }),
|
|
2602
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: timeLabel(b.startsAt) })
|
|
2603
|
+
] }),
|
|
2604
|
+
hasAmount && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-sm font-semibold tabular-nums text-foreground", children: formatCurrency(b.orderTotal, currencyCode) })
|
|
2605
|
+
]
|
|
2606
|
+
},
|
|
2607
|
+
b.id
|
|
2608
|
+
);
|
|
2609
|
+
}) })
|
|
2610
|
+
] }, g.key)) });
|
|
2611
|
+
}
|
|
2449
2612
|
function bookingToEvent(booking, statusColors) {
|
|
2450
2613
|
const color = statusColors[booking.status] ?? "#6b7280";
|
|
2451
2614
|
const serviceNames = booking.services?.map((s) => s.name).join(", ") ?? "";
|
|
@@ -2568,11 +2731,37 @@ function CalendarSkeleton({ profCount = 2 }) {
|
|
|
2568
2731
|
] }, h)) })
|
|
2569
2732
|
] });
|
|
2570
2733
|
}
|
|
2571
|
-
var
|
|
2734
|
+
var RESOURCE_VIEW_OPTIONS = [
|
|
2572
2735
|
{ key: "resourceTimeGridDay", labelKey: "agenda.calendar.viewDay" },
|
|
2573
2736
|
{ key: "resourceTimeGridWeek", labelKey: "agenda.calendar.viewWeek" },
|
|
2574
2737
|
{ key: "dayGridMonth", labelKey: "agenda.calendar.viewMonth" }
|
|
2575
2738
|
];
|
|
2739
|
+
var PLAIN_VIEW_OPTIONS = [
|
|
2740
|
+
{ key: "timeGridDay", labelKey: "agenda.calendar.viewDay" },
|
|
2741
|
+
{ key: "timeGridWeek", labelKey: "agenda.calendar.viewWeek" },
|
|
2742
|
+
{ key: "dayGridMonth", labelKey: "agenda.calendar.viewMonth" },
|
|
2743
|
+
{ key: "listMonth", labelKey: "agenda.calendar.viewList" }
|
|
2744
|
+
];
|
|
2745
|
+
var MOBILE_LIST_VIEW = "__mobileList__";
|
|
2746
|
+
function viewForNoResources(view) {
|
|
2747
|
+
return view.startsWith("resource") ? "dayGridMonth" : view;
|
|
2748
|
+
}
|
|
2749
|
+
function viewForResources(view) {
|
|
2750
|
+
if (view === "timeGridWeek") return "resourceTimeGridWeek";
|
|
2751
|
+
if (view === "timeGridDay") return "resourceTimeGridDay";
|
|
2752
|
+
return view;
|
|
2753
|
+
}
|
|
2754
|
+
function useIsMobile() {
|
|
2755
|
+
const [isMobile, setIsMobile] = useState(() => typeof window !== "undefined" && window.matchMedia("(max-width: 767px)").matches);
|
|
2756
|
+
useEffect(() => {
|
|
2757
|
+
if (typeof window === "undefined") return;
|
|
2758
|
+
const mq = window.matchMedia("(max-width: 767px)");
|
|
2759
|
+
const onChange = () => setIsMobile(mq.matches);
|
|
2760
|
+
mq.addEventListener("change", onChange);
|
|
2761
|
+
return () => mq.removeEventListener("change", onChange);
|
|
2762
|
+
}, []);
|
|
2763
|
+
return isMobile;
|
|
2764
|
+
}
|
|
2576
2765
|
function CalendarView() {
|
|
2577
2766
|
injectCalendarStyles();
|
|
2578
2767
|
const config = useAgendaConfig();
|
|
@@ -2605,6 +2794,18 @@ function CalendarView() {
|
|
|
2605
2794
|
const _openModal = useAgendaStore((s) => s.openAppointmentModal);
|
|
2606
2795
|
const closeModal = useAgendaStore((s) => s.closeAppointmentModal);
|
|
2607
2796
|
const modalState = useAgendaStore((s) => s.appointmentModal);
|
|
2797
|
+
const isMobile = useIsMobile();
|
|
2798
|
+
const resourceCapable = professionals.length > 0;
|
|
2799
|
+
const viewOptions = resourceCapable ? RESOURCE_VIEW_OPTIONS : PLAIN_VIEW_OPTIONS;
|
|
2800
|
+
const [mobileView, setMobileView] = useState(MOBILE_LIST_VIEW);
|
|
2801
|
+
const mobileViewOptions = useMemo(
|
|
2802
|
+
() => [
|
|
2803
|
+
...viewOptions.filter((o) => o.key !== "listMonth"),
|
|
2804
|
+
{ key: MOBILE_LIST_VIEW, labelKey: "agenda.calendar.viewList" }
|
|
2805
|
+
],
|
|
2806
|
+
[viewOptions]
|
|
2807
|
+
);
|
|
2808
|
+
const mobileGridActive = isMobile && mobileView !== MOBILE_LIST_VIEW;
|
|
2608
2809
|
const [selectedBooking, setSelectedBooking] = useState(null);
|
|
2609
2810
|
const [popoverAnchor, setPopoverAnchor] = useState(null);
|
|
2610
2811
|
const [popoverVisible, _setPopoverVisible] = useState(false);
|
|
@@ -2644,6 +2845,24 @@ function CalendarView() {
|
|
|
2644
2845
|
fetchSchedules();
|
|
2645
2846
|
if (config.modules.locationSelection) fetchLocations();
|
|
2646
2847
|
}, []);
|
|
2848
|
+
useEffect(() => {
|
|
2849
|
+
const api = calendarRef.current?.getApi();
|
|
2850
|
+
if (!api) return;
|
|
2851
|
+
if (!resourceCapable && currentView.startsWith("resource")) {
|
|
2852
|
+
api.changeView("dayGridMonth");
|
|
2853
|
+
} else if (resourceCapable && (currentView === "timeGridWeek" || currentView === "timeGridDay")) {
|
|
2854
|
+
api.changeView(viewForResources(currentView));
|
|
2855
|
+
}
|
|
2856
|
+
}, [resourceCapable, currentView]);
|
|
2857
|
+
useEffect(() => {
|
|
2858
|
+
if (!isMobile) return;
|
|
2859
|
+
const start = /* @__PURE__ */ new Date();
|
|
2860
|
+
start.setHours(0, 0, 0, 0);
|
|
2861
|
+
start.setDate(start.getDate() - 1);
|
|
2862
|
+
const end = new Date(start);
|
|
2863
|
+
end.setDate(end.getDate() + 91);
|
|
2864
|
+
fetchBookings({ start: start.toISOString(), end: end.toISOString() });
|
|
2865
|
+
}, [isMobile, fetchBookings]);
|
|
2647
2866
|
const resources = useMemo(() => {
|
|
2648
2867
|
const filtered = selectedProfIds.length > 0 ? professionals.filter((p) => selectedProfIds.includes(p.id)) : professionals;
|
|
2649
2868
|
return filtered.map((p) => ({
|
|
@@ -2668,6 +2887,8 @@ function CalendarView() {
|
|
|
2668
2887
|
[schedules, professionals, visibleRange, effectiveBusinessHours, locationMap]
|
|
2669
2888
|
);
|
|
2670
2889
|
const events = useMemo(() => [...bookingEvents, ...availabilityEvents], [bookingEvents, availabilityEvents]);
|
|
2890
|
+
const storedView = prefs.get("calendarView");
|
|
2891
|
+
const effectiveInitialView = resourceCapable ? viewForResources(storedView) : viewForNoResources(storedView);
|
|
2671
2892
|
const handleDatesSet = useCallback((arg) => {
|
|
2672
2893
|
setCalendarTitle(arg.view.title);
|
|
2673
2894
|
setView(arg.view.type);
|
|
@@ -2784,12 +3005,12 @@ function CalendarView() {
|
|
|
2784
3005
|
const locId = schedule?.locationId ?? schedule?.metadata?.locationId;
|
|
2785
3006
|
openModal("create", { prefill: { startsAt: arg.startStr, endsAt: arg.endStr, professionalId, locationId: locId || void 0 } });
|
|
2786
3007
|
};
|
|
2787
|
-
if (!schedule) {
|
|
3008
|
+
if (config.modules.workingHours && !schedule) {
|
|
2788
3009
|
setScheduleConfirm({ onConfirm: proceed });
|
|
2789
3010
|
return;
|
|
2790
3011
|
}
|
|
2791
3012
|
proceed();
|
|
2792
|
-
}, [openModal, findScheduleForSlot]);
|
|
3013
|
+
}, [openModal, findScheduleForSlot, config.modules.workingHours]);
|
|
2793
3014
|
const handleEventDrop = useCallback(async (arg) => {
|
|
2794
3015
|
const booking = arg.event.extendedProps.booking;
|
|
2795
3016
|
try {
|
|
@@ -2802,6 +3023,12 @@ function CalendarView() {
|
|
|
2802
3023
|
calendarRef.current?.getApi().changeView(v);
|
|
2803
3024
|
prefs.set("calendarView", v);
|
|
2804
3025
|
}, [prefs]);
|
|
3026
|
+
const handleMobileViewChange = useCallback((v) => {
|
|
3027
|
+
setMobileView(v);
|
|
3028
|
+
if (v === MOBILE_LIST_VIEW) return;
|
|
3029
|
+
setView(v);
|
|
3030
|
+
calendarRef.current?.getApi()?.changeView(v);
|
|
3031
|
+
}, [setView]);
|
|
2805
3032
|
const handlePrev = useCallback(() => calendarRef.current?.getApi().prev(), []);
|
|
2806
3033
|
const handleNext = useCallback(() => calendarRef.current?.getApi().next(), []);
|
|
2807
3034
|
const handleToday = useCallback(() => {
|
|
@@ -2887,8 +3114,21 @@ function CalendarView() {
|
|
|
2887
3114
|
}, [applyResourceStriping, currentView, visibleRange, bookings]);
|
|
2888
3115
|
useEffect(() => {
|
|
2889
3116
|
function handleOpenBooking(e) {
|
|
2890
|
-
const
|
|
2891
|
-
|
|
3117
|
+
const detail = e.detail ?? {};
|
|
3118
|
+
const externalSource = detail.source ? { type: String(detail.source), id: detail.waitlistId, waitlistId: detail.waitlistId } : void 0;
|
|
3119
|
+
if (detail.bookingId) {
|
|
3120
|
+
openModal("edit", { bookingId: detail.bookingId, externalSource });
|
|
3121
|
+
return;
|
|
3122
|
+
}
|
|
3123
|
+
openModal("create", {
|
|
3124
|
+
prefill: detail.prefill ?? {
|
|
3125
|
+
clientId: detail.clientId,
|
|
3126
|
+
professionalId: detail.professionalId,
|
|
3127
|
+
locationId: detail.locationId,
|
|
3128
|
+
startsAt: detail.startsAt
|
|
3129
|
+
},
|
|
3130
|
+
externalSource
|
|
3131
|
+
});
|
|
2892
3132
|
}
|
|
2893
3133
|
window.addEventListener("agenda:open-booking", handleOpenBooking);
|
|
2894
3134
|
return () => window.removeEventListener("agenda:open-booking", handleOpenBooking);
|
|
@@ -2962,110 +3202,135 @@ function CalendarView() {
|
|
|
2962
3202
|
}
|
|
2963
3203
|
),
|
|
2964
3204
|
/* @__PURE__ */ jsx(MiniCalendar, { selectedDate, onDateSelect: handleMiniDateSelect }),
|
|
2965
|
-
/* @__PURE__ */
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
/* @__PURE__ */
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
{
|
|
2989
|
-
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
2997
|
-
|
|
2998
|
-
|
|
2999
|
-
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
|
|
3004
|
-
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
3010
|
-
|
|
3011
|
-
|
|
3205
|
+
config.capabilities.professional && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3206
|
+
/* @__PURE__ */ jsx("div", { className: "h-px bg-border my-4" }),
|
|
3207
|
+
/* @__PURE__ */ jsxs(
|
|
3208
|
+
"button",
|
|
3209
|
+
{
|
|
3210
|
+
onClick: () => setProfSectionOpen((p) => !p),
|
|
3211
|
+
className: "flex items-center justify-between w-full text-xs font-medium text-muted-foreground hover:text-foreground py-1 transition-colors",
|
|
3212
|
+
children: [
|
|
3213
|
+
/* @__PURE__ */ jsx("span", { children: "Agendas" }),
|
|
3214
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: `h-3.5 w-3.5 transition-transform ${profSectionOpen ? "" : "-rotate-90"}` })
|
|
3215
|
+
]
|
|
3216
|
+
}
|
|
3217
|
+
),
|
|
3218
|
+
profSectionOpen && /* @__PURE__ */ jsx("div", { className: "mt-1.5 space-y-0.5", children: professionals.map((prof) => {
|
|
3219
|
+
const active = selectedProfIds.length === 0 || selectedProfIds.includes(prof.id);
|
|
3220
|
+
return /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2.5 rounded px-1 py-1 cursor-pointer hover:bg-muted/50 transition-colors", children: [
|
|
3221
|
+
/* @__PURE__ */ 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__ */ jsx("svg", { className: "h-2.5 w-2.5 text-primary-foreground", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M2 6l3 3 5-5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }),
|
|
3222
|
+
/* @__PURE__ */ jsx("span", { className: `text-xs truncate ${active ? "text-foreground" : "text-muted-foreground"}`, children: prof.name }),
|
|
3223
|
+
/* @__PURE__ */ jsx("input", { type: "checkbox", className: "sr-only", checked: active, onChange: () => toggleProfessional(prof.id) })
|
|
3224
|
+
] }, prof.id);
|
|
3225
|
+
}) })
|
|
3226
|
+
] }),
|
|
3227
|
+
config.capabilities.status && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3228
|
+
/* @__PURE__ */ jsx("div", { className: "h-px bg-border my-4" }),
|
|
3229
|
+
/* @__PURE__ */ jsxs(
|
|
3230
|
+
"button",
|
|
3231
|
+
{
|
|
3232
|
+
onClick: () => setStatusSectionOpen((p) => !p),
|
|
3233
|
+
className: "flex items-center justify-between w-full text-xs font-medium text-muted-foreground hover:text-foreground py-1 transition-colors",
|
|
3234
|
+
children: [
|
|
3235
|
+
/* @__PURE__ */ jsx("span", { children: "Status" }),
|
|
3236
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: `h-3.5 w-3.5 transition-transform ${statusSectionOpen ? "" : "-rotate-90"}` })
|
|
3237
|
+
]
|
|
3238
|
+
}
|
|
3239
|
+
),
|
|
3240
|
+
statusSectionOpen && /* @__PURE__ */ jsx("div", { className: "mt-1.5 space-y-0.5", children: config.statuses.map((st) => {
|
|
3241
|
+
const active = selectedStatuses.length === 0 || selectedStatuses.includes(st.value);
|
|
3242
|
+
return /* @__PURE__ */ jsxs("label", { className: "flex items-center gap-2.5 rounded px-1 py-1 cursor-pointer hover:bg-muted/50 transition-colors", children: [
|
|
3243
|
+
/* @__PURE__ */ jsx(
|
|
3244
|
+
"span",
|
|
3245
|
+
{
|
|
3246
|
+
className: "flex h-4 w-4 shrink-0 items-center justify-center rounded-sm border-2 transition-colors",
|
|
3247
|
+
style: { borderColor: active ? st.color : st.color + "40", backgroundColor: active ? st.color : "transparent" },
|
|
3248
|
+
children: active && /* @__PURE__ */ jsx("svg", { className: "h-2.5 w-2.5 text-white", viewBox: "0 0 12 12", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M2 6l3 3 5-5", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) })
|
|
3249
|
+
}
|
|
3250
|
+
),
|
|
3251
|
+
/* @__PURE__ */ jsx("span", { className: `text-xs ${active ? "text-foreground" : "text-muted-foreground"}`, children: st.label }),
|
|
3252
|
+
/* @__PURE__ */ jsx("input", { type: "checkbox", className: "sr-only", checked: active, onChange: () => toggleStatus(st.value) })
|
|
3253
|
+
] }, st.value);
|
|
3254
|
+
}) })
|
|
3255
|
+
] })
|
|
3012
3256
|
] }),
|
|
3013
3257
|
/* @__PURE__ */ jsxs("div", { className: "flex-1 flex flex-col min-w-0 overflow-hidden", children: [
|
|
3014
|
-
/* @__PURE__ */ jsxs("div", { className: "
|
|
3015
|
-
/* @__PURE__ */ jsxs("div", { className: "flex items-center
|
|
3016
|
-
/* @__PURE__ */
|
|
3017
|
-
|
|
3018
|
-
|
|
3019
|
-
|
|
3020
|
-
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
"
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3258
|
+
/* @__PURE__ */ jsxs("div", { className: "shrink-0 border-b", children: [
|
|
3259
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pl-4 pr-14 md:px-4 h-12", children: [
|
|
3260
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 min-w-0", children: [
|
|
3261
|
+
/* @__PURE__ */ jsx(
|
|
3262
|
+
"button",
|
|
3263
|
+
{
|
|
3264
|
+
onClick: () => openModal("create"),
|
|
3265
|
+
className: "md:hidden flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground",
|
|
3266
|
+
children: /* @__PURE__ */ jsx(Plus, { className: "h-4 w-4" })
|
|
3267
|
+
}
|
|
3268
|
+
),
|
|
3269
|
+
/* @__PURE__ */ jsxs("div", { className: `${mobileGridActive ? "flex" : "hidden"} md:flex items-center gap-3`, children: [
|
|
3270
|
+
/* @__PURE__ */ jsx(
|
|
3271
|
+
"button",
|
|
3272
|
+
{
|
|
3273
|
+
onClick: handleToday,
|
|
3274
|
+
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",
|
|
3275
|
+
children: t("agenda.toolbar.today")
|
|
3276
|
+
}
|
|
3277
|
+
),
|
|
3278
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5", children: [
|
|
3279
|
+
/* @__PURE__ */ jsx("button", { onClick: handlePrev, className: "flex h-7 w-7 items-center justify-center rounded-full hover:bg-muted transition-colors", children: /* @__PURE__ */ jsx(ChevronLeft, { className: "h-4 w-4" }) }),
|
|
3280
|
+
/* @__PURE__ */ jsx("button", { onClick: handleNext, className: "flex h-7 w-7 items-center justify-center rounded-full hover:bg-muted transition-colors", children: /* @__PURE__ */ jsx(ChevronRight, { className: "h-4 w-4" }) })
|
|
3281
|
+
] })
|
|
3282
|
+
] }),
|
|
3283
|
+
/* @__PURE__ */ jsx("h1", { className: "text-base sm:text-lg font-normal truncate", children: isMobile && !mobileGridActive ? t("agenda.calendar.viewAgenda") : calendarTitle })
|
|
3035
3284
|
] }),
|
|
3036
|
-
/* @__PURE__ */
|
|
3285
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 shrink-0", children: [
|
|
3286
|
+
/* @__PURE__ */ jsx("div", { className: "hidden md:flex items-center gap-1", children: viewOptions.map((opt) => /* @__PURE__ */ jsx(
|
|
3287
|
+
"button",
|
|
3288
|
+
{
|
|
3289
|
+
onClick: () => handleViewChange(opt.key),
|
|
3290
|
+
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"}`,
|
|
3291
|
+
children: t(opt.labelKey)
|
|
3292
|
+
},
|
|
3293
|
+
opt.key
|
|
3294
|
+
)) }),
|
|
3295
|
+
/* @__PURE__ */ jsx(
|
|
3296
|
+
Button,
|
|
3297
|
+
{
|
|
3298
|
+
variant: "outline",
|
|
3299
|
+
size: "icon",
|
|
3300
|
+
onClick: () => window.location.hash = "/settings/agenda",
|
|
3301
|
+
title: "Agenda Settings",
|
|
3302
|
+
className: "ml-1",
|
|
3303
|
+
children: /* @__PURE__ */ jsx(Settings, { className: "h-4 w-4" })
|
|
3304
|
+
}
|
|
3305
|
+
)
|
|
3306
|
+
] })
|
|
3037
3307
|
] }),
|
|
3038
|
-
/* @__PURE__ */
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
)) }),
|
|
3048
|
-
/* @__PURE__ */ jsx(
|
|
3049
|
-
Button,
|
|
3050
|
-
{
|
|
3051
|
-
variant: "outline",
|
|
3052
|
-
size: "icon",
|
|
3053
|
-
onClick: () => window.location.hash = "/settings/agenda",
|
|
3054
|
-
title: "Agenda Settings",
|
|
3055
|
-
className: "ml-1",
|
|
3056
|
-
children: /* @__PURE__ */ jsx(Settings, { className: "h-4 w-4" })
|
|
3057
|
-
}
|
|
3058
|
-
)
|
|
3059
|
-
] })
|
|
3308
|
+
/* @__PURE__ */ jsx("div", { className: "flex md:hidden items-center gap-1 overflow-x-auto px-3 pb-2", children: mobileViewOptions.map((opt) => /* @__PURE__ */ jsx(
|
|
3309
|
+
"button",
|
|
3310
|
+
{
|
|
3311
|
+
onClick: () => handleMobileViewChange(opt.key),
|
|
3312
|
+
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"}`,
|
|
3313
|
+
children: t(opt.labelKey)
|
|
3314
|
+
},
|
|
3315
|
+
opt.key
|
|
3316
|
+
)) })
|
|
3060
3317
|
] }),
|
|
3061
|
-
|
|
3318
|
+
isMobile && !mobileGridActive ? /* @__PURE__ */ jsx(
|
|
3319
|
+
AgendaMobileList,
|
|
3320
|
+
{
|
|
3321
|
+
bookings,
|
|
3322
|
+
statusColors,
|
|
3323
|
+
currencyCode: config.currency.code,
|
|
3324
|
+
onSelect: (b) => openModal("edit", { bookingId: b.id })
|
|
3325
|
+
}
|
|
3326
|
+
) : professionalsLoading && professionals.length === 0 ? /* @__PURE__ */ jsx(CalendarSkeleton, { profCount: 2 }) : /* @__PURE__ */ jsx("div", { ref: wrapperRef, className: "flex-1 overflow-hidden agenda-calendar", children: /* @__PURE__ */ jsx(
|
|
3062
3327
|
FullCalendar,
|
|
3063
3328
|
{
|
|
3064
3329
|
ref: calendarRef,
|
|
3065
3330
|
plugins: [dayGridPlugin, timeGridPlugin, resourceTimeGridPlugin, interactionPlugin, listPlugin],
|
|
3066
|
-
initialView:
|
|
3331
|
+
initialView: mobileGridActive ? mobileView : effectiveInitialView,
|
|
3067
3332
|
schedulerLicenseKey: "CC-Attribution-NonCommercial-NoDerivatives",
|
|
3068
|
-
resources,
|
|
3333
|
+
resources: resourceCapable ? resources : void 0,
|
|
3069
3334
|
events,
|
|
3070
3335
|
headerToolbar: false,
|
|
3071
3336
|
locale: fcLocale,
|
|
@@ -3155,8 +3420,14 @@ function CalendarView() {
|
|
|
3155
3420
|
bookingId: modalState.bookingId,
|
|
3156
3421
|
prefill: modalState.prefill,
|
|
3157
3422
|
initialTab: modalState.initialTab,
|
|
3423
|
+
externalSource: modalState.externalSource,
|
|
3158
3424
|
onClose: closeModal,
|
|
3159
|
-
onCreated: (newId) => {
|
|
3425
|
+
onCreated: (newId, source) => {
|
|
3426
|
+
if (config.onBookingCreated) {
|
|
3427
|
+
void Promise.resolve(config.onBookingCreated({ bookingId: newId, source })).catch((error) => {
|
|
3428
|
+
console.error("Failed to run agenda booking-created bridge", error);
|
|
3429
|
+
});
|
|
3430
|
+
}
|
|
3160
3431
|
setTimeout(() => {
|
|
3161
3432
|
const booking = bookings.find((b) => b.id === newId);
|
|
3162
3433
|
if (!booking) return;
|
|
@@ -3182,7 +3453,7 @@ function CalendarView() {
|
|
|
3182
3453
|
function AgendaPage({ config, provider: provider2, store }) {
|
|
3183
3454
|
const runtime = usePluginRuntimeOptional();
|
|
3184
3455
|
const tenantId = runtime?.context.tenant?.id;
|
|
3185
|
-
|
|
3456
|
+
React12.useEffect(() => {
|
|
3186
3457
|
setAgendaTenantId(tenantId);
|
|
3187
3458
|
return () => setAgendaTenantId(void 0);
|
|
3188
3459
|
}, [tenantId]);
|
|
@@ -3297,7 +3568,8 @@ async function hydrateCanonicalBookings(core, bookings) {
|
|
|
3297
3568
|
locationId: booking.location_id,
|
|
3298
3569
|
metadata: booking.metadata ?? {},
|
|
3299
3570
|
clientId: booking.party_id,
|
|
3300
|
-
|
|
3571
|
+
// Simple events (no party) fall back to the title stored in metadata.
|
|
3572
|
+
clientName: client?.name ?? booking.metadata?.title ?? null,
|
|
3301
3573
|
clientPhone: client?.phone ?? null,
|
|
3302
3574
|
clientEmail: client?.email ?? null,
|
|
3303
3575
|
clientAvatarUrl: client?.avatar_url ?? null,
|
|
@@ -3375,6 +3647,7 @@ function createSupabaseAgendaProvider() {
|
|
|
3375
3647
|
const { data: person } = await core.from("persons").select("name").eq("id", input.clientId).single();
|
|
3376
3648
|
contactName = person?.name;
|
|
3377
3649
|
}
|
|
3650
|
+
const eventTitle = input.title?.trim() || void 0;
|
|
3378
3651
|
const { data: order, error: orderErr } = await core.from("orders").insert({
|
|
3379
3652
|
tenant_id: tenantId,
|
|
3380
3653
|
kind: input.kind ?? "appointment",
|
|
@@ -3388,7 +3661,8 @@ function createSupabaseAgendaProvider() {
|
|
|
3388
3661
|
metadata: {
|
|
3389
3662
|
source: "agenda",
|
|
3390
3663
|
...serviceNames ? { serviceNames, itemsSummary: serviceNames } : {},
|
|
3391
|
-
...contactName ? { contactName } : {}
|
|
3664
|
+
...contactName ? { contactName } : {},
|
|
3665
|
+
...eventTitle ? { title: eventTitle } : {}
|
|
3392
3666
|
}
|
|
3393
3667
|
}).select("id").single();
|
|
3394
3668
|
if (orderErr) throw orderErr;
|
|
@@ -3406,7 +3680,8 @@ function createSupabaseAgendaProvider() {
|
|
|
3406
3680
|
metadata: {
|
|
3407
3681
|
source: "agenda",
|
|
3408
3682
|
...serviceNames ? { serviceNames, itemsSummary: serviceNames } : {},
|
|
3409
|
-
...contactName ? { contactName } : {}
|
|
3683
|
+
...contactName ? { contactName } : {},
|
|
3684
|
+
...eventTitle ? { title: eventTitle } : {}
|
|
3410
3685
|
}
|
|
3411
3686
|
}).select("id").single();
|
|
3412
3687
|
if (bookingErr) throw bookingErr;
|
|
@@ -3431,11 +3706,14 @@ function createSupabaseAgendaProvider() {
|
|
|
3431
3706
|
},
|
|
3432
3707
|
async updateBooking(id, data) {
|
|
3433
3708
|
const { core } = getClients();
|
|
3434
|
-
const { data: existing, error: existingErr } = await core.from("bookings").select("id, order_id, starts_at, assignee_id").eq("id", id).single();
|
|
3709
|
+
const { data: existing, error: existingErr } = await core.from("bookings").select("id, order_id, starts_at, assignee_id, metadata").eq("id", id).single();
|
|
3435
3710
|
if (existingErr) throw existingErr;
|
|
3436
3711
|
const updates = {};
|
|
3437
3712
|
if (data.startsAt) updates.starts_at = data.startsAt;
|
|
3438
3713
|
if (data.endsAt) updates.ends_at = data.endsAt;
|
|
3714
|
+
if (data.title !== void 0) {
|
|
3715
|
+
updates.metadata = { ...existing?.metadata ?? {}, title: data.title };
|
|
3716
|
+
}
|
|
3439
3717
|
if (data.status) {
|
|
3440
3718
|
updates.status = data.status;
|
|
3441
3719
|
}
|
|
@@ -3841,10 +4119,11 @@ function createSampleSchedules() {
|
|
|
3841
4119
|
}
|
|
3842
4120
|
return schedules;
|
|
3843
4121
|
}
|
|
3844
|
-
function createMockAgendaProvider() {
|
|
3845
|
-
const
|
|
3846
|
-
const
|
|
3847
|
-
const
|
|
4122
|
+
function createMockAgendaProvider(options) {
|
|
4123
|
+
const seed = options?.seed;
|
|
4124
|
+
const bookings = seed?.bookings ? [...seed.bookings] : createSampleBookings();
|
|
4125
|
+
const schedules = seed?.schedules ? [...seed.schedules] : createSampleSchedules();
|
|
4126
|
+
const professionals = seed?.professionals ? [...seed.professionals] : [...SAMPLE_PROFESSIONALS];
|
|
3848
4127
|
return {
|
|
3849
4128
|
async getLocations() {
|
|
3850
4129
|
return [];
|
|
@@ -3879,7 +4158,7 @@ function createMockAgendaProvider() {
|
|
|
3879
4158
|
locationId: input.locationId ?? null,
|
|
3880
4159
|
metadata: {},
|
|
3881
4160
|
clientId: input.clientId,
|
|
3882
|
-
clientName: "New Client",
|
|
4161
|
+
clientName: input.title?.trim() || "New Client",
|
|
3883
4162
|
clientPhone: null,
|
|
3884
4163
|
clientEmail: null,
|
|
3885
4164
|
clientAvatarUrl: null,
|
|
@@ -3925,6 +4204,7 @@ function createMockAgendaProvider() {
|
|
|
3925
4204
|
...existing,
|
|
3926
4205
|
...data.startsAt && { startsAt: data.startsAt },
|
|
3927
4206
|
...data.endsAt && { endsAt: data.endsAt },
|
|
4207
|
+
...data.title !== void 0 && { clientName: data.title },
|
|
3928
4208
|
...data.status && { status: data.status },
|
|
3929
4209
|
...data.notes !== void 0 && { notes: data.notes ?? null },
|
|
3930
4210
|
...data.professionalId && { professionalId: data.professionalId },
|
|
@@ -4233,7 +4513,7 @@ function createAgendaStore(provider2, financialBridge) {
|
|
|
4233
4513
|
set({ selectedDate: date });
|
|
4234
4514
|
},
|
|
4235
4515
|
openAppointmentModal(mode, options) {
|
|
4236
|
-
set({ appointmentModal: { open: true, mode, bookingId: options?.bookingId, prefill: options?.prefill, initialTab: options?.initialTab } });
|
|
4516
|
+
set({ appointmentModal: { open: true, mode, bookingId: options?.bookingId, prefill: options?.prefill, initialTab: options?.initialTab, externalSource: options?.externalSource } });
|
|
4237
4517
|
},
|
|
4238
4518
|
closeAppointmentModal() {
|
|
4239
4519
|
set((state) => ({ appointmentModal: { ...state.appointmentModal, open: false } }));
|
|
@@ -4424,8 +4704,12 @@ var en = {
|
|
|
4424
4704
|
"agenda.calendar.today": "Today",
|
|
4425
4705
|
"agenda.calendar.viewAgenda": "Agenda",
|
|
4426
4706
|
"agenda.calendar.viewDay": "Day",
|
|
4707
|
+
"agenda.calendar.viewList": "List",
|
|
4427
4708
|
"agenda.calendar.viewMonth": "Month",
|
|
4428
4709
|
"agenda.calendar.viewWeek": "Week",
|
|
4710
|
+
"agenda.list.today": "Today",
|
|
4711
|
+
"agenda.list.tomorrow": "Tomorrow",
|
|
4712
|
+
"agenda.list.empty": "No upcoming items",
|
|
4429
4713
|
"agenda.confirmations.confirmed": "Confirmed:",
|
|
4430
4714
|
"agenda.confirmations.empty": "No pending confirmations",
|
|
4431
4715
|
"agenda.confirmations.emptySubtitle": "All upcoming appointments have been confirmed",
|
|
@@ -4436,6 +4720,10 @@ var en = {
|
|
|
4436
4720
|
"agenda.confirmations.call": "Call",
|
|
4437
4721
|
"agenda.confirmations.markConfirmed": "Mark Confirmed",
|
|
4438
4722
|
"agenda.confirmations.markCancelled": "Mark Cancelled",
|
|
4723
|
+
"agenda.cancellation.loadingReasons": "Loading reasons...",
|
|
4724
|
+
"agenda.cancellation.notesOptional": "Cancellation notes (optional)",
|
|
4725
|
+
"agenda.cancellation.notesRequired": "Cancellation notes required",
|
|
4726
|
+
"agenda.cancellation.selectReason": "Select cancellation reason",
|
|
4439
4727
|
"agenda.contextMenu.changeStatus": "Change status",
|
|
4440
4728
|
"agenda.contextMenu.delete": "Delete",
|
|
4441
4729
|
"agenda.contextMenu.edit": "Edit",
|
|
@@ -4536,7 +4824,11 @@ var en = {
|
|
|
4536
4824
|
"agenda.appointment.addNotes": "Add notes",
|
|
4537
4825
|
"agenda.appointment.duration": "Duration (minutes)",
|
|
4538
4826
|
"agenda.appointment.price": "Price",
|
|
4827
|
+
"agenda.appointment.title": "Title",
|
|
4828
|
+
"agenda.appointment.addTitle": "Add a title",
|
|
4829
|
+
"agenda.appointment.untitled": "(no title)",
|
|
4539
4830
|
"agenda.bookingType.appointment": "Appointment",
|
|
4831
|
+
"agenda.bookingType.event": "Event",
|
|
4540
4832
|
"agenda.bookingType.task": "Task",
|
|
4541
4833
|
"agenda.bookingType.out_of_office": "Out of Office",
|
|
4542
4834
|
"agenda.bookingType.block": "Block",
|
|
@@ -4632,8 +4924,12 @@ var ptBR = {
|
|
|
4632
4924
|
"agenda.calendar.today": "Hoje",
|
|
4633
4925
|
"agenda.calendar.viewAgenda": "Agenda",
|
|
4634
4926
|
"agenda.calendar.viewDay": "Dia",
|
|
4927
|
+
"agenda.calendar.viewList": "Lista",
|
|
4635
4928
|
"agenda.calendar.viewMonth": "M\xEAs",
|
|
4636
4929
|
"agenda.calendar.viewWeek": "Semana",
|
|
4930
|
+
"agenda.list.today": "Hoje",
|
|
4931
|
+
"agenda.list.tomorrow": "Amanh\xE3",
|
|
4932
|
+
"agenda.list.empty": "Nenhum item pr\xF3ximo",
|
|
4637
4933
|
"agenda.confirmations.confirmed": "Confirmado:",
|
|
4638
4934
|
"agenda.confirmations.empty": "Nenhuma confirma\xE7\xE3o pendente",
|
|
4639
4935
|
"agenda.confirmations.emptySubtitle": "Todos os agendamentos futuros foram confirmados",
|
|
@@ -4644,6 +4940,10 @@ var ptBR = {
|
|
|
4644
4940
|
"agenda.confirmations.call": "Ligar",
|
|
4645
4941
|
"agenda.confirmations.markConfirmed": "Marcar Confirmado",
|
|
4646
4942
|
"agenda.confirmations.markCancelled": "Marcar Cancelado",
|
|
4943
|
+
"agenda.cancellation.loadingReasons": "Carregando motivos...",
|
|
4944
|
+
"agenda.cancellation.notesOptional": "Observa\xE7\xF5es do cancelamento (opcional)",
|
|
4945
|
+
"agenda.cancellation.notesRequired": "Observa\xE7\xF5es do cancelamento obrigat\xF3rias",
|
|
4946
|
+
"agenda.cancellation.selectReason": "Selecionar motivo do cancelamento",
|
|
4647
4947
|
"agenda.contextMenu.changeStatus": "Alterar status",
|
|
4648
4948
|
"agenda.contextMenu.delete": "Excluir",
|
|
4649
4949
|
"agenda.contextMenu.edit": "Editar",
|
|
@@ -4744,7 +5044,11 @@ var ptBR = {
|
|
|
4744
5044
|
"agenda.appointment.addNotes": "Adicionar observa\xE7\xF5es",
|
|
4745
5045
|
"agenda.appointment.duration": "Dura\xE7\xE3o (minutos)",
|
|
4746
5046
|
"agenda.appointment.price": "Pre\xE7o",
|
|
5047
|
+
"agenda.appointment.title": "T\xEDtulo",
|
|
5048
|
+
"agenda.appointment.addTitle": "Adicionar um t\xEDtulo",
|
|
5049
|
+
"agenda.appointment.untitled": "(sem t\xEDtulo)",
|
|
4747
5050
|
"agenda.bookingType.appointment": "Agendamento",
|
|
5051
|
+
"agenda.bookingType.event": "Evento",
|
|
4748
5052
|
"agenda.bookingType.task": "Tarefa",
|
|
4749
5053
|
"agenda.bookingType.out_of_office": "Aus\xEAncia",
|
|
4750
5054
|
"agenda.bookingType.block": "Bloqueio",
|
|
@@ -5180,6 +5484,7 @@ function createAgendaPlugin(options) {
|
|
|
5180
5484
|
() => createMockAgendaProvider()
|
|
5181
5485
|
);
|
|
5182
5486
|
const store = createAgendaStore(provider2, options?.financialBridge);
|
|
5487
|
+
const registries = [...agendaRegistries, ...options?.settingsRegistries ?? []];
|
|
5183
5488
|
setScheduleBlockConfig({
|
|
5184
5489
|
defaults: config.scheduleBlockDefaults,
|
|
5185
5490
|
showServices: !!config.serviceLookup,
|
|
@@ -5195,7 +5500,7 @@ function createAgendaPlugin(options) {
|
|
|
5195
5500
|
return locations;
|
|
5196
5501
|
} : void 0
|
|
5197
5502
|
});
|
|
5198
|
-
const PageComponent = () =>
|
|
5503
|
+
const PageComponent = () => React12.createElement(AgendaPage, { config, provider: provider2, store });
|
|
5199
5504
|
return {
|
|
5200
5505
|
id: "agenda",
|
|
5201
5506
|
name: config.labels.pageTitle,
|
|
@@ -5295,18 +5600,18 @@ function createAgendaPlugin(options) {
|
|
|
5295
5600
|
permission: { feature: "appointments", action: "read" }
|
|
5296
5601
|
}
|
|
5297
5602
|
],
|
|
5298
|
-
registries
|
|
5603
|
+
registries,
|
|
5299
5604
|
settings: [
|
|
5300
5605
|
{
|
|
5301
5606
|
id: "agenda",
|
|
5302
5607
|
label: "Agenda",
|
|
5303
5608
|
icon: "Calendar",
|
|
5304
|
-
// Standard layout: General | Integrations (Google Calendar addon) | Properties
|
|
5609
|
+
// Standard layout: General | Integrations (Google Calendar addon) | Properties.
|
|
5305
5610
|
component: (() => {
|
|
5306
|
-
const AgendaSettingsTab = () =>
|
|
5611
|
+
const AgendaSettingsTab = () => React12.createElement(PluginSettingsPanel, {
|
|
5307
5612
|
title: "Agenda",
|
|
5308
|
-
generalSettings:
|
|
5309
|
-
registries
|
|
5613
|
+
generalSettings: React12.createElement(AgendaGeneralSettings),
|
|
5614
|
+
registries,
|
|
5310
5615
|
hostPluginId: "agenda",
|
|
5311
5616
|
routeBase: "/settings/agenda"
|
|
5312
5617
|
});
|
|
@@ -5321,6 +5626,6 @@ function createAgendaPlugin(options) {
|
|
|
5321
5626
|
};
|
|
5322
5627
|
}
|
|
5323
5628
|
|
|
5324
|
-
export { bookingToEvent2 as bookingToEvent, computeCommissionAmount, createAgendaPlugin, createFinancialBridge, createGoogleCalendarPlugin, createGoogleCalendarProvider, eventToBookingPatch, googleCalendarConnector };
|
|
5629
|
+
export { bookingToEvent2 as bookingToEvent, computeCommissionAmount, createAgendaPlugin, createFinancialBridge, createGoogleCalendarPlugin, createGoogleCalendarProvider, createMockAgendaProvider, eventToBookingPatch, googleCalendarConnector };
|
|
5325
5630
|
//# sourceMappingURL=index.js.map
|
|
5326
5631
|
//# sourceMappingURL=index.js.map
|