@fayz-ai/plugin-agenda 0.10.2 → 0.11.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/index.js CHANGED
@@ -2,7 +2,7 @@ import { createMockAgendaProvider, createSupabasePublicBookingProvider } from '.
2
2
  export { createMockAgendaProvider } from './chunk-ISLJXP7O.js';
3
3
  import React13, { useState, useEffect, useCallback, useMemo, useRef } from 'react';
4
4
  import { connectionStatus, createConnectionStore, getSupabaseClientOptional, getActiveTenantId, registerTranslations, registerMessageDefaults, createSafeDataProvider, connectorRuntimeToken, CONNECTOR_RUNTIME_TOKEN_HEADER, emitDataChanged, usePluginRuntimeOptional, useTranslation, emitDomainEvent, getCurrentLocale, useDataChanged, formatCurrency } from '@fayz-ai/core';
5
- import { createPluginContext, fayzConnectorOAuth, setScheduleBlockConfig, getScheduleBlockConfig, PluginSettingsPanel, dedup, useTenantPluginSettings, SettingsGroup, ToggleRow, SelectRow, usePluginPrefs, PersonLink, useLimitGuard, CrudFormPage, CrudDetailPage, ContactPicker, invalidateLimit } from '@fayz-ai/admin';
5
+ import { createPluginContext, fayzConnectorOAuth, setScheduleBlockConfig, getScheduleBlockConfig, PluginSettingsPanel, dedup, useTenantPluginSettings, SettingsGroup, ToggleRow, SelectRow, usePluginPrefs, PersonLink, useLimitGuard, useLookupCreate, RightRailPage, CrudFormPage, CrudDetailPage, ContactPicker, invalidateLimit } from '@fayz-ai/admin';
6
6
  import FullCalendar from '@fullcalendar/react';
7
7
  import dayGridPlugin from '@fullcalendar/daygrid';
8
8
  import timeGridPlugin from '@fullcalendar/timegrid';
@@ -13,7 +13,7 @@ import ptBrLocale from '@fullcalendar/core/locales/pt-br';
13
13
  import { Loader2, AlertCircle, RefreshCw, CalendarPlus, X, CheckCircle2, Plus, ChevronDown, ChevronLeft, ChevronRight, Settings, Pencil, Trash2, Clock, User, MapPin, FileText, AlertTriangle, Check, DollarSign, HandCoins, Ban, CheckSquare, Calendar, Briefcase, CircleDot, FileCheck, ArrowRightLeft, CreditCard, QrCode, Banknote, ExternalLink } from 'lucide-react';
14
14
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
15
15
  import { createPortal } from 'react-dom';
16
- import { toast, Button, Checkbox, Badge, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, ConfirmDialog, Modal, ModalContent, ModalTitle, SearchCombobox, cn, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '@fayz-ai/ui';
16
+ import { toast, Button, Checkbox, Badge, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, ConfirmDialog, SearchCombobox, cn, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '@fayz-ai/ui';
17
17
  import { createStore } from 'zustand/vanilla';
18
18
 
19
19
  // src/lib/message-events.ts
@@ -962,7 +962,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
962
962
  children: /* @__PURE__ */ jsxs(
963
963
  "div",
964
964
  {
965
- className: "w-full max-w-md rounded-xl border bg-card shadow-2xl mx-4 transition-all duration-200 max-h-[90vh] overflow-y-auto",
965
+ className: "w-full max-w-md rounded-modal border bg-card shadow-2xl mx-4 transition-all duration-200 max-h-[90vh] overflow-y-auto",
966
966
  style: { opacity: visible ? 1 : 0, transform: visible ? "scale(1) translateY(0)" : "scale(0.95) translateY(8px)" },
967
967
  onClick: (e) => e.stopPropagation(),
968
968
  children: [
@@ -1269,6 +1269,17 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1269
1269
  const [cancellationNotes, setCancellationNotes] = useState("");
1270
1270
  const [cancellationReasonsLoading, setCancellationReasonsLoading] = useState(false);
1271
1271
  const activeType = config.bookingTypes.find((bt) => bt.id === bookingType) ?? config.bookingTypes[0];
1272
+ const requestKey = [
1273
+ mode,
1274
+ bookingId ?? "",
1275
+ initialTab ?? "",
1276
+ prefill?.startsAt ?? "",
1277
+ prefill?.endsAt ?? "",
1278
+ prefill?.professionalId ?? "",
1279
+ prefill?.locationId ?? "",
1280
+ prefill?.clientId ?? "",
1281
+ prefill?.kind ?? ""
1282
+ ].join("|");
1272
1283
  useEffect(() => {
1273
1284
  if (!open) return;
1274
1285
  setClientId(prefill?.clientId ?? "");
@@ -1312,7 +1323,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1312
1323
  }
1313
1324
  return "09:00";
1314
1325
  });
1315
- }, [open]);
1326
+ }, [open, requestKey]);
1316
1327
  useEffect(() => {
1317
1328
  if (!open || mode !== "edit" || !bookingId) return;
1318
1329
  setLoading(true);
@@ -1369,7 +1380,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1369
1380
  }).finally(() => setPaymentStatusLoading(false));
1370
1381
  }
1371
1382
  }).catch(() => setLoading(false));
1372
- }, [open, mode, bookingId]);
1383
+ }, [open, requestKey]);
1373
1384
  useEffect(() => {
1374
1385
  if (modalTab !== "client" || !clientId || !config.contactLookup || clientData) return;
1375
1386
  setClientDataLoading(true);
@@ -1444,6 +1455,21 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1444
1455
  function removeService(idx) {
1445
1456
  setServices((p) => p.filter((_, i) => i !== idx));
1446
1457
  }
1458
+ const serviceCreate = useLookupCreate(
1459
+ config.serviceLookup,
1460
+ (created) => {
1461
+ const duration = created.data?.duration_minutes || 60;
1462
+ setServices((p) => [...p, {
1463
+ serviceId: created.id,
1464
+ name: created.label,
1465
+ durationMinutes: duration,
1466
+ price: created.price ?? 0
1467
+ }]);
1468
+ setServiceSearch("");
1469
+ setServiceOptions([]);
1470
+ },
1471
+ { variant: "inline" }
1472
+ );
1447
1473
  async function handleSubmit(e) {
1448
1474
  e.preventDefault();
1449
1475
  if (!canSubmit) return;
@@ -1513,431 +1539,442 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
1513
1539
  const isDirty = mode === "create" || formSnapshot !== initialSnapshot.current;
1514
1540
  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;
1515
1541
  const showTabs = config.bookingTypes.length > 1;
1516
- return /* @__PURE__ */ jsx(Modal, { open, onOpenChange: (v) => {
1517
- if (!v) onClose();
1518
- }, children: /* @__PURE__ */ jsxs(ModalContent, { size: "lg", noPadding: true, children: [
1519
- /* @__PURE__ */ jsxs("div", { className: "px-5 pt-4 pb-3 shrink-0 space-y-3", children: [
1520
- /* @__PURE__ */ jsx(ModalTitle, { className: "sr-only", children: mode === "create" ? t2("agenda.appointment.new") : t2("agenda.appointment.edit") }),
1521
- showTabs && mode === "create" ? /* @__PURE__ */ jsx("div", { className: "flex gap-1 border-b fayz-glass-divider", children: config.bookingTypes.map((bt) => {
1522
- const label = t2(`agenda.bookingType.${bt.id}`) || bt.label;
1523
- const isActive = bookingType === bt.id;
1524
- const BtIcon = BOOKING_TYPE_ICONS[bt.icon] ?? Calendar;
1525
- return /* @__PURE__ */ jsxs(
1526
- "button",
1527
- {
1528
- type: "button",
1529
- onClick: () => setBookingType(bt.id),
1530
- className: `relative flex items-center gap-1.5 pb-2 px-2 text-xs font-medium transition-colors ${isActive ? "text-foreground" : "text-muted-foreground hover:text-foreground"}`,
1531
- children: [
1532
- /* @__PURE__ */ jsx(BtIcon, { className: "h-3.5 w-3.5" }),
1533
- label,
1534
- isActive && /* @__PURE__ */ jsx("span", { className: "absolute bottom-0 left-0 right-0 h-0.5 rounded-full bg-foreground" })
1535
- ]
1536
- },
1537
- bt.id
1538
- );
1539
- }) }) : mode === "edit" && (config.clientEntityDef && clientId || config.modules.financial && editOrderId ? /* @__PURE__ */ jsx("div", { className: "flex gap-1 border-b fayz-glass-divider", children: [
1540
- { id: "appointment", label: t2(`agenda.bookingType.${bookingType}`) || activeType.label, icon: Calendar, show: true },
1541
- { id: "client", label: t2("agenda.appointment.clientTab"), icon: User, show: !!config.clientEntityDef && !!clientId },
1542
- { id: "financial", label: t2("agenda.payment.title"), icon: DollarSign, show: config.modules.financial && !!editOrderId }
1543
- ].filter((tab) => tab.show).map((tab) => {
1544
- const isActive = modalTab === tab.id;
1545
- return /* @__PURE__ */ jsxs(
1546
- "button",
1547
- {
1548
- type: "button",
1549
- onClick: () => setModalTab(tab.id),
1550
- className: `relative flex items-center gap-1.5 pb-2 px-2 text-xs font-medium transition-colors ${isActive ? "text-foreground" : "text-muted-foreground hover:text-foreground"}`,
1551
- children: [
1552
- /* @__PURE__ */ jsx(tab.icon, { className: "h-3.5 w-3.5" }),
1553
- tab.label,
1554
- tab.id === "financial" && editPaymentStatus === "paid" && /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-success" }),
1555
- tab.id === "financial" && editPaymentStatus !== "paid" && (editPaymentStatus !== "none" || totalPrice > 0 && date <= (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)) && /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-warning" }),
1556
- isActive && /* @__PURE__ */ jsx("span", { className: "absolute bottom-0 left-0 right-0 h-0.5 rounded-full bg-foreground" })
1557
- ]
1558
- },
1559
- tab.id
1560
- );
1561
- }) }) : /* @__PURE__ */ jsx("h2", { className: "text-sm font-semibold", children: t2(`agenda.bookingType.${bookingType}`) || activeType.label })),
1562
- modalTab === "appointment" && /* @__PURE__ */ jsxs(Fragment, { children: [
1563
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1564
- /* @__PURE__ */ jsx(Clock, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1565
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1.5 text-sm flex-1 min-w-0", children: [
1566
- editingDate ? /* @__PURE__ */ jsx(
1567
- "input",
1568
- {
1569
- type: "date",
1570
- value: date,
1571
- autoFocus: true,
1572
- onChange: (e) => {
1573
- setDate(e.target.value);
1574
- setEditingDate(false);
1575
- },
1576
- onBlur: () => setEditingDate(false),
1577
- className: "bg-transparent text-sm font-medium focus:outline-none border-b border-foreground w-auto shrink-0"
1578
- }
1579
- ) : /* @__PURE__ */ jsx(
1580
- "button",
1581
- {
1582
- type: "button",
1583
- onClick: () => setEditingDate(true),
1584
- className: "px-0.5 py-0.5 font-medium border-b border-transparent hover:border-foreground transition-colors shrink-0",
1585
- children: formatCompactDate(date, locale)
1586
- }
1587
- ),
1588
- editingTime ? /* @__PURE__ */ jsx(
1589
- "input",
1590
- {
1591
- type: "time",
1592
- value: startTime,
1593
- autoFocus: true,
1594
- onChange: (e) => {
1595
- if (e.target.value) {
1596
- setStartTime(e.target.value);
1597
- setEditingTime(false);
1598
- }
1599
- },
1600
- onBlur: () => setEditingTime(false),
1601
- className: "bg-transparent text-xs text-muted-foreground focus:outline-none border-b border-foreground w-auto shrink-0"
1602
- }
1603
- ) : /* @__PURE__ */ jsx(
1604
- "button",
1605
- {
1606
- type: "button",
1607
- onClick: () => setEditingTime(true),
1608
- className: "px-0.5 py-0.5 text-xs text-muted-foreground border-b border-transparent hover:border-foreground transition-colors shrink-0",
1609
- children: startTime
1610
- }
1611
- ),
1612
- /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground/60", children: "\u2014" }),
1613
- /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground/60", children: endTime }),
1614
- activeType.fields.professional && /* @__PURE__ */ jsx("div", { className: "ml-auto shrink-0", children: /* @__PURE__ */ jsx(ProfessionalSelect, { value: professionalId, onChange: setProfessionalId, professionals }) })
1615
- ] })
1616
- ] }),
1617
- conflict && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-lg bg-destructive/10 px-3 py-1.5 text-xs text-destructive", children: [
1618
- /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3.5 w-3.5 shrink-0" }),
1619
- t2("agenda.appointment.conflict")
1620
- ] })
1621
- ] })
1622
- ] }),
1623
- /* @__PURE__ */ jsxs("div", { className: "flex flex-col flex-1 min-h-0 overflow-hidden", children: [
1624
- modalTab === "client" && clientId && config.clientEntityDef && /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto px-5 py-4", children: clientDataLoading ? /* @__PURE__ */ jsx("div", { className: "space-y-3 py-2", children: [...Array(4)].map((_, i) => /* @__PURE__ */ jsx("div", { className: "h-8 rounded-lg bg-muted animate-pulse" }, i)) }) : clientData ? clientEditing ? /* @__PURE__ */ jsx(
1625
- CrudFormPage,
1626
- {
1627
- embedded: true,
1628
- entityDef: config.clientEntityDef,
1629
- mode: "edit",
1630
- initialData: clientData,
1631
- namePlural: "",
1632
- onCancel: () => setClientEditing(false),
1633
- onSubmit: async (data) => {
1634
- try {
1635
- const { getSupabaseClientOptional: getSupabaseClientOptional4 } = await import('@fayz-ai/core');
1636
- const supabase = getSupabaseClientOptional4();
1637
- if (!supabase) return;
1638
- const dbData = {};
1639
- for (const [k, v] of Object.entries(data)) {
1640
- dbData[k.replace(/[A-Z]/g, (m) => "_" + m.toLowerCase())] = v;
1641
- }
1642
- await supabase.from("people").update(dbData).eq("id", clientId);
1643
- setClientData({ ...clientData, ...data });
1644
- setClientEditing(false);
1645
- } catch {
1646
- }
1647
- }
1648
- }
1649
- ) : /* @__PURE__ */ jsx(
1650
- CrudDetailPage,
1651
- {
1652
- embedded: true,
1653
- entityDef: config.clientEntityDef,
1654
- item: clientData,
1655
- namePlural: "",
1656
- basePath: "",
1657
- onBack: () => setModalTab("appointment"),
1658
- onEdit: () => setClientEditing(true)
1659
- }
1660
- ) : /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground py-4 text-center", children: t2("agenda.appointment.noClientData") }) }),
1661
- modalTab === "financial" && editOrderId && config.financialBridge && /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto", children: /* @__PURE__ */ jsx(
1662
- BookingPaymentPanel,
1663
- {
1664
- orderId: editOrderId,
1665
- orderTotal: editOrderTotal,
1666
- services: services.map((s) => ({ name: s.name, price: s.price, durationMinutes: s.durationMinutes })),
1667
- formatCurrency: (v) => fmtCurrency(v, currency.locale, currency.code),
1668
- inline: true,
1669
- onClose: () => setModalTab("appointment"),
1670
- onPaymentChange: () => {
1671
- config.financialBridge.getPaymentDetail(editOrderId).then((d) => {
1672
- if (d) {
1673
- setEditPaymentStatus(d.status);
1674
- setEditOrderTotal(d.totalAmount);
1675
- }
1676
- }).catch(() => {
1677
- });
1678
- if (agendaDateRange.start) fetchBookings(agendaDateRange);
1679
- }
1680
- }
1681
- ) }),
1682
- /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col flex-1 min-h-0 overflow-hidden", style: { display: modalTab === "appointment" ? void 0 : "none" }, children: [
1683
- /* @__PURE__ */ jsxs("div", { className: "border-t fayz-glass-divider px-5 py-3 space-y-1 flex-1 overflow-y-auto", children: [
1684
- loading && /* @__PURE__ */ jsx("div", { className: "space-y-2 py-2", children: [...Array(4)].map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1.5", children: [
1685
- /* @__PURE__ */ jsx("div", { className: "h-4 w-4 rounded bg-muted animate-pulse shrink-0" }),
1686
- /* @__PURE__ */ jsx("div", { className: "h-8 flex-1 rounded-lg bg-muted animate-pulse" })
1687
- ] }, i)) }),
1688
- !loading && /* @__PURE__ */ jsxs(Fragment, { children: [
1689
- activeType.fields.title && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1", children: [
1690
- /* @__PURE__ */ jsx(Calendar, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1691
- /* @__PURE__ */ jsx(
1542
+ const railTitle = mode === "create" ? t2("agenda.appointment.new") : title.trim() || clientSearch || t2("agenda.appointment.edit");
1543
+ return (
1544
+ // A booking is written next to the calendar it belongs to, not on top of
1545
+ // it: the dialog used to dim the week you were reading while you filled in
1546
+ // a slot from it, and a second panel opening from the same edge as the
1547
+ // assistant meant two vocabularies for one side of the screen.
1548
+ /* @__PURE__ */ jsxs(RightRailPage, { title: railTitle, open, onClose, width: "max-w-lg", children: [
1549
+ /* @__PURE__ */ jsxs("div", { className: "px-5 pt-3 pb-3 shrink-0 space-y-3", children: [
1550
+ showTabs && mode === "create" ? /* @__PURE__ */ jsx("div", { className: "flex gap-1 border-b fayz-glass-divider", children: config.bookingTypes.map((bt) => {
1551
+ const label = t2(`agenda.bookingType.${bt.id}`) || bt.label;
1552
+ const isActive = bookingType === bt.id;
1553
+ const BtIcon = BOOKING_TYPE_ICONS[bt.icon] ?? Calendar;
1554
+ return /* @__PURE__ */ jsxs(
1555
+ "button",
1556
+ {
1557
+ type: "button",
1558
+ onClick: () => setBookingType(bt.id),
1559
+ className: `relative flex items-center gap-1.5 pb-2 px-2 text-xs font-medium transition-colors ${isActive ? "text-foreground" : "text-muted-foreground hover:text-foreground"}`,
1560
+ children: [
1561
+ /* @__PURE__ */ jsx(BtIcon, { className: "h-3.5 w-3.5" }),
1562
+ label,
1563
+ isActive && /* @__PURE__ */ jsx("span", { className: "absolute bottom-0 left-0 right-0 h-0.5 rounded-full bg-foreground" })
1564
+ ]
1565
+ },
1566
+ bt.id
1567
+ );
1568
+ }) }) : mode === "edit" && (config.clientEntityDef && clientId || config.modules.financial && editOrderId ? /* @__PURE__ */ jsx("div", { className: "flex gap-1 border-b fayz-glass-divider", children: [
1569
+ { id: "appointment", label: t2(`agenda.bookingType.${bookingType}`) || activeType.label, icon: Calendar, show: true },
1570
+ { id: "client", label: t2("agenda.appointment.clientTab"), icon: User, show: !!config.clientEntityDef && !!clientId },
1571
+ { id: "financial", label: t2("agenda.payment.title"), icon: DollarSign, show: config.modules.financial && !!editOrderId }
1572
+ ].filter((tab) => tab.show).map((tab) => {
1573
+ const isActive = modalTab === tab.id;
1574
+ return /* @__PURE__ */ jsxs(
1575
+ "button",
1576
+ {
1577
+ type: "button",
1578
+ onClick: () => setModalTab(tab.id),
1579
+ className: `relative flex items-center gap-1.5 pb-2 px-2 text-xs font-medium transition-colors ${isActive ? "text-foreground" : "text-muted-foreground hover:text-foreground"}`,
1580
+ children: [
1581
+ /* @__PURE__ */ jsx(tab.icon, { className: "h-3.5 w-3.5" }),
1582
+ tab.label,
1583
+ tab.id === "financial" && editPaymentStatus === "paid" && /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-success" }),
1584
+ tab.id === "financial" && editPaymentStatus !== "paid" && (editPaymentStatus !== "none" || totalPrice > 0 && date <= (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)) && /* @__PURE__ */ jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-warning" }),
1585
+ isActive && /* @__PURE__ */ jsx("span", { className: "absolute bottom-0 left-0 right-0 h-0.5 rounded-full bg-foreground" })
1586
+ ]
1587
+ },
1588
+ tab.id
1589
+ );
1590
+ }) }) : null),
1591
+ modalTab === "appointment" && /* @__PURE__ */ jsxs(Fragment, { children: [
1592
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1593
+ /* @__PURE__ */ jsx(Clock, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1594
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-1.5 gap-y-1 text-sm flex-1 min-w-0", children: [
1595
+ editingDate ? /* @__PURE__ */ jsx(
1692
1596
  "input",
1693
1597
  {
1694
- type: "text",
1695
- value: title,
1696
- onChange: (e) => setTitle(e.target.value),
1697
- placeholder: t2("agenda.appointment.addTitle"),
1698
- autoFocus: mode === "create",
1699
- 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"
1598
+ type: "date",
1599
+ value: date,
1600
+ autoFocus: true,
1601
+ onChange: (e) => {
1602
+ setDate(e.target.value);
1603
+ setEditingDate(false);
1604
+ },
1605
+ onBlur: () => setEditingDate(false),
1606
+ className: "bg-transparent text-sm font-medium focus:outline-none border-b border-foreground w-auto shrink-0"
1700
1607
  }
1701
- )
1702
- ] }),
1703
- activeType.fields.client && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1", children: [
1704
- /* @__PURE__ */ jsx(User, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1705
- /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
1706
- ContactPicker,
1608
+ ) : /* @__PURE__ */ jsx(
1609
+ "button",
1610
+ {
1611
+ type: "button",
1612
+ onClick: () => setEditingDate(true),
1613
+ className: "px-0.5 py-0.5 font-medium border-b border-transparent hover:border-foreground transition-colors shrink-0",
1614
+ children: formatCompactDate(date, locale)
1615
+ }
1616
+ ),
1617
+ editingTime ? /* @__PURE__ */ jsx(
1618
+ "input",
1707
1619
  {
1708
- value: clientId || clientSearch ? { id: clientId || void 0, name: clientSearch, phone: clientPhone } : null,
1709
- onChange: (contact) => {
1710
- setClientId(contact?.id ?? "");
1711
- setClientSearch(contact?.name ?? "");
1712
- setClientPhone(contact?.phone ?? "");
1620
+ type: "time",
1621
+ value: startTime,
1622
+ autoFocus: true,
1623
+ onChange: (e) => {
1624
+ if (e.target.value) {
1625
+ setStartTime(e.target.value);
1626
+ setEditingTime(false);
1627
+ }
1713
1628
  },
1714
- kind: config.clientKind,
1715
- extensionTable: "clients",
1716
- lookup: config.contactLookup,
1717
- clearable: !isPaidBooking,
1718
- variant: "inline",
1719
- placeholder: t2("agenda.appointment.searchClient"),
1720
- createLabel: t2("agenda.appointment.newClient")
1629
+ onBlur: () => setEditingTime(false),
1630
+ className: "bg-transparent text-xs text-muted-foreground focus:outline-none border-b border-foreground w-auto shrink-0"
1721
1631
  }
1722
- ) })
1723
- ] }),
1724
- activeType.fields.location && config.modules.locationSelection && (storeLocations.length > 0 || config.locations.length > 0) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1", children: [
1725
- /* @__PURE__ */ jsx(MapPin, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1726
- /* @__PURE__ */ jsx(
1727
- LocationSelect,
1632
+ ) : /* @__PURE__ */ jsx(
1633
+ "button",
1728
1634
  {
1729
- value: locationId,
1730
- onChange: setLocationId,
1731
- locations: storeLocations.length > 0 ? storeLocations : config.locations
1635
+ type: "button",
1636
+ onClick: () => setEditingTime(true),
1637
+ className: "px-0.5 py-0.5 text-xs text-muted-foreground border-b border-transparent hover:border-foreground transition-colors shrink-0",
1638
+ children: startTime
1732
1639
  }
1733
- )
1734
- ] }),
1735
- activeType.fields.services && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 py-1", children: [
1736
- /* @__PURE__ */ jsx(Briefcase, { className: "h-4 w-4 mt-1.5 text-muted-foreground shrink-0" }),
1737
- /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
1738
- services.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-0.5 mb-1", children: services.map((svc, idx) => /* @__PURE__ */ jsxs("div", { className: "flex items-center rounded-md bg-muted/30 px-2.5 py-1.5 text-sm", children: [
1739
- /* @__PURE__ */ jsx("span", { className: "flex-1 truncate min-w-0", children: svc.name }),
1740
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 shrink-0 ml-3", children: [
1741
- /* @__PURE__ */ jsx(
1742
- InlineNumber,
1743
- {
1744
- value: svc.durationMinutes,
1745
- onChange: (v) => setServices((p) => p.map((s, i) => i === idx ? { ...s, durationMinutes: Math.round(v) } : s)),
1746
- min: 5,
1747
- step: 5,
1748
- className: "w-10 shrink-0",
1749
- disabled: isPaidBooking,
1750
- tooltip: t2("agenda.appointment.duration")
1751
- }
1752
- ),
1753
- /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: "min" })
1754
- ] }),
1755
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 shrink-0 ml-3", children: [
1756
- /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: currency.symbol }),
1757
- /* @__PURE__ */ jsx(
1758
- InlineNumber,
1759
- {
1760
- value: svc.price,
1761
- onChange: (v) => setServices((p) => p.map((s, i) => i === idx ? { ...s, price: v } : s)),
1762
- min: 0,
1763
- step: 0.01,
1764
- className: "w-14 shrink-0",
1765
- disabled: isPaidBooking,
1766
- tooltip: t2("agenda.appointment.price")
1767
- }
1768
- )
1769
- ] }),
1770
- !isPaidBooking && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => removeService(idx), className: "p-0.5 ml-2 text-muted-foreground hover:text-destructive shrink-0", children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" }) })
1771
- ] }, idx)) }),
1772
- !isPaidBooking && /* @__PURE__ */ jsx(
1773
- SearchCombobox,
1640
+ ),
1641
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground/60", children: "\u2014" }),
1642
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground/60", children: endTime }),
1643
+ activeType.fields.professional && /* @__PURE__ */ jsx("div", { className: "ml-auto shrink-0", children: /* @__PURE__ */ jsx(ProfessionalSelect, { value: professionalId, onChange: setProfessionalId, professionals }) })
1644
+ ] })
1645
+ ] }),
1646
+ conflict && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 rounded-lg bg-destructive/10 px-3 py-1.5 text-xs text-destructive", children: [
1647
+ /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3.5 w-3.5 shrink-0" }),
1648
+ t2("agenda.appointment.conflict")
1649
+ ] })
1650
+ ] })
1651
+ ] }),
1652
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col flex-1 min-h-0 overflow-hidden", children: [
1653
+ modalTab === "client" && clientId && config.clientEntityDef && /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto px-5 py-4", children: clientDataLoading ? /* @__PURE__ */ jsx("div", { className: "space-y-3 py-2", children: [...Array(4)].map((_, i) => /* @__PURE__ */ jsx("div", { className: "h-8 rounded-lg bg-muted animate-pulse" }, i)) }) : clientData ? clientEditing ? /* @__PURE__ */ jsx(
1654
+ CrudFormPage,
1655
+ {
1656
+ embedded: true,
1657
+ entityDef: config.clientEntityDef,
1658
+ mode: "edit",
1659
+ initialData: clientData,
1660
+ namePlural: "",
1661
+ onCancel: () => setClientEditing(false),
1662
+ onSubmit: async (data) => {
1663
+ try {
1664
+ const { getSupabaseClientOptional: getSupabaseClientOptional4 } = await import('@fayz-ai/core');
1665
+ const supabase = getSupabaseClientOptional4();
1666
+ if (!supabase) return;
1667
+ const dbData = {};
1668
+ for (const [k, v] of Object.entries(data)) {
1669
+ dbData[k.replace(/[A-Z]/g, (m) => "_" + m.toLowerCase())] = v;
1670
+ }
1671
+ await supabase.from("people").update(dbData).eq("id", clientId);
1672
+ setClientData({ ...clientData, ...data });
1673
+ setClientEditing(false);
1674
+ } catch {
1675
+ }
1676
+ }
1677
+ }
1678
+ ) : /* @__PURE__ */ jsx(
1679
+ CrudDetailPage,
1680
+ {
1681
+ embedded: true,
1682
+ entityDef: config.clientEntityDef,
1683
+ item: clientData,
1684
+ namePlural: "",
1685
+ basePath: "",
1686
+ onBack: () => setModalTab("appointment"),
1687
+ onEdit: () => setClientEditing(true)
1688
+ }
1689
+ ) : /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground py-4 text-center", children: t2("agenda.appointment.noClientData") }) }),
1690
+ modalTab === "financial" && editOrderId && config.financialBridge && /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto", children: /* @__PURE__ */ jsx(
1691
+ BookingPaymentPanel,
1692
+ {
1693
+ orderId: editOrderId,
1694
+ orderTotal: editOrderTotal,
1695
+ services: services.map((s) => ({ name: s.name, price: s.price, durationMinutes: s.durationMinutes })),
1696
+ formatCurrency: (v) => fmtCurrency(v, currency.locale, currency.code),
1697
+ inline: true,
1698
+ onClose: () => setModalTab("appointment"),
1699
+ onPaymentChange: () => {
1700
+ config.financialBridge.getPaymentDetail(editOrderId).then((d) => {
1701
+ if (d) {
1702
+ setEditPaymentStatus(d.status);
1703
+ setEditOrderTotal(d.totalAmount);
1704
+ }
1705
+ }).catch(() => {
1706
+ });
1707
+ if (agendaDateRange.start) fetchBookings(agendaDateRange);
1708
+ }
1709
+ }
1710
+ ) }),
1711
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, className: "flex flex-col flex-1 min-h-0 overflow-hidden", style: { display: modalTab === "appointment" ? void 0 : "none" }, children: [
1712
+ /* @__PURE__ */ jsxs("div", { className: "border-t fayz-glass-divider px-5 py-3 space-y-1 flex-1 overflow-y-auto", children: [
1713
+ loading && /* @__PURE__ */ jsx("div", { className: "space-y-2 py-2", children: [...Array(4)].map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1.5", children: [
1714
+ /* @__PURE__ */ jsx("div", { className: "h-4 w-4 rounded bg-muted animate-pulse shrink-0" }),
1715
+ /* @__PURE__ */ jsx("div", { className: "h-8 flex-1 rounded-lg bg-muted animate-pulse" })
1716
+ ] }, i)) }),
1717
+ !loading && /* @__PURE__ */ jsxs(Fragment, { children: [
1718
+ activeType.fields.title && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1", children: [
1719
+ /* @__PURE__ */ jsx(Calendar, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1720
+ /* @__PURE__ */ jsx(
1721
+ "input",
1774
1722
  {
1775
- value: serviceSearch,
1776
- onChange: setServiceSearch,
1777
- onSelect: addService,
1778
- options: serviceOptions,
1779
- placeholder: t2("agenda.appointment.searchService"),
1780
- onBlurEmpty: () => {
1781
- },
1782
- onFocus: () => {
1783
- if (!serviceSearch && serviceOptions.length === 0) setServiceOptions(initialServiceOptions);
1784
- },
1785
- renderRight: (opt) => {
1786
- const dur = opt.data?.duration_minutes || 0;
1787
- const hasPrice = opt.price != null;
1788
- if (!hasPrice && !dur) return null;
1789
- return /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground text-xs whitespace-nowrap", children: [
1790
- hasPrice && fmtCurrency(opt.price, currency.locale, currency.code),
1791
- hasPrice && dur > 0 && " \xB7 ",
1792
- dur > 0 && `${dur}min`
1793
- ] });
1723
+ type: "text",
1724
+ value: title,
1725
+ onChange: (e) => setTitle(e.target.value),
1726
+ placeholder: t2("agenda.appointment.addTitle"),
1727
+ autoFocus: mode === "create",
1728
+ "aria-required": true,
1729
+ 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"
1730
+ }
1731
+ )
1732
+ ] }),
1733
+ activeType.fields.client && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1", children: [
1734
+ /* @__PURE__ */ jsx(User, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1735
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
1736
+ ContactPicker,
1737
+ {
1738
+ value: clientId || clientSearch ? { id: clientId || void 0, name: clientSearch, phone: clientPhone } : null,
1739
+ onChange: (contact) => {
1740
+ setClientId(contact?.id ?? "");
1741
+ setClientSearch(contact?.name ?? "");
1742
+ setClientPhone(contact?.phone ?? "");
1794
1743
  },
1795
- minimal: true,
1796
- inlineLabel: services.length > 0 ? t2("agenda.appointment.addService") : t2("agenda.appointment.addServiceFirst"),
1797
- inlineIcon: services.length > 0 ? "plus" : "search"
1744
+ kind: config.clientKind,
1745
+ lookup: config.contactLookup,
1746
+ createEntityDef: config.clientEntityDef,
1747
+ clearable: !isPaidBooking,
1748
+ variant: "inline",
1749
+ placeholder: t2("agenda.appointment.searchClient"),
1750
+ createLabel: t2("agenda.appointment.newClient"),
1751
+ required: activeType.requiresClient
1752
+ }
1753
+ ) })
1754
+ ] }),
1755
+ activeType.fields.location && config.modules.locationSelection && (storeLocations.length > 0 || config.locations.length > 0) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1", children: [
1756
+ /* @__PURE__ */ jsx(MapPin, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1757
+ /* @__PURE__ */ jsx(
1758
+ LocationSelect,
1759
+ {
1760
+ value: locationId,
1761
+ onChange: setLocationId,
1762
+ locations: storeLocations.length > 0 ? storeLocations : config.locations
1798
1763
  }
1799
1764
  )
1800
- ] })
1801
- ] }),
1802
- activeType.fields.status && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1", children: [
1803
- /* @__PURE__ */ jsx(CircleDot, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1804
- /* @__PURE__ */ jsx(StatusSelect, { value: status, onChange: setStatus, statuses: config.statuses, bookingDate: date })
1805
- ] }),
1806
- needsCancellationReason && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 py-1", children: [
1807
- /* @__PURE__ */ jsx(Ban, { className: "h-4 w-4 mt-2 text-muted-foreground shrink-0" }),
1808
- /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-2", children: [
1809
- /* @__PURE__ */ jsxs(
1810
- "select",
1765
+ ] }),
1766
+ activeType.fields.services && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 py-1", children: [
1767
+ /* @__PURE__ */ jsx(Briefcase, { className: "h-4 w-4 mt-1.5 text-muted-foreground shrink-0" }),
1768
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
1769
+ services.length > 0 && /* @__PURE__ */ jsx("div", { className: "space-y-0.5 mb-1", children: services.map((svc, idx) => /* @__PURE__ */ jsxs("div", { className: "flex items-center rounded-md bg-muted/30 px-2.5 py-1.5 text-sm", children: [
1770
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate min-w-0", children: svc.name }),
1771
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 shrink-0 ml-3", children: [
1772
+ /* @__PURE__ */ jsx(
1773
+ InlineNumber,
1774
+ {
1775
+ value: svc.durationMinutes,
1776
+ onChange: (v) => setServices((p) => p.map((s, i) => i === idx ? { ...s, durationMinutes: Math.round(v) } : s)),
1777
+ min: 5,
1778
+ step: 5,
1779
+ className: "w-10 shrink-0",
1780
+ disabled: isPaidBooking,
1781
+ tooltip: t2("agenda.appointment.duration")
1782
+ }
1783
+ ),
1784
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: "min" })
1785
+ ] }),
1786
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 shrink-0 ml-3", children: [
1787
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: currency.symbol }),
1788
+ /* @__PURE__ */ jsx(
1789
+ InlineNumber,
1790
+ {
1791
+ value: svc.price,
1792
+ onChange: (v) => setServices((p) => p.map((s, i) => i === idx ? { ...s, price: v } : s)),
1793
+ min: 0,
1794
+ step: 0.01,
1795
+ className: "w-14 shrink-0",
1796
+ disabled: isPaidBooking,
1797
+ tooltip: t2("agenda.appointment.price")
1798
+ }
1799
+ )
1800
+ ] }),
1801
+ !isPaidBooking && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => removeService(idx), className: "p-0.5 ml-2 text-muted-foreground hover:text-destructive shrink-0", children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" }) })
1802
+ ] }, idx)) }),
1803
+ serviceCreate.creating && serviceCreate.form,
1804
+ !isPaidBooking && !serviceCreate.creating && /* @__PURE__ */ jsx(
1805
+ SearchCombobox,
1806
+ {
1807
+ value: serviceSearch,
1808
+ onChange: setServiceSearch,
1809
+ onSelect: addService,
1810
+ options: serviceOptions,
1811
+ placeholder: t2("agenda.appointment.searchService"),
1812
+ onBlurEmpty: () => {
1813
+ },
1814
+ onFocus: () => {
1815
+ if (!serviceSearch && serviceOptions.length === 0) setServiceOptions(initialServiceOptions);
1816
+ },
1817
+ renderRight: (opt) => {
1818
+ const dur = opt.data?.duration_minutes || 0;
1819
+ const hasPrice = opt.price != null;
1820
+ if (!hasPrice && !dur) return null;
1821
+ return /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground text-xs whitespace-nowrap", children: [
1822
+ hasPrice && fmtCurrency(opt.price, currency.locale, currency.code),
1823
+ hasPrice && dur > 0 && " \xB7 ",
1824
+ dur > 0 && `${dur}min`
1825
+ ] });
1826
+ },
1827
+ minimal: true,
1828
+ chevron: true,
1829
+ required: activeType.requiresServices && services.length === 0,
1830
+ allowCreate: !!serviceCreate.onCreate,
1831
+ onCreateNew: serviceCreate.onCreate,
1832
+ createLabel: t2("agenda.appointment.newService"),
1833
+ inlineLabel: services.length > 0 ? t2("agenda.appointment.addService") : t2("agenda.appointment.addServiceFirst"),
1834
+ inlineIcon: services.length > 0 ? "plus" : "search"
1835
+ }
1836
+ )
1837
+ ] })
1838
+ ] }),
1839
+ activeType.fields.status && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1", children: [
1840
+ /* @__PURE__ */ jsx(CircleDot, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1841
+ /* @__PURE__ */ jsx(StatusSelect, { value: status, onChange: setStatus, statuses: config.statuses, bookingDate: date })
1842
+ ] }),
1843
+ needsCancellationReason && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 py-1", children: [
1844
+ /* @__PURE__ */ jsx(Ban, { className: "h-4 w-4 mt-2 text-muted-foreground shrink-0" }),
1845
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-2", children: [
1846
+ /* @__PURE__ */ jsxs(
1847
+ "select",
1848
+ {
1849
+ value: cancellationReasonId,
1850
+ onChange: (event) => setCancellationReasonId(event.target.value),
1851
+ disabled: cancellationReasonsLoading,
1852
+ "aria-required": true,
1853
+ 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",
1854
+ children: [
1855
+ /* @__PURE__ */ jsx("option", { value: "", children: cancellationReasonsLoading ? t2("agenda.cancellation.loadingReasons") : t2("agenda.cancellation.selectReason") }),
1856
+ cancellationReasons.map((reason) => /* @__PURE__ */ jsxs("option", { value: reason.id, children: [
1857
+ reason.label,
1858
+ reason.requiresNotes ? " *" : ""
1859
+ ] }, reason.id))
1860
+ ]
1861
+ }
1862
+ ),
1863
+ /* @__PURE__ */ jsx(
1864
+ "textarea",
1865
+ {
1866
+ value: cancellationNotes,
1867
+ onChange: (event) => setCancellationNotes(event.target.value),
1868
+ placeholder: needsCancellationNotes ? t2("agenda.cancellation.notesRequired") : t2("agenda.cancellation.notesOptional"),
1869
+ rows: 2,
1870
+ "aria-required": needsCancellationNotes || void 0,
1871
+ 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"
1872
+ }
1873
+ )
1874
+ ] })
1875
+ ] }),
1876
+ !showNotes ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1", children: [
1877
+ /* @__PURE__ */ jsx(FileText, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1878
+ /* @__PURE__ */ jsx(
1879
+ "button",
1811
1880
  {
1812
- value: cancellationReasonId,
1813
- onChange: (event) => setCancellationReasonId(event.target.value),
1814
- disabled: cancellationReasonsLoading,
1815
- 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",
1816
- children: [
1817
- /* @__PURE__ */ jsx("option", { value: "", children: cancellationReasonsLoading ? t2("agenda.cancellation.loadingReasons") : t2("agenda.cancellation.selectReason") }),
1818
- cancellationReasons.map((reason) => /* @__PURE__ */ jsxs("option", { value: reason.id, children: [
1819
- reason.label,
1820
- reason.requiresNotes ? " *" : ""
1821
- ] }, reason.id))
1822
- ]
1881
+ type: "button",
1882
+ onClick: () => setShowNotes(true),
1883
+ className: "text-sm text-muted-foreground hover:text-foreground transition-colors text-left py-1.5 pl-2",
1884
+ children: t2("agenda.appointment.addNotes")
1823
1885
  }
1824
- ),
1886
+ )
1887
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 py-1", children: [
1888
+ /* @__PURE__ */ jsx(FileText, { className: "h-4 w-4 mt-2 text-muted-foreground shrink-0" }),
1825
1889
  /* @__PURE__ */ jsx(
1826
1890
  "textarea",
1827
1891
  {
1828
- value: cancellationNotes,
1829
- onChange: (event) => setCancellationNotes(event.target.value),
1830
- placeholder: needsCancellationNotes ? t2("agenda.cancellation.notesRequired") : t2("agenda.cancellation.notesOptional"),
1892
+ value: notes,
1893
+ onChange: (e) => setNotes(e.target.value),
1894
+ placeholder: t2("agenda.appointment.addDescription"),
1831
1895
  rows: 2,
1832
- 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"
1896
+ autoFocus: true,
1897
+ className: "w-full flex-1 rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-2.5 py-1.5 text-sm resize-none focus:outline-none focus:ring-2 focus:ring-ring"
1833
1898
  }
1834
1899
  )
1835
1900
  ] })
1836
- ] }),
1837
- !showNotes ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-1", children: [
1838
- /* @__PURE__ */ jsx(FileText, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
1839
- /* @__PURE__ */ jsx(
1840
- "button",
1841
- {
1842
- type: "button",
1843
- onClick: () => setShowNotes(true),
1844
- className: "text-sm text-muted-foreground hover:text-foreground transition-colors text-left py-1.5 pl-2",
1845
- children: t2("agenda.appointment.addNotes")
1846
- }
1847
- )
1848
- ] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-3 py-1", children: [
1849
- /* @__PURE__ */ jsx(FileText, { className: "h-4 w-4 mt-2 text-muted-foreground shrink-0" }),
1850
- /* @__PURE__ */ jsx(
1851
- "textarea",
1852
- {
1853
- value: notes,
1854
- onChange: (e) => setNotes(e.target.value),
1855
- placeholder: t2("agenda.appointment.addDescription"),
1856
- rows: 2,
1857
- autoFocus: true,
1858
- className: "w-full flex-1 rounded-input border border-input bg-card shadow-[inset_0_1px_0_rgb(0_0_0_/0.06)] px-2.5 py-1.5 text-sm resize-none focus:outline-none focus:ring-2 focus:ring-ring"
1859
- }
1860
- )
1861
- ] })
1862
- ] })
1863
- ] }),
1864
- modalTab === "appointment" && !clientEditing && /* @__PURE__ */ jsx("div", { className: "px-5 py-2.5 border-t fayz-glass-divider bg-transparent shrink-0 sm:rounded-b-2xl", children: confirmingDelete ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1865
- /* @__PURE__ */ jsx("span", { className: "text-xs text-destructive font-medium", children: t2("agenda.appointment.deleteConfirm") }),
1866
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1867
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setConfirmingDelete(false), children: t2("agenda.appointment.cancel") }),
1868
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "destructive", size: "sm", onClick: handleDelete, disabled: deleting, children: deleting ? t2("agenda.appointment.deleting") : t2("agenda.appointment.confirmDelete") })
1869
- ] })
1870
- ] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1871
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1872
- mode === "edit" && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setConfirmingDelete(true), className: "text-xs text-destructive font-medium hover:underline", children: t2("agenda.appointment.delete") }),
1873
- services.length > 0 && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
1874
- /* @__PURE__ */ jsx(Clock, { className: "h-3 w-3" }),
1875
- totalDuration,
1876
- "min",
1877
- config.modules.financial && totalPrice > 0 && mode === "edit" ? paymentStatusLoading ? /* @__PURE__ */ jsx("span", { className: "h-4 w-16 rounded-full bg-muted animate-pulse" }) : isPaidBooking ? /* @__PURE__ */ jsxs(
1878
- "button",
1879
- {
1880
- type: "button",
1881
- onClick: (e) => {
1882
- e.stopPropagation();
1883
- setModalTab("financial");
1884
- },
1885
- className: "inline-flex items-center gap-1 rounded-full bg-success/10 px-2 py-0.5 text-[10px] font-medium text-success hover:bg-success/20 transition-colors",
1886
- children: [
1887
- /* @__PURE__ */ jsx(Check, { className: "h-2.5 w-2.5" }),
1888
- fmtCurrency(totalPrice, currency.locale, currency.code),
1889
- " \xB7 ",
1890
- t2("agenda.payment.paid")
1891
- ]
1892
- }
1893
- ) : date <= (/* @__PURE__ */ new Date()).toISOString().slice(0, 10) ? /* @__PURE__ */ jsxs(
1894
- "button",
1895
- {
1896
- type: "button",
1897
- onClick: (e) => {
1898
- e.stopPropagation();
1899
- setModalTab("financial");
1900
- },
1901
- className: "inline-flex items-center gap-1 rounded-full bg-warning/10 px-2 py-0.5 text-[10px] font-medium text-warning hover:bg-warning/20 transition-colors",
1902
- children: [
1903
- /* @__PURE__ */ jsx(HandCoins, { className: "h-3 w-3" }),
1904
- fmtCurrency(totalPrice, currency.locale, currency.code),
1905
- " \xB7 ",
1906
- t2("agenda.payment.collect")
1907
- ]
1908
- }
1909
- ) : /* @__PURE__ */ jsxs("span", { children: [
1910
- "\xB7 ",
1911
- fmtCurrency(totalPrice, currency.locale, currency.code)
1912
- ] }) : /* @__PURE__ */ jsxs("span", { children: [
1913
- "\xB7 ",
1914
- fmtCurrency(totalPrice, currency.locale, currency.code)
1915
- ] })
1916
1901
  ] })
1917
1902
  ] }),
1918
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1919
- /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: onClose, className: "hidden sm:inline-flex", children: t2("agenda.appointment.cancel") }),
1920
- /* @__PURE__ */ jsx(Button, { type: "submit", variant: "default", size: "sm", disabled: !canSubmit || !isDirty, children: saving ? t2("agenda.appointment.saving") : mode === "create" ? t2("agenda.appointment.save") : t2("agenda.appointment.update") })
1921
- ] })
1922
- ] }) })
1923
- ] }),
1924
- modalTab !== "appointment" && !clientEditing && /* @__PURE__ */ jsx("div", { className: "px-5 py-2.5 border-t fayz-glass-divider bg-transparent shrink-0 sm:rounded-b-2xl", children: /* @__PURE__ */ jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsxs(
1925
- "button",
1926
- {
1927
- type: "button",
1928
- onClick: () => {
1929
- setModalTab("appointment");
1930
- setClientEditing(false);
1931
- },
1932
- className: "inline-flex items-center gap-1 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors",
1933
- children: [
1934
- /* @__PURE__ */ jsx("svg", { className: "h-3 w-3", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M10 12L6 8l4-4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }),
1935
- t2("agenda.payment.backToAppointment")
1936
- ]
1937
- }
1938
- ) }) })
1903
+ modalTab === "appointment" && !clientEditing && /* @__PURE__ */ jsx("div", { className: "px-5 py-2.5 border-t fayz-glass-divider bg-transparent shrink-0", children: confirmingDelete ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1904
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-destructive font-medium", children: t2("agenda.appointment.deleteConfirm") }),
1905
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1906
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setConfirmingDelete(false), children: t2("agenda.appointment.cancel") }),
1907
+ /* @__PURE__ */ jsx(Button, { type: "button", variant: "destructive", size: "sm", onClick: handleDelete, disabled: deleting, children: deleting ? t2("agenda.appointment.deleting") : t2("agenda.appointment.confirmDelete") })
1908
+ ] })
1909
+ ] }) : /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1910
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1911
+ mode === "edit" && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => setConfirmingDelete(true), className: "text-xs text-destructive font-medium hover:underline", children: t2("agenda.appointment.delete") }),
1912
+ services.length > 0 && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
1913
+ /* @__PURE__ */ jsx(Clock, { className: "h-3 w-3" }),
1914
+ totalDuration,
1915
+ "min",
1916
+ config.modules.financial && totalPrice > 0 && mode === "edit" ? paymentStatusLoading ? /* @__PURE__ */ jsx("span", { className: "h-4 w-16 rounded-full bg-muted animate-pulse" }) : isPaidBooking ? /* @__PURE__ */ jsxs(
1917
+ "button",
1918
+ {
1919
+ type: "button",
1920
+ onClick: (e) => {
1921
+ e.stopPropagation();
1922
+ setModalTab("financial");
1923
+ },
1924
+ className: "inline-flex items-center gap-1 rounded-full bg-success/10 px-2 py-0.5 text-[10px] font-medium text-success hover:bg-success/20 transition-colors",
1925
+ children: [
1926
+ /* @__PURE__ */ jsx(Check, { className: "h-2.5 w-2.5" }),
1927
+ fmtCurrency(totalPrice, currency.locale, currency.code),
1928
+ " \xB7 ",
1929
+ t2("agenda.payment.paid")
1930
+ ]
1931
+ }
1932
+ ) : date <= (/* @__PURE__ */ new Date()).toISOString().slice(0, 10) ? /* @__PURE__ */ jsxs(
1933
+ "button",
1934
+ {
1935
+ type: "button",
1936
+ onClick: (e) => {
1937
+ e.stopPropagation();
1938
+ setModalTab("financial");
1939
+ },
1940
+ className: "inline-flex items-center gap-1 rounded-full bg-warning/10 px-2 py-0.5 text-[10px] font-medium text-warning hover:bg-warning/20 transition-colors",
1941
+ children: [
1942
+ /* @__PURE__ */ jsx(HandCoins, { className: "h-3 w-3" }),
1943
+ fmtCurrency(totalPrice, currency.locale, currency.code),
1944
+ " \xB7 ",
1945
+ t2("agenda.payment.collect")
1946
+ ]
1947
+ }
1948
+ ) : /* @__PURE__ */ jsxs("span", { children: [
1949
+ "\xB7 ",
1950
+ fmtCurrency(totalPrice, currency.locale, currency.code)
1951
+ ] }) : /* @__PURE__ */ jsxs("span", { children: [
1952
+ "\xB7 ",
1953
+ fmtCurrency(totalPrice, currency.locale, currency.code)
1954
+ ] })
1955
+ ] })
1956
+ ] }),
1957
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: /* @__PURE__ */ jsx(Button, { type: "submit", variant: "default", size: "sm", disabled: !canSubmit || !isDirty, children: saving ? t2("agenda.appointment.saving") : mode === "create" ? t2("agenda.appointment.save") : t2("agenda.appointment.update") }) })
1958
+ ] }) })
1959
+ ] }),
1960
+ modalTab !== "appointment" && !clientEditing && /* @__PURE__ */ jsx("div", { className: "px-5 py-2.5 border-t fayz-glass-divider bg-transparent shrink-0", children: /* @__PURE__ */ jsx("div", { className: "flex items-center", children: /* @__PURE__ */ jsxs(
1961
+ "button",
1962
+ {
1963
+ type: "button",
1964
+ onClick: () => {
1965
+ setModalTab("appointment");
1966
+ setClientEditing(false);
1967
+ },
1968
+ className: "inline-flex items-center gap-1 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors",
1969
+ children: [
1970
+ /* @__PURE__ */ jsx("svg", { className: "h-3 w-3", viewBox: "0 0 16 16", fill: "none", children: /* @__PURE__ */ jsx("path", { d: "M10 12L6 8l4-4", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }) }),
1971
+ t2("agenda.payment.backToAppointment")
1972
+ ]
1973
+ }
1974
+ ) }) })
1975
+ ] })
1939
1976
  ] })
1940
- ] }) });
1977
+ );
1941
1978
  }
1942
1979
  var STYLE_ID2 = "saas-floating-panel";
1943
1980
  if (typeof document !== "undefined" && !document.getElementById(STYLE_ID2)) {
@@ -2405,7 +2442,7 @@ function AgendaMobileList({ bookings, statusColors, currencyCode, onSelect }) {
2405
2442
  "button",
2406
2443
  {
2407
2444
  onClick: () => onSelect(b),
2408
- 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",
2445
+ className: "flex w-full items-center gap-3 rounded-card border bg-card px-3 py-2.5 text-left shadow-sm transition-colors active:bg-muted/50",
2409
2446
  children: [
2410
2447
  /* @__PURE__ */ jsx("span", { className: "h-2.5 w-2.5 shrink-0 rounded-full", style: { backgroundColor: color } }),
2411
2448
  /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
@@ -2646,6 +2683,7 @@ function CalendarView() {
2646
2683
  const [profSectionOpen, setProfSectionOpen] = useState(true);
2647
2684
  const [statusSectionOpen, setStatusSectionOpen] = useState(false);
2648
2685
  const [visibleRange, setVisibleRange] = useState(null);
2686
+ useEffect(() => () => closeModal(), [closeModal]);
2649
2687
  const openModal = useCallback((...args) => {
2650
2688
  setPopoverVisible(false);
2651
2689
  setSelectedBooking(null);
@@ -3784,7 +3822,11 @@ function createSupabaseAgendaProvider(providerOptions) {
3784
3822
  },
3785
3823
  async getProfessionals() {
3786
3824
  const { core } = getClients();
3787
- const { data, error } = await core.from("people").select("id, name, avatar_url, is_active").eq("kind", professionalKind).eq("is_active", true).order("name");
3825
+ const base = () => core.from("people").select("id, name, avatar_url, is_active").eq("kind", professionalKind).eq("is_active", true);
3826
+ let { data, error } = await base().eq("is_bookable", true).order("name");
3827
+ if (error && error.code === "42703") {
3828
+ ({ data, error } = await base().order("name"));
3829
+ }
3788
3830
  if (error) throw error;
3789
3831
  return (data ?? []).map((p) => ({
3790
3832
  id: p.id,
@@ -4201,6 +4243,8 @@ var en = {
4201
4243
  "agenda.appointment.name": "Name",
4202
4244
  "agenda.appointment.new": "New Appointment",
4203
4245
  "agenda.appointment.newClient": "New client",
4246
+ "agenda.appointment.required": "Required",
4247
+ "agenda.appointment.newService": "New service",
4204
4248
  "agenda.appointment.notes": "Notes",
4205
4249
  "agenda.appointment.phone": "Phone",
4206
4250
  "agenda.appointment.professional": "Professional",
@@ -4421,6 +4465,8 @@ var ptBR = {
4421
4465
  "agenda.appointment.name": "Nome",
4422
4466
  "agenda.appointment.new": "Novo Agendamento",
4423
4467
  "agenda.appointment.newClient": "Novo cliente",
4468
+ "agenda.appointment.required": "Obrigat\xF3rio",
4469
+ "agenda.appointment.newService": "Novo servi\xE7o",
4424
4470
  "agenda.appointment.notes": "Observa\xE7\xF5es",
4425
4471
  "agenda.appointment.phone": "Telefone",
4426
4472
  "agenda.appointment.professional": "Profissional",
@@ -5715,7 +5761,7 @@ function ImportEventsWizard({
5715
5761
  setImporting(false);
5716
5762
  }
5717
5763
  }
5718
- return /* @__PURE__ */ jsxs("div", { className: "rounded-md border bg-card", children: [
5764
+ return /* @__PURE__ */ jsxs("div", { className: "rounded-card border bg-card", children: [
5719
5765
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b px-3 py-2", children: [
5720
5766
  /* @__PURE__ */ jsx(CalendarPlus, { className: "h-4 w-4 text-muted-foreground" }),
5721
5767
  /* @__PURE__ */ jsx("h4", { className: "text-sm font-semibold", children: "Import future events" }),
@@ -8000,6 +8046,7 @@ function createAgendaPlugin(options) {
8000
8046
  const PageComponent = () => React13.createElement(AgendaPage, { config, provider: provider2, store });
8001
8047
  return {
8002
8048
  id: "agenda",
8049
+ defaultAgentRole: "frontdesk",
8003
8050
  name: config.labels.pageTitle,
8004
8051
  icon: "Calendar",
8005
8052
  version: "1.0.0",