@fayz-ai/plugin-agenda 0.20.0 → 0.21.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
@@ -1,8 +1,10 @@
1
1
  import { createMockAgendaProvider, createSupabasePublicBookingProvider } from './chunk-A77PUFY5.js';
2
2
  export { createMockAgendaProvider } from './chunk-A77PUFY5.js';
3
- import React21, { useState, useEffect, useRef, useMemo, useCallback, useLayoutEffect } from 'react';
3
+ import React22, { useState, useEffect, useRef, useMemo, useCallback, useLayoutEffect } from 'react';
4
4
  import { tl, recordRailFields, RECORD_GROUP, activeFacet, connectionStatus, createConnectionStore, getActiveTenantId, createSafeDataProvider, todayLocal, getSupabaseClientOptional, registerTranslations, registerMessageDefaults, getActiveUnitId, assertWrote, connectorRuntimeToken, CONNECTOR_RUNTIME_TOKEN_HEADER, emitDataChanged, globalCache, usePluginRuntimeOptional, useTranslation, useActiveUnitId, emitDomainEvent, getCurrentLocale, localDateKey, useDataChanged, formatCurrency, resolveDataProvider } from '@fayz-ai/core';
5
- import { createPluginContext, fayzConnectorOAuth, setScheduleBlockConfig, getScheduleBlockConfig, registerPersonActions, PluginSettingsPanel, createArchetypeLookup, dedup, usePluginPrefs, useTenantPluginSettings, SettingsGroup, ToggleRow, SelectRow, PermissionGate, PersonLink, PageActions, useRightRailStore, usePermissionOptional, ScheduleEditor, useLimitGuard, useLookupCreate, CrudFormPage, CrudDetailPage, ContactPicker, openEntity, PaymentStatusBadge as PaymentStatusBadge$1, RightRailPage, invalidateLimit } from '@fayz-ai/admin';
5
+ import { createPluginContext, fayzConnectorOAuth, setScheduleBlockConfig, getScheduleBlockConfig, registerPersonActions, UNIT_DETAIL_ZONES, PluginSettingsPanel, createArchetypeLookup, dedup, adminNavigateTo, usePluginPrefs, useTenantPluginSettings, SettingsGroup, ToggleRow, SelectRow, PermissionGate, PersonLink, PageActions, useRightRailStore, usePermissionOptional, ScheduleEditor, useLimitGuard, useLookupCreate, CrudFormPage, CrudDetailPage, ContactPicker, openEntity, PaymentStatusBadge as PaymentStatusBadge$1, RightRailPage, invalidateLimit } from '@fayz-ai/admin';
6
+ import { toast, Card, CardHeader, CardTitle, CardDescription, CardContent, Skeleton, Switch, Input, Button, FilterRail, AdaptiveFilter, FilterCheckRow, TooltipProvider, ModulePage, Avatar, AvatarImage, AvatarFallback, Badge, Checkbox, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, PersonAvatar, abbreviatePersonName, INLINE_FILTER_MAX, PersonMultiPicker, SegmentedControl, MiniCalendar, PageHeaderActions, ConfirmDialog, RailWorkspace, Tooltip, TooltipTrigger, TooltipContent, SubpageHeader, ModalTask, Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, PersonPicker, Tabs, TabsList, TabsTrigger, DatePicker, TimePicker, ModalFooter, ModalTaskHost, SearchCombobox, ModalTaskHeader, Dropdown, DropdownTrigger, DropdownContent, DropdownItem, cn, normalizePhone, DotBadge } from '@fayz-ai/ui';
7
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
6
8
  import FullCalendar from '@fullcalendar/react';
7
9
  import dayGridPlugin from '@fullcalendar/daygrid';
8
10
  import timeGridPlugin from '@fullcalendar/timegrid';
@@ -11,13 +13,109 @@ import interactionPlugin from '@fullcalendar/interaction';
11
13
  import listPlugin from '@fullcalendar/list';
12
14
  import ptBrLocale from '@fullcalendar/core/locales/pt-br';
13
15
  import { CheckCircle, Stethoscope, RotateCcw, Check, CircleDashed, Layers, Users, Activity, RefreshCw, Clock3, CalendarPlus, Loader2, AlertCircle, X, CheckCircle2, Ban, Plus, Settings, ChevronLeft, ChevronRight, ChevronDown, CircleDollarSign, LogIn, CalendarDays, Play, UserRound, Package, Clock, Mic, MapPin, CalendarX, UserX, Search, CalendarOff, Pencil, Trash2, User, FileText, AlertTriangle, DollarSign, HandCoins, CheckSquare, Calendar, History, ExternalLink, DoorOpen, Briefcase, CircleDot, MessageCircle, Phone, AlarmClock, ArrowLeft, Link2, Receipt, ClipboardCheck, FileCheck, ArrowRightLeft, CreditCard, QrCode, Banknote, Sparkles } from 'lucide-react';
14
- import { toast, FilterRail, AdaptiveFilter, FilterCheckRow, TooltipProvider, ModulePage, Card, CardContent, Button, Avatar, AvatarImage, AvatarFallback, Badge, Checkbox, Select, SelectTrigger, SelectValue, SelectContent, SelectItem, PersonAvatar, abbreviatePersonName, INLINE_FILTER_MAX, PersonMultiPicker, SegmentedControl, Switch, MiniCalendar, PageHeaderActions, ConfirmDialog, RailWorkspace, Tooltip, TooltipTrigger, TooltipContent, SubpageHeader, ModalTask, Skeleton, Modal, ModalContent, ModalHeader, ModalTitle, ModalBody, PersonPicker, Input, Tabs, TabsList, TabsTrigger, DatePicker, TimePicker, ModalFooter, ModalTaskHost, SearchCombobox, ModalTaskHeader, Dropdown, DropdownTrigger, DropdownContent, DropdownItem, cn, normalizePhone, DotBadge } from '@fayz-ai/ui';
15
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
16
16
  import { createPortal } from 'react-dom';
17
17
  import { FormFillDialog, PersonFormsAlert } from '@fayz-ai/plugin-forms';
18
18
  import { ClientAccountModal, ClientAccountInline } from '@fayz-ai/plugin-financial';
19
19
  import { createStore } from 'zustand/vanilla';
20
20
 
21
+ function UnitTurnQueueWidget({ item }) {
22
+ const unitId = item?.id;
23
+ const [row, setRow] = React22.useState(null);
24
+ const [loading, setLoading] = React22.useState(true);
25
+ const [saving, setSaving] = React22.useState(false);
26
+ const [radius, setRadius] = React22.useState("80");
27
+ React22.useEffect(() => {
28
+ if (!unitId) return;
29
+ let alive = true;
30
+ const supabase = getSupabaseClientOptional();
31
+ void supabase?.from("plg_agenda_turn_queues").select("is_active, geofence_required, geofence_radius_meters").eq("unit_id", unitId).eq("queue_key", "walk_in").maybeSingle().then(({ data }) => {
32
+ if (!alive) return;
33
+ const next = data ?? { is_active: false, geofence_required: true, geofence_radius_meters: 80 };
34
+ setRow(next);
35
+ setRadius(String(next.geofence_radius_meters));
36
+ setLoading(false);
37
+ });
38
+ return () => {
39
+ alive = false;
40
+ };
41
+ }, [unitId]);
42
+ const save = async (patch) => {
43
+ if (!unitId || !row) return;
44
+ const next = { ...row, ...patch };
45
+ setSaving(true);
46
+ const supabase = getSupabaseClientOptional();
47
+ const { data, error } = await supabase.rpc("agenda_turn_queue_configure", {
48
+ p_unit_id: unitId,
49
+ p_enabled: next.is_active,
50
+ p_geofence_required: next.geofence_required,
51
+ p_radius_meters: next.geofence_radius_meters
52
+ });
53
+ setSaving(false);
54
+ if (error) {
55
+ toast.error("N\xE3o foi poss\xEDvel salvar a lista da vez", { description: error.message });
56
+ return;
57
+ }
58
+ setRow({ is_active: data.is_active, geofence_required: data.geofence_required, geofence_radius_meters: data.geofence_radius_meters });
59
+ toast.success("Lista da vez salva");
60
+ };
61
+ const radiusNumber = Number(radius);
62
+ const radiusInvalid = !Number.isInteger(radiusNumber) || radiusNumber < 10 || radiusNumber > 1e3;
63
+ const hasCoordinate = item?.latitude != null && item?.longitude != null;
64
+ return /* @__PURE__ */ jsxs(Card, { children: [
65
+ /* @__PURE__ */ jsxs(CardHeader, { children: [
66
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: "Lista da vez" }),
67
+ /* @__PURE__ */ jsx(CardDescription, { children: "Fila de atendimento por ordem de chegada. Desligada, o item some do menu e o bot\xE3o de disponibilidade some do topo." })
68
+ ] }),
69
+ /* @__PURE__ */ jsx(CardContent, { className: "space-y-4", children: loading || !row ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
70
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" }),
71
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" })
72
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
73
+ /* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-4 rounded-md border border-input bg-background px-3 py-2.5", children: [
74
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Ativar lista da vez nesta unidade" }),
75
+ /* @__PURE__ */ jsx(Switch, { checked: row.is_active, disabled: saving, onCheckedChange: (v) => void save({ is_active: v }) })
76
+ ] }),
77
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
78
+ /* @__PURE__ */ jsxs("div", { children: [
79
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Localiza\xE7\xE3o da lista da vez" }),
80
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Valida o bot\xE3o \u201Cestou dispon\xEDvel\u201D dos profissionais pela dist\xE2ncia at\xE9 a unidade." })
81
+ ] }),
82
+ /* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-4 rounded-md border border-input bg-background px-3 py-2.5", children: [
83
+ /* @__PURE__ */ jsx("span", { className: "text-sm", children: "Exigir que o profissional esteja na unidade" }),
84
+ /* @__PURE__ */ jsx(Switch, { checked: row.geofence_required, disabled: saving, onCheckedChange: (v) => void save({ geofence_required: v }) })
85
+ ] }),
86
+ /* @__PURE__ */ jsx("div", { className: "fz-container", children: /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
87
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
88
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Coordenada" }),
89
+ /* @__PURE__ */ jsx("p", { className: "flex h-10 items-center rounded-md border border-input bg-muted/40 px-3 text-sm tabular-nums text-muted-foreground", children: hasCoordinate ? `${Number(item.latitude).toFixed(6)}, ${Number(item.longitude).toFixed(6)}` : "Sem coordenada \u2014 preencha o endere\xE7o" }),
90
+ /* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
91
+ "Vem do endere\xE7o da unidade.",
92
+ " ",
93
+ /* @__PURE__ */ jsx("button", { type: "button", className: "underline decoration-dashed underline-offset-2", onClick: () => adminNavigateTo(`/settings/locations/${item?.id}/edit`), children: "Editar endere\xE7o" })
94
+ ] })
95
+ ] }),
96
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
97
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Raio (m)" }),
98
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
99
+ /* @__PURE__ */ jsx(Input, { type: "number", min: 10, max: 1e3, value: radius, onChange: (e) => setRadius(e.target.value), className: "tabular-nums" }),
100
+ /* @__PURE__ */ jsx(
101
+ Button,
102
+ {
103
+ variant: "outline",
104
+ disabled: saving || radiusInvalid || radiusNumber === row.geofence_radius_meters,
105
+ onClick: () => void save({ geofence_radius_meters: radiusNumber }),
106
+ children: "Salvar"
107
+ }
108
+ )
109
+ ] }),
110
+ radiusInvalid && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: "Use um raio entre 10 e 1000 metros." })
111
+ ] })
112
+ ] }) }),
113
+ row.geofence_required && !hasCoordinate && row.is_active && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: "Sem coordenada no endere\xE7o, ningu\xE9m consegue marcar \u201Cestou dispon\xEDvel\u201D nesta unidade." })
114
+ ] })
115
+ ] }) })
116
+ ] });
117
+ }
118
+
21
119
  // src/lib/message-events.ts
22
120
  var BOOKING_FIELDS = [
23
121
  { name: "customer_name", label: "Cliente", type: "string", example: "Maria Silva" },
@@ -761,20 +859,20 @@ function BlockDialog({
761
859
  }) {
762
860
  const professionals = useAgendaStore((s) => s.agendaProfessionals);
763
861
  const provider2 = useAgendaProvider();
764
- const [assigneeId, setAssigneeId] = React21.useState(prefillProfessional ?? "");
765
- const [title, setTitle] = React21.useState("");
766
- const [description, setDescription] = React21.useState("");
767
- const [mode, setMode] = React21.useState("single");
768
- const [specificDate, setSpecificDate] = React21.useState(date ?? hoje());
769
- const [allDay, setAllDay] = React21.useState(false);
770
- const [startsAt, setStartsAt] = React21.useState("12:00");
771
- const [endsAt, setEndsAt] = React21.useState("13:00");
772
- const [days2, setDays] = React21.useState([]);
773
- const [validFrom, setValidFrom] = React21.useState(date ?? hoje());
774
- const [validTo, setValidTo] = React21.useState("");
775
- const [weekInterval, setWeekInterval] = React21.useState("0");
776
- const [saving, setSaving] = React21.useState(false);
777
- React21.useEffect(() => {
862
+ const [assigneeId, setAssigneeId] = React22.useState(prefillProfessional ?? "");
863
+ const [title, setTitle] = React22.useState("");
864
+ const [description, setDescription] = React22.useState("");
865
+ const [mode, setMode] = React22.useState("single");
866
+ const [specificDate, setSpecificDate] = React22.useState(date ?? hoje());
867
+ const [allDay, setAllDay] = React22.useState(false);
868
+ const [startsAt, setStartsAt] = React22.useState("12:00");
869
+ const [endsAt, setEndsAt] = React22.useState("13:00");
870
+ const [days2, setDays] = React22.useState([]);
871
+ const [validFrom, setValidFrom] = React22.useState(date ?? hoje());
872
+ const [validTo, setValidTo] = React22.useState("");
873
+ const [weekInterval, setWeekInterval] = React22.useState("0");
874
+ const [saving, setSaving] = React22.useState(false);
875
+ React22.useEffect(() => {
778
876
  if (!open) return;
779
877
  if (editing) {
780
878
  setAssigneeId(editing.assigneeId);
@@ -964,10 +1062,10 @@ function BlockDialog({
964
1062
  }
965
1063
  function LostSlotsCard({ range, selectedStatuses, onPick }) {
966
1064
  const provider2 = useAgendaProvider();
967
- const [counts, setCounts] = React21.useState(null);
968
- const [falhou, setFalhou] = React21.useState(false);
1065
+ const [counts, setCounts] = React22.useState(null);
1066
+ const [falhou, setFalhou] = React22.useState(false);
969
1067
  const key = range ? `${range.start}|${range.end}` : null;
970
- React21.useEffect(() => {
1068
+ React22.useEffect(() => {
971
1069
  if (!range || !provider2.getLostCounts) {
972
1070
  setCounts(null);
973
1071
  return;
@@ -1024,14 +1122,14 @@ function ScheduleGridDialog({ open, onClose, professionalId }) {
1024
1122
  const t2 = useTranslation();
1025
1123
  const professionals = useAgendaStore((s) => s.professionals);
1026
1124
  const invalidateSchedules2 = useAgendaStore((s) => s.invalidateSchedules);
1027
- const [search, setSearch] = React21.useState("");
1028
- const [selectedId, setSelectedId] = React21.useState(null);
1029
- React21.useEffect(() => {
1125
+ const [search, setSearch] = React22.useState("");
1126
+ const [selectedId, setSelectedId] = React22.useState(null);
1127
+ React22.useEffect(() => {
1030
1128
  if (!open) return;
1031
1129
  setSelectedId((current) => professionalId ?? current ?? professionals[0]?.id ?? null);
1032
1130
  }, [open, professionalId, professionals]);
1033
- const selectedRef = React21.useRef(null);
1034
- React21.useEffect(() => {
1131
+ const selectedRef = React22.useRef(null);
1132
+ React22.useEffect(() => {
1035
1133
  if (!open || !selectedId) return;
1036
1134
  const id = window.setTimeout(() => {
1037
1135
  selectedRef.current?.scrollIntoView({ block: "nearest" });
@@ -1386,7 +1484,7 @@ function BookingPaymentPanel({ orderId, orderTotal, services, onClose, onPayment
1386
1484
  /* @__PURE__ */ jsxs(
1387
1485
  "a",
1388
1486
  {
1389
- href: `#/financial/receivables/detail/${detail.invoiceId}`,
1487
+ href: detail.status === "paid" || detail.status === "cancelled" ? `#/financial/receivables/detail/${detail.invoiceId}` : `#/financial/receivables/${detail.invoiceId}/edit`,
1390
1488
  className: "inline-flex items-center gap-1.5 text-xs font-medium text-primary hover:underline ml-auto",
1391
1489
  children: [
1392
1490
  t2("agenda.payment.viewInvoice"),
@@ -1695,9 +1793,9 @@ function ClientHistoryPanel({ clientId, currentBookingId, formatCurrency: format
1695
1793
  const t2 = useTranslation();
1696
1794
  const config = useAgendaConfig();
1697
1795
  const provider2 = useAgendaProvider();
1698
- const [bookings, setBookings] = React21.useState(null);
1699
- const [failed, setFailed] = React21.useState(false);
1700
- React21.useEffect(() => {
1796
+ const [bookings, setBookings] = React22.useState(null);
1797
+ const [failed, setFailed] = React22.useState(false);
1798
+ React22.useEffect(() => {
1701
1799
  let alive = true;
1702
1800
  if (!provider2.getClientHistory) {
1703
1801
  setBookings([]);
@@ -1749,13 +1847,13 @@ function ClientHistoryPanel({ clientId, currentBookingId, formatCurrency: format
1749
1847
  }
1750
1848
  function BookingInterestsPanel({ clientId, bookingId, value, onChange }) {
1751
1849
  const provider2 = useAgendaProvider();
1752
- const [crmInterests, setCrmInterests] = React21.useState([]);
1753
- const [term, setTerm] = React21.useState("");
1754
- const [options, setOptions] = React21.useState([]);
1755
- const initializedFor = React21.useRef(null);
1756
- const onChangeRef = React21.useRef(onChange);
1850
+ const [crmInterests, setCrmInterests] = React22.useState([]);
1851
+ const [term, setTerm] = React22.useState("");
1852
+ const [options, setOptions] = React22.useState([]);
1853
+ const initializedFor = React22.useRef(null);
1854
+ const onChangeRef = React22.useRef(onChange);
1757
1855
  onChangeRef.current = onChange;
1758
- React21.useEffect(() => {
1856
+ React22.useEffect(() => {
1759
1857
  if (!clientId) {
1760
1858
  setCrmInterests([]);
1761
1859
  initializedFor.current = null;
@@ -1788,7 +1886,7 @@ function BookingInterestsPanel({ clientId, bookingId, value, onChange }) {
1788
1886
  vivo = false;
1789
1887
  };
1790
1888
  }, [clientId, bookingId, provider2]);
1791
- React21.useEffect(() => {
1889
+ React22.useEffect(() => {
1792
1890
  if (!term.trim()) {
1793
1891
  setOptions([]);
1794
1892
  return;
@@ -2143,13 +2241,13 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
2143
2241
  const [markingWaiting, setMarkingWaiting] = useState(false);
2144
2242
  const [preenchendoForm, setPreenchendoForm] = useState(null);
2145
2243
  const [formsVersao, setFormsVersao] = useState(0);
2146
- const professionalOptions = React21.useMemo(() => {
2244
+ const professionalOptions = React22.useMemo(() => {
2147
2245
  const doLocal = professionalsForLocation(professionals, locationId, professionalId);
2148
2246
  if (!professionalId || doLocal.some((p) => p.id === professionalId)) return doLocal;
2149
2247
  const nome = loadedBookings.find((b) => b.id === bookingId)?.professionalName;
2150
2248
  return [...doLocal, { id: professionalId, name: nome ?? "\u2014", avatarUrl: null }];
2151
2249
  }, [professionals, professionalId, locationId, loadedBookings, bookingId]);
2152
- React21.useEffect(() => {
2250
+ React22.useEffect(() => {
2153
2251
  const fora = shouldClearProfessional(professionals, locationId, professionalId);
2154
2252
  if (!fora) return;
2155
2253
  setProfessionalId("");
@@ -2455,7 +2553,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
2455
2553
  setCheckingSeries(false);
2456
2554
  };
2457
2555
  }, [open, seriesDates, professionalId, startTime, totalDuration, bookingId, activeUnitId, provider2]);
2458
- const [feriado, setFeriado] = React21.useState(null);
2556
+ const [feriado, setFeriado] = React22.useState(null);
2459
2557
  useEffect(() => {
2460
2558
  if (!open || !date || !provider2.getHolidays) {
2461
2559
  setFeriado(null);
@@ -3483,9 +3581,9 @@ function useSeriesDelete(booking, enabled, onDone) {
3483
3581
  }
3484
3582
  function SeriesDeleteChoice({ seriesSize, deleting, onPick, onCancel }) {
3485
3583
  const t2 = useTranslation();
3486
- const allLabel = t2("agenda.recurrence.scopeSeries").replace("{{count}}", seriesSize ? String(seriesSize) : "\u2026");
3487
- const btn = "w-full rounded px-2 py-1 text-left text-[11px] font-medium text-destructive hover:bg-destructive/10 disabled:opacity-40 transition-colors";
3488
- return /* @__PURE__ */ jsxs("div", { className: "px-3 py-2 space-y-1", children: [
3584
+ const allLabel = t2("agenda.recurrence.scopeSeriesShort").replace("{{count}}", seriesSize ? String(seriesSize) : "\u2026");
3585
+ const btn = "flex-1 min-w-0 rounded border border-destructive/30 px-2 py-1 text-center text-[11px] font-medium text-destructive hover:bg-destructive/10 disabled:opacity-40 transition-colors";
3586
+ return /* @__PURE__ */ jsxs("div", { className: "px-3 py-2 space-y-1.5", children: [
3489
3587
  /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
3490
3588
  /* @__PURE__ */ jsx("span", { className: "text-[11px] font-medium text-destructive", children: t2("agenda.recurrence.scopeDeleteTitle") }),
3491
3589
  /* @__PURE__ */ jsx(
@@ -3499,8 +3597,10 @@ function SeriesDeleteChoice({ seriesSize, deleting, onPick, onCancel }) {
3499
3597
  }
3500
3598
  )
3501
3599
  ] }),
3502
- /* @__PURE__ */ jsx("button", { type: "button", className: btn, disabled: deleting !== null, onClick: () => onPick("this"), children: deleting === "this" ? t2("agenda.appointment.deleting") : t2("agenda.recurrence.scopeThis") }),
3503
- /* @__PURE__ */ jsx("button", { type: "button", className: btn, disabled: deleting !== null, onClick: () => onPick("series"), children: deleting === "series" ? t2("agenda.appointment.deleting") : allLabel })
3600
+ /* @__PURE__ */ jsxs("div", { className: "flex items-stretch gap-1.5", children: [
3601
+ /* @__PURE__ */ jsx("button", { type: "button", className: btn, disabled: deleting !== null, onClick: () => onPick("this"), children: deleting === "this" ? t2("agenda.appointment.deleting") : t2("agenda.recurrence.scopeThisShort") }),
3602
+ /* @__PURE__ */ jsx("button", { type: "button", className: btn, disabled: deleting !== null, onClick: () => onPick("series"), children: deleting === "series" ? t2("agenda.appointment.deleting") : allLabel })
3603
+ ] })
3504
3604
  ] });
3505
3605
  }
3506
3606
  function fmtTime(iso) {
@@ -4225,9 +4325,9 @@ function CalendarView() {
4225
4325
  const [serviceFilter, setServiceFilter] = useState([]);
4226
4326
  const [hideEmptyGrids, setHideEmptyGrids] = useState(false);
4227
4327
  const [schedulesOpen, setSchedulesOpen] = useState(false);
4228
- const [blockFor, setBlockFor] = React21.useState(null);
4229
- const [schedulesFor, setSchedulesFor] = React21.useState(null);
4230
- const [editingBlock, setEditingBlock] = React21.useState(null);
4328
+ const [blockFor, setBlockFor] = React22.useState(null);
4329
+ const [schedulesFor, setSchedulesFor] = React22.useState(null);
4330
+ const [editingBlock, setEditingBlock] = React22.useState(null);
4231
4331
  const isMobile = useIsMobile();
4232
4332
  const resourceCapable = professionals.length > 0;
4233
4333
  const viewOptions = resourceCapable ? RESOURCE_VIEW_OPTIONS : PLAIN_VIEW_OPTIONS;
@@ -5570,7 +5670,7 @@ function getAgendaTenantId() {
5570
5670
  function AgendaPage({ config, provider: provider2, store, children }) {
5571
5671
  const runtime = usePluginRuntimeOptional();
5572
5672
  const tenantId = runtime?.context.tenant?.id;
5573
- React21.useEffect(() => {
5673
+ React22.useEffect(() => {
5574
5674
  setAgendaTenantId(tenantId);
5575
5675
  return () => setAgendaTenantId(void 0);
5576
5676
  }, [tenantId]);
@@ -5586,10 +5686,10 @@ function CheckinDialog({
5586
5686
  onClose,
5587
5687
  formatCurrency: formatCurrency2 = brl
5588
5688
  }) {
5589
- const [busy, setBusy] = React21.useState(false);
5590
- const [preenchendo, setPreenchendo] = React21.useState(null);
5591
- const [preenchidos, setPreenchidos] = React21.useState(0);
5592
- const [pendencias, setPendencias] = React21.useState([]);
5689
+ const [busy, setBusy] = React22.useState(false);
5690
+ const [preenchendo, setPreenchendo] = React22.useState(null);
5691
+ const [preenchidos, setPreenchidos] = React22.useState(0);
5692
+ const [pendencias, setPendencias] = React22.useState([]);
5593
5693
  const blocking = requirements.filter((r) => r.blocking);
5594
5694
  const paid = booking.paymentStatus === "paid";
5595
5695
  const hasBill = !!booking.paymentStatus && booking.paymentStatus !== "none";
@@ -5699,7 +5799,7 @@ function CheckinDialog({
5699
5799
  }
5700
5800
  function PaymentStatusBadge(props) {
5701
5801
  const t2 = useTranslation();
5702
- const labels = React21.useMemo(() => ({
5802
+ const labels = React22.useMemo(() => ({
5703
5803
  none: t2("agenda.payment.title"),
5704
5804
  pending: t2("agenda.payment.pending"),
5705
5805
  partial: t2("agenda.payment.partial"),
@@ -5859,7 +5959,7 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
5859
5959
  const config = useAgendaConfig();
5860
5960
  const provider2 = useAgendaProvider();
5861
5961
  const statusLabel = (v) => config.statuses.find((x) => x.value === v)?.label ?? v;
5862
- const checkinStatuses = React21.useMemo(
5962
+ const checkinStatuses = React22.useMemo(
5863
5963
  () => CHECKIN_STATUSES.map((v) => config.statuses.find((x) => x.value === v)).filter((x) => !!x),
5864
5964
  [config.statuses]
5865
5965
  );
@@ -5867,14 +5967,14 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
5867
5967
  const loading = useAgendaStore((s) => s.bookingsLoading);
5868
5968
  const fetchBookings = useAgendaStore((s) => s.fetchBookings);
5869
5969
  const updateBookingStatus = useAgendaStore((s) => s.updateBookingStatus);
5870
- const [term, setTerm] = React21.useState("");
5871
- const [busyId, setBusyId] = React21.useState(null);
5872
- const [myPersonId, setMyPersonId] = React21.useState(void 0);
5873
- const [startRefusal, setStartRefusal] = React21.useState(null);
5874
- const [checkouts, setCheckouts] = React21.useState([]);
5875
- const [conferindo, setConferindo] = React21.useState(null);
5876
- const [checkoutRefusal, setCheckoutRefusal] = React21.useState(null);
5877
- const [panel, setPanel] = React21.useState(null);
5970
+ const [term, setTerm] = React22.useState("");
5971
+ const [busyId, setBusyId] = React22.useState(null);
5972
+ const [myPersonId, setMyPersonId] = React22.useState(void 0);
5973
+ const [startRefusal, setStartRefusal] = React22.useState(null);
5974
+ const [checkouts, setCheckouts] = React22.useState([]);
5975
+ const [conferindo, setConferindo] = React22.useState(null);
5976
+ const [checkoutRefusal, setCheckoutRefusal] = React22.useState(null);
5977
+ const [panel, setPanel] = React22.useState(null);
5878
5978
  const reviewing = panel?.kind === "review" ? panel.booking : null;
5879
5979
  const editing = panel?.kind === "edit" ? panel.booking : null;
5880
5980
  const charging = panel?.kind === "charge" ? panel.booking : null;
@@ -5883,17 +5983,17 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
5883
5983
  const setCharging = (b) => setPanel(b ? { kind: "charge", booking: b } : null);
5884
5984
  const can = usePermissionOptional();
5885
5985
  const canReceive = can("financial", "create") || can("financial.receivables", "create");
5886
- React21.useEffect(() => {
5986
+ React22.useEffect(() => {
5887
5987
  void fetchBookings(todayRange(), [...CHECKIN_STATUSES]);
5888
5988
  }, [fetchBookings]);
5889
- const [shownStatuses, setShownStatuses] = React21.useState([]);
5890
- const [shownPros, setShownPros] = React21.useState([]);
5891
- const prosToday = React21.useMemo(() => {
5989
+ const [shownStatuses, setShownStatuses] = React22.useState([]);
5990
+ const [shownPros, setShownPros] = React22.useState([]);
5991
+ const prosToday = React22.useMemo(() => {
5892
5992
  const seen = /* @__PURE__ */ new Map();
5893
5993
  for (const b of bookings) if (b.professionalName) seen.set(b.professionalName, b.professionalName);
5894
5994
  return [...seen.keys()].sort();
5895
5995
  }, [bookings]);
5896
- const cards = React21.useMemo(() => {
5996
+ const cards = React22.useMemo(() => {
5897
5997
  const q = term.trim().toLowerCase();
5898
5998
  return bookings.filter((b) => !shownStatuses.length || shownStatuses.includes(b.status)).filter((b) => !shownPros.length || b.professionalName && shownPros.includes(b.professionalName)).filter((b) => !q || (b.clientName ?? "").toLowerCase().includes(q) || (b.professionalName ?? "").toLowerCase().includes(q)).sort((a, b) => a.startsAt.localeCompare(b.startsAt));
5899
5999
  }, [bookings, term, shownStatuses, shownPros]);
@@ -5906,8 +6006,8 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
5906
6006
  return out;
5907
6007
  }
5908
6008
  const money = (n) => n.toLocaleString("pt-BR", { style: "currency", currency: "BRL" });
5909
- const [lost, setLost] = React21.useState({ cancelled: 0, noShow: 0 });
5910
- React21.useEffect(() => {
6009
+ const [lost, setLost] = React22.useState({ cancelled: 0, noShow: 0 });
6010
+ React22.useEffect(() => {
5911
6011
  if (!provider2.getLostCounts) return;
5912
6012
  let cancelado = false;
5913
6013
  provider2.getLostCounts(todayRange()).then((c) => {
@@ -5918,7 +6018,7 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
5918
6018
  cancelado = true;
5919
6019
  };
5920
6020
  }, [provider2]);
5921
- const openAmount = React21.useMemo(
6021
+ const openAmount = React22.useMemo(
5922
6022
  () => bookings.filter((b) => b.orderTotal && b.paymentStatus && b.paymentStatus !== "paid").reduce((soma, b) => soma + (b.orderTotal ?? 0), 0),
5923
6023
  [bookings]
5924
6024
  );
@@ -5930,7 +6030,7 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
5930
6030
  amount: m.paidAmount
5931
6031
  }));
5932
6032
  }
5933
- React21.useEffect(() => {
6033
+ React22.useEffect(() => {
5934
6034
  let vivo = true;
5935
6035
  (async () => {
5936
6036
  try {
@@ -5944,17 +6044,17 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
5944
6044
  vivo = false;
5945
6045
  };
5946
6046
  }, []);
5947
- const recarregarCheckouts = React21.useCallback(async () => {
6047
+ const recarregarCheckouts = React22.useCallback(async () => {
5948
6048
  try {
5949
6049
  setCheckouts(await provider2.getPendingCheckouts?.() ?? []);
5950
6050
  } catch {
5951
6051
  setCheckouts([]);
5952
6052
  }
5953
6053
  }, []);
5954
- React21.useEffect(() => {
6054
+ React22.useEffect(() => {
5955
6055
  void recarregarCheckouts();
5956
6056
  }, [recarregarCheckouts]);
5957
- React21.useEffect(() => {
6057
+ React22.useEffect(() => {
5958
6058
  const parar = provider2.subscribeToBookingChanges?.(() => {
5959
6059
  void recarregarCheckouts();
5960
6060
  void fetchBookings(todayRange(), [...CHECKIN_STATUSES]);
@@ -6337,7 +6437,7 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
6337
6437
  function CheckinPage({ config, provider: provider2, store, onReceive, onOpenBooking }) {
6338
6438
  const runtime = usePluginRuntimeOptional();
6339
6439
  const tenantId = runtime?.context.tenant?.id;
6340
- React21.useEffect(() => {
6440
+ React22.useEffect(() => {
6341
6441
  setAgendaTenantId(tenantId);
6342
6442
  return () => setAgendaTenantId(void 0);
6343
6443
  }, [tenantId]);
@@ -6350,11 +6450,11 @@ function hoje2() {
6350
6450
  }
6351
6451
  function PersonAttendanceBlock({ personId }) {
6352
6452
  const provider2 = useAgendaProvider();
6353
- const [pessoa, setPessoa] = React21.useState(void 0);
6354
- const [booking, setBooking] = React21.useState(null);
6355
- const [indo, setIndo] = React21.useState(false);
6356
- const [recusa, setRecusa] = React21.useState(null);
6357
- React21.useEffect(() => {
6453
+ const [pessoa, setPessoa] = React22.useState(void 0);
6454
+ const [booking, setBooking] = React22.useState(null);
6455
+ const [indo, setIndo] = React22.useState(false);
6456
+ const [recusa, setRecusa] = React22.useState(null);
6457
+ React22.useEffect(() => {
6358
6458
  let vivo = true;
6359
6459
  (async () => {
6360
6460
  const eu = await provider2.getCurrentPersonId?.() ?? null;
@@ -8815,16 +8915,16 @@ function TarefaPane({ titulo, aoVoltar, children }) {
8815
8915
  }
8816
8916
  function AttendanceRuler({ booking, open, onClose, onFinished, frame = "modal" }) {
8817
8917
  const provider2 = useAgendaProvider();
8818
- const [versao, setVersao] = React21.useState(0);
8819
- const [statusLocal, setStatusLocal] = React21.useState(booking.status);
8820
- React21.useEffect(() => {
8918
+ const [versao, setVersao] = React22.useState(0);
8919
+ const [statusLocal, setStatusLocal] = React22.useState(booking.status);
8920
+ React22.useEffect(() => {
8821
8921
  setStatusLocal(booking.status);
8822
8922
  }, [booking.id, booking.status]);
8823
8923
  const emCurso = statusLocal === "in_progress";
8824
8924
  const naoIniciado = statusLocal === "scheduled" || statusLocal === "confirmed" || statusLocal === "waiting";
8825
8925
  const somenteLeitura = !emCurso;
8826
- const [eu, setEu] = React21.useState(void 0);
8827
- React21.useEffect(() => {
8926
+ const [eu, setEu] = React22.useState(void 0);
8927
+ React22.useEffect(() => {
8828
8928
  if (!naoIniciado) return;
8829
8929
  let vivo = true;
8830
8930
  (async () => {
@@ -8835,31 +8935,31 @@ function AttendanceRuler({ booking, open, onClose, onFinished, frame = "modal" }
8835
8935
  vivo = false;
8836
8936
  };
8837
8937
  }, [naoIniciado, provider2]);
8838
- const [iniciando, setIniciando] = React21.useState(false);
8839
- const [recusaInicio, setRecusaInicio] = React21.useState(null);
8840
- const [rows, setRows] = React21.useState(null);
8841
- const [record, setRecord] = React21.useState(null);
8842
- const [erro, setErro] = React21.useState(null);
8843
- const [contratando, setContratando] = React21.useState(false);
8844
- const [servicos, setServicos] = React21.useState([]);
8845
- const [marcados, setMarcados] = React21.useState({});
8846
- const [salvando, setSalvando] = React21.useState(false);
8847
- const [confirmando, setConfirmando] = React21.useState(null);
8848
- const [encerrando, setEncerrando] = React21.useState(false);
8849
- const [avisoFinal, setAvisoFinal] = React21.useState(null);
8850
- const [preenchendo, setPreenchendo] = React21.useState(null);
8851
- const [avaliando, setAvaliando] = React21.useState(false);
8852
- const [lancando, setLancando] = React21.useState(null);
8853
- const [executando, setExecutando] = React21.useState(null);
8854
- const [vinculando, setVinculando] = React21.useState(null);
8855
- const [candidatos, setCandidatos] = React21.useState(null);
8856
- const [buscaInsumo, setBuscaInsumo] = React21.useState("");
8857
- const [insumos, setInsumos] = React21.useState([]);
8858
- const [usados, setUsados] = React21.useState([]);
8859
- const [busca, setBusca] = React21.useState("");
8860
- const [catalogo, setCatalogo] = React21.useState([]);
8861
- const [indicados, setIndicados] = React21.useState([]);
8862
- React21.useEffect(() => {
8938
+ const [iniciando, setIniciando] = React22.useState(false);
8939
+ const [recusaInicio, setRecusaInicio] = React22.useState(null);
8940
+ const [rows, setRows] = React22.useState(null);
8941
+ const [record, setRecord] = React22.useState(null);
8942
+ const [erro, setErro] = React22.useState(null);
8943
+ const [contratando, setContratando] = React22.useState(false);
8944
+ const [servicos, setServicos] = React22.useState([]);
8945
+ const [marcados, setMarcados] = React22.useState({});
8946
+ const [salvando, setSalvando] = React22.useState(false);
8947
+ const [confirmando, setConfirmando] = React22.useState(null);
8948
+ const [encerrando, setEncerrando] = React22.useState(false);
8949
+ const [avisoFinal, setAvisoFinal] = React22.useState(null);
8950
+ const [preenchendo, setPreenchendo] = React22.useState(null);
8951
+ const [avaliando, setAvaliando] = React22.useState(false);
8952
+ const [lancando, setLancando] = React22.useState(null);
8953
+ const [executando, setExecutando] = React22.useState(null);
8954
+ const [vinculando, setVinculando] = React22.useState(null);
8955
+ const [candidatos, setCandidatos] = React22.useState(null);
8956
+ const [buscaInsumo, setBuscaInsumo] = React22.useState("");
8957
+ const [insumos, setInsumos] = React22.useState([]);
8958
+ const [usados, setUsados] = React22.useState([]);
8959
+ const [busca, setBusca] = React22.useState("");
8960
+ const [catalogo, setCatalogo] = React22.useState([]);
8961
+ const [indicados, setIndicados] = React22.useState([]);
8962
+ React22.useEffect(() => {
8863
8963
  if (!open) return;
8864
8964
  let vivo = true;
8865
8965
  (async () => {
@@ -8916,7 +9016,7 @@ function AttendanceRuler({ booking, open, onClose, onFinished, frame = "modal" }
8916
9016
  const d = ORDEM.indexOf(a.kind) - ORDEM.indexOf(b.kind);
8917
9017
  return d !== 0 ? d : a.id.localeCompare(b.id);
8918
9018
  };
8919
- const doAtendimento = React21.useMemo(() => {
9019
+ const doAtendimento = React22.useMemo(() => {
8920
9020
  const m = /* @__PURE__ */ new Map();
8921
9021
  for (const r of (rows ?? []).filter((x) => x.scope === "attendance")) {
8922
9022
  const k = `${r.kind}:${r.refId ?? ""}`;
@@ -8929,7 +9029,7 @@ function AttendanceRuler({ booking, open, onClose, onFinished, frame = "modal" }
8929
9029
  }
8930
9030
  return [...m.values()].sort(porOrdem);
8931
9031
  }, [rows]);
8932
- const porServico = React21.useMemo(() => {
9032
+ const porServico = React22.useMemo(() => {
8933
9033
  const m = /* @__PURE__ */ new Map();
8934
9034
  for (const r of rows ?? []) {
8935
9035
  if (r.scope === "attendance") continue;
@@ -8954,7 +9054,7 @@ function AttendanceRuler({ booking, open, onClose, onFinished, frame = "modal" }
8954
9054
  }, [rows]);
8955
9055
  const faltaParaTerminar = (rows ?? []).filter((r) => r.blocksFinish && r.status === "pending");
8956
9056
  const tarefaAberta = Boolean(vinculando || executando || lancando || avaliando || confirmando || contratando || preenchendo?.refId && booking.clientId);
8957
- React21.useEffect(() => {
9057
+ React22.useEffect(() => {
8958
9058
  if (!avaliando) return;
8959
9059
  let vivo = true;
8960
9060
  const t2 = setTimeout(async () => {
@@ -8970,7 +9070,7 @@ function AttendanceRuler({ booking, open, onClose, onFinished, frame = "modal" }
8970
9070
  clearTimeout(t2);
8971
9071
  };
8972
9072
  }, [avaliando, busca]);
8973
- React21.useEffect(() => {
9073
+ React22.useEffect(() => {
8974
9074
  if (!lancando) return;
8975
9075
  let vivo = true;
8976
9076
  const t2 = setTimeout(async () => {
@@ -8994,7 +9094,7 @@ function AttendanceRuler({ booking, open, onClose, onFinished, frame = "modal" }
8994
9094
  return [...xs, ...kit.filter((k) => !tem.has(k.productId)).map((k) => ({ productId: k.productId, name: k.name, qtd: k.quantity, unitId: k.unitId }))];
8995
9095
  });
8996
9096
  }
8997
- React21.useEffect(() => {
9097
+ React22.useEffect(() => {
8998
9098
  if (!vinculando) return;
8999
9099
  let vivo = true;
9000
9100
  (async () => {
@@ -9710,10 +9810,10 @@ var STATUS_LABEL = {
9710
9810
  };
9711
9811
  function PersonAttendancesWidget({ personId }) {
9712
9812
  const provider2 = useAgendaProvider();
9713
- const [linhas, setLinhas] = React21.useState(null);
9714
- const [aberto, setAberto] = React21.useState(null);
9715
- const [emCurso, setEmCurso] = React21.useState(null);
9716
- const recarregar = React21.useCallback(async () => {
9813
+ const [linhas, setLinhas] = React22.useState(null);
9814
+ const [aberto, setAberto] = React22.useState(null);
9815
+ const [emCurso, setEmCurso] = React22.useState(null);
9816
+ const recarregar = React22.useCallback(async () => {
9717
9817
  try {
9718
9818
  const r = await provider2.getPersonAttendances?.(personId);
9719
9819
  setLinhas(r ?? []);
@@ -9721,10 +9821,10 @@ function PersonAttendancesWidget({ personId }) {
9721
9821
  setLinhas([]);
9722
9822
  }
9723
9823
  }, [personId, provider2]);
9724
- React21.useEffect(() => {
9824
+ React22.useEffect(() => {
9725
9825
  void recarregar();
9726
9826
  }, [recarregar]);
9727
- React21.useEffect(() => {
9827
+ React22.useEffect(() => {
9728
9828
  let vivo = true;
9729
9829
  (async () => {
9730
9830
  const eu = await provider2.getCurrentPersonId?.() ?? null;
@@ -9822,8 +9922,8 @@ function PersonAttendancesWidget({ personId }) {
9822
9922
  }
9823
9923
  function PersonServicesWidget({ personId }) {
9824
9924
  const provider2 = useAgendaProvider();
9825
- const [rows, setRows] = React21.useState(null);
9826
- React21.useEffect(() => {
9925
+ const [rows, setRows] = React22.useState(null);
9926
+ React22.useEffect(() => {
9827
9927
  let alive = true;
9828
9928
  (async () => {
9829
9929
  try {
@@ -10196,6 +10296,8 @@ var en = {
10196
10296
  "agenda.recurrence.scopeDeleteTitle": "Delete recurring appointment",
10197
10297
  "agenda.recurrence.scopeThis": "This appointment",
10198
10298
  "agenda.recurrence.scopeSeries": "All in the series ({{count}})",
10299
+ "agenda.recurrence.scopeThisShort": "This one",
10300
+ "agenda.recurrence.scopeSeriesShort": "All ({{count}})",
10199
10301
  "agenda.recurrence.dateStaysLocal": "The new date applies to this appointment only: every occurrence keeps its own day, and only the time becomes {{time}}."
10200
10302
  };
10201
10303
 
@@ -10503,6 +10605,8 @@ var ptBR = {
10503
10605
  "agenda.recurrence.scopeDeleteTitle": "Excluir agendamento recorrente",
10504
10606
  "agenda.recurrence.scopeThis": "Este agendamento",
10505
10607
  "agenda.recurrence.scopeSeries": "Todos da s\xE9rie ({{count}})",
10608
+ "agenda.recurrence.scopeThisShort": "S\xF3 este",
10609
+ "agenda.recurrence.scopeSeriesShort": "Todos ({{count}})",
10506
10610
  "agenda.recurrence.dateStaysLocal": "A data nova vale s\xF3 para este agendamento: cada ocorr\xEAncia da s\xE9rie mant\xE9m o dia dela, e s\xF3 o hor\xE1rio passa a ser {{time}}."
10507
10611
  };
10508
10612
 
@@ -15103,6 +15207,63 @@ GRANT EXECUTE ON FUNCTION public.agenda_series_update(uuid, text, uuid, uuid, uu
15103
15207
  COMMENT ON FUNCTION public.agenda_series_update(uuid, text, uuid, uuid, uuid, text, text, text[]) IS
15104
15208
  'Aplica a mesma mudan\xE7a a todas as ocorr\xEAncias da s\xE9rie num UPDATE s\xF3, preservando o DIA de cada uma. N\xE3o toca nas linhas de servi\xE7o \u2014 essa regra vive no cliente.';
15105
15209
  `;
15210
+ var MIGRATION_020_A_LISTA_DA_VEZ_SE_LIGA_NO_CADASTRO_DA_UNIDADE = `-- A lista da vez se liga no cadastro da unidade.
15211
+ --
15212
+ -- No V1 o interruptor "Ativar lista da vez nesta unidade" e o raio da cerca
15213
+ -- moram no cadastro da empresa (V1: companies.turn_queue_enabled,
15214
+ -- turn_queue_radius_m). Em 016 a fila nasceu sem tela de configura\xE7\xE3o: a linha
15215
+ -- surge sozinha, LIGADA, no primeiro "estou dispon\xEDvel", e o app s\xF3 pode l\xEA-la.
15216
+ --
15217
+ -- Esta RPC \xE9 a porta que faltava \u2014 a aba "Lista da vez" da unidade grava por
15218
+ -- ela. Quem configura \xE9 quem administra a filial (settings.manage NA unidade).
15219
+ -- A coordenada continua sendo a do endere\xE7o da unidade (142); n\xE3o h\xE1 segunda
15220
+ -- latitude aqui.
15221
+ --
15222
+ -- Chave que conecta: plg_agenda_turn_queues.unit_id = app.units.id
15223
+ -- (UNIQUE tenant_id, unit_id, queue_key). Idempotente.
15224
+
15225
+ CREATE OR REPLACE FUNCTION public.agenda_turn_queue_configure(
15226
+ p_unit_id uuid,
15227
+ p_enabled boolean,
15228
+ p_geofence_required boolean DEFAULT NULL,
15229
+ p_radius_meters integer DEFAULT NULL
15230
+ )
15231
+ RETURNS public.plg_agenda_turn_queues
15232
+ LANGUAGE plpgsql
15233
+ SECURITY DEFINER
15234
+ SET search_path TO ''
15235
+ AS $function$
15236
+ DECLARE
15237
+ v_tenant uuid := app.current_tenant_id();
15238
+ v_row public.plg_agenda_turn_queues;
15239
+ BEGIN
15240
+ IF v_tenant IS NULL OR auth.uid() IS NULL THEN
15241
+ RAISE EXCEPTION 'agenda_turn_queue_configure: no session or no active tenant' USING ERRCODE = '42501';
15242
+ END IF;
15243
+ IF NOT EXISTS (SELECT 1 FROM app.units u WHERE u.id = p_unit_id AND u.tenant_id = v_tenant) THEN
15244
+ RAISE EXCEPTION 'agenda_turn_queue_configure: unit does not belong to this tenant' USING ERRCODE = '42501';
15245
+ END IF;
15246
+ IF NOT app.has_unit(p_unit_id) OR NOT app.has_permission('settings.manage', p_unit_id) THEN
15247
+ RAISE EXCEPTION 'agenda_turn_queue_configure: settings.manage is required on this unit' USING ERRCODE = '42501';
15248
+ END IF;
15249
+ IF p_radius_meters IS NOT NULL AND (p_radius_meters < 10 OR p_radius_meters > 1000) THEN
15250
+ RAISE EXCEPTION 'agenda_turn_queue_configure: radius must be between 10 and 1000 meters' USING ERRCODE = '22023';
15251
+ END IF;
15252
+
15253
+ INSERT INTO public.plg_agenda_turn_queues AS q (tenant_id, unit_id, is_active, geofence_required, geofence_radius_meters)
15254
+ VALUES (v_tenant, p_unit_id, coalesce(p_enabled, false), coalesce(p_geofence_required, true), coalesce(p_radius_meters, 80))
15255
+ ON CONFLICT (tenant_id, unit_id, queue_key) DO UPDATE
15256
+ SET is_active = coalesce(p_enabled, q.is_active),
15257
+ geofence_required = coalesce(p_geofence_required, q.geofence_required),
15258
+ geofence_radius_meters = coalesce(p_radius_meters, q.geofence_radius_meters),
15259
+ updated_at = now()
15260
+ RETURNING * INTO v_row;
15261
+ RETURN v_row;
15262
+ END $function$;
15263
+
15264
+ REVOKE ALL ON FUNCTION public.agenda_turn_queue_configure(uuid, boolean, boolean, integer) FROM PUBLIC, anon;
15265
+ GRANT EXECUTE ON FUNCTION public.agenda_turn_queue_configure(uuid, boolean, boolean, integer) TO authenticated, service_role;
15266
+ `;
15106
15267
  var MIGRATIONS = [
15107
15268
  { id: "000_baseline", sql: MIGRATION_000_BASELINE },
15108
15269
  { id: "001_the_connector_definers_keep_a_pinned_path", sql: MIGRATION_001_THE_CONNECTOR_DEFINERS_KEEP_A_PINNED_PATH },
@@ -15123,7 +15284,8 @@ var MIGRATIONS = [
15123
15284
  { id: "016_a_vez_e_um_fato", sql: MIGRATION_016_A_VEZ_E_UM_FATO },
15124
15285
  { id: "017_a_equipe_do_dia_aparece", sql: MIGRATION_017_A_EQUIPE_DO_DIA_APARECE },
15125
15286
  { id: "018_o_agendamento_que_se_repete", sql: MIGRATION_018_O_AGENDAMENTO_QUE_SE_REPETE },
15126
- { id: "019_a_serie_nasce_numa_viagem_so", sql: MIGRATION_019_A_SERIE_NASCE_NUMA_VIAGEM_SO }
15287
+ { id: "019_a_serie_nasce_numa_viagem_so", sql: MIGRATION_019_A_SERIE_NASCE_NUMA_VIAGEM_SO },
15288
+ { id: "020_a_lista_da_vez_se_liga_no_cadastro_da_unidade", sql: MIGRATION_020_A_LISTA_DA_VEZ_SE_LIGA_NO_CADASTRO_DA_UNIDADE }
15127
15289
  ];
15128
15290
  var channelSequence = 0;
15129
15291
  function client() {
@@ -15270,9 +15432,9 @@ function failureMessage(error) {
15270
15432
  }
15271
15433
  function AvailabilityHeaderControl({ provider: provider2 }) {
15272
15434
  const unitId = useActiveUnitId();
15273
- const [status, setStatus] = React21.useState(null);
15274
- const [busy, setBusy] = React21.useState(false);
15275
- const refresh = React21.useCallback(async () => {
15435
+ const [status, setStatus] = React22.useState(null);
15436
+ const [busy, setBusy] = React22.useState(false);
15437
+ const refresh = React22.useCallback(async () => {
15276
15438
  if (!unitId) {
15277
15439
  setStatus(null);
15278
15440
  return;
@@ -15287,7 +15449,7 @@ function AvailabilityHeaderControl({ provider: provider2 }) {
15287
15449
  setStatus(null);
15288
15450
  }
15289
15451
  }, [provider2, unitId]);
15290
- React21.useEffect(() => {
15452
+ React22.useEffect(() => {
15291
15453
  void refresh();
15292
15454
  return provider2.subscribe(() => {
15293
15455
  void refresh();
@@ -15441,12 +15603,12 @@ function EmptyUnit() {
15441
15603
  }
15442
15604
  function TurnQueuePage({ provider: provider2, agendaProvider, title, subtitle }) {
15443
15605
  const unitId = useActiveUnitId();
15444
- const [agendaProfessionals, setAgendaProfessionals] = React21.useState([]);
15445
- const [snapshot, setSnapshot] = React21.useState(null);
15446
- const [loading, setLoading] = React21.useState(false);
15447
- const [error, setError] = React21.useState(null);
15448
- const [, tick] = React21.useState(0);
15449
- const load = React21.useCallback(async () => {
15606
+ const [agendaProfessionals, setAgendaProfessionals] = React22.useState([]);
15607
+ const [snapshot, setSnapshot] = React22.useState(null);
15608
+ const [loading, setLoading] = React22.useState(false);
15609
+ const [error, setError] = React22.useState(null);
15610
+ const [, tick] = React22.useState(0);
15611
+ const load = React22.useCallback(async () => {
15450
15612
  if (!unitId) {
15451
15613
  setSnapshot(null);
15452
15614
  return;
@@ -15461,10 +15623,10 @@ function TurnQueuePage({ provider: provider2, agendaProvider, title, subtitle })
15461
15623
  setLoading(false);
15462
15624
  }
15463
15625
  }, [provider2, unitId]);
15464
- React21.useEffect(() => {
15626
+ React22.useEffect(() => {
15465
15627
  void load();
15466
15628
  }, [load]);
15467
- React21.useEffect(() => {
15629
+ React22.useEffect(() => {
15468
15630
  if (!unitId) {
15469
15631
  setAgendaProfessionals([]);
15470
15632
  return;
@@ -15479,19 +15641,19 @@ function TurnQueuePage({ provider: provider2, agendaProvider, title, subtitle })
15479
15641
  active = false;
15480
15642
  };
15481
15643
  }, [agendaProvider, unitId]);
15482
- React21.useEffect(() => provider2.subscribe(() => {
15644
+ React22.useEffect(() => provider2.subscribe(() => {
15483
15645
  void load();
15484
15646
  }), [provider2, load]);
15485
- React21.useEffect(() => {
15647
+ React22.useEffect(() => {
15486
15648
  const timer = window.setInterval(() => tick((value) => value + 1), 6e4);
15487
15649
  return () => window.clearInterval(timer);
15488
15650
  }, []);
15489
- const rows = React21.useMemo(() => [...snapshot?.entries ?? []].sort((a, b) => {
15651
+ const rows = React22.useMemo(() => [...snapshot?.entries ?? []].sort((a, b) => {
15490
15652
  const rank = { available: 0, serving: 1, unavailable: 2 };
15491
15653
  return rank[a.status] - rank[b.status] || a.position - b.position;
15492
15654
  }), [snapshot]);
15493
15655
  const queueRows = rows.filter((row) => row.status !== "unavailable");
15494
- const professionals = React21.useMemo(() => {
15656
+ const professionals = React22.useMemo(() => {
15495
15657
  const byId = /* @__PURE__ */ new Map();
15496
15658
  for (const professional of agendaProfessionals) {
15497
15659
  byId.set(professional.id, {
@@ -15514,7 +15676,7 @@ function TurnQueuePage({ provider: provider2, agendaProvider, title, subtitle })
15514
15676
  }, [agendaProfessionals, snapshot]);
15515
15677
  const available = professionals.filter((row) => row.status === "available").length;
15516
15678
  const serving = professionals.filter((row) => row.status === "serving").length;
15517
- const addWalkIn = React21.useCallback((professionalId) => {
15679
+ const addWalkIn = React22.useCallback((professionalId) => {
15518
15680
  if (!unitId) return;
15519
15681
  window.location.hash = "/agenda";
15520
15682
  window.setTimeout(() => {
@@ -15620,8 +15782,8 @@ function createAgendaTurnQueuePlugin(options = {}) {
15620
15782
  const menu = options.labels?.menu ?? "Pr\xF3ximo da vez";
15621
15783
  const title = options.labels?.title ?? menu;
15622
15784
  const subtitle = options.labels?.subtitle ?? "Ordem ao vivo dos profissionais dispon\xEDveis nesta unidade";
15623
- const Page = (() => React21.createElement(TurnQueuePage, { provider: provider2, agendaProvider, title, subtitle }));
15624
- const HeaderControl = (() => React21.createElement(AvailabilityHeaderControl, { provider: provider2 }));
15785
+ const Page = (() => React22.createElement(TurnQueuePage, { provider: provider2, agendaProvider, title, subtitle }));
15786
+ const HeaderControl = (() => React22.createElement(AvailabilityHeaderControl, { provider: provider2 }));
15625
15787
  return {
15626
15788
  id: "agenda-turn-queue",
15627
15789
  name: menu,
@@ -18975,28 +19137,28 @@ function createAgendaPlugin(options) {
18975
19137
  }
18976
19138
  }
18977
19139
  ]);
18978
- const PageComponent = () => React21.createElement(AgendaPage, { config, provider: provider2, store });
19140
+ const PageComponent = () => React22.createElement(AgendaPage, { config, provider: provider2, store });
18979
19141
  const PersonAttendancesBound = (props) => (
18980
19142
  // O provider do PLUGIN, sem a moldura da página: dentro de uma aba a
18981
19143
  // `AgendaPage` traria a altura cheia e a rolagem própria do calendário, e a
18982
19144
  // lista passaria a rolar dentro de uma caixa que já rola.
18983
- React21.createElement(
19145
+ React22.createElement(
18984
19146
  AgendaContextProvider,
18985
19147
  { config, provider: provider2, store },
18986
- React21.createElement(PersonAttendancesWidget, { personId: props.item?.id })
19148
+ React22.createElement(PersonAttendancesWidget, { personId: props.item?.id })
18987
19149
  )
18988
19150
  );
18989
19151
  PersonAttendancesBound.displayName = "AgendaPersonAttendances";
18990
- const PersonAttendanceBlockBound = (props) => React21.createElement(
19152
+ const PersonAttendanceBlockBound = (props) => React22.createElement(
18991
19153
  AgendaContextProvider,
18992
19154
  { config, provider: provider2, store },
18993
- React21.createElement(PersonAttendanceBlock, { personId: props.item?.id })
19155
+ React22.createElement(PersonAttendanceBlock, { personId: props.item?.id })
18994
19156
  );
18995
19157
  PersonAttendanceBlockBound.displayName = "AgendaPersonAttendanceBlock";
18996
- const PersonServicesBound = (props) => React21.createElement(
19158
+ const PersonServicesBound = (props) => React22.createElement(
18997
19159
  AgendaContextProvider,
18998
19160
  { config, provider: provider2, store },
18999
- React21.createElement(PersonServicesWidget, { personId: props.item?.id })
19161
+ React22.createElement(PersonServicesWidget, { personId: props.item?.id })
19000
19162
  );
19001
19163
  PersonServicesBound.displayName = "AgendaPersonServices";
19002
19164
  return {
@@ -19152,10 +19314,10 @@ function createAgendaPlugin(options) {
19152
19314
  },
19153
19315
  {
19154
19316
  path: "/agenda/confirmations",
19155
- component: (() => React21.createElement(
19317
+ component: (() => React22.createElement(
19156
19318
  AgendaPage,
19157
19319
  { config, provider: provider2, store },
19158
- React21.createElement(ConfirmationsView)
19320
+ React22.createElement(ConfirmationsView)
19159
19321
  )),
19160
19322
  fullBleed: true,
19161
19323
  permission: { feature: "agenda", action: "read" },
@@ -19163,7 +19325,7 @@ function createAgendaPlugin(options) {
19163
19325
  },
19164
19326
  {
19165
19327
  path: "/checkin",
19166
- component: (() => React21.createElement(CheckinPage, {
19328
+ component: (() => React22.createElement(CheckinPage, {
19167
19329
  config,
19168
19330
  provider: provider2,
19169
19331
  store,
@@ -19179,6 +19341,12 @@ function createAgendaPlugin(options) {
19179
19341
  }
19180
19342
  ],
19181
19343
  widgets: [
19344
+ {
19345
+ id: "agenda-unit-turn-queue",
19346
+ zone: UNIT_DETAIL_ZONES.turnQueue,
19347
+ component: UnitTurnQueueWidget,
19348
+ order: 0
19349
+ },
19182
19350
  {
19183
19351
  // O HISTÓRICO DE ATENDIMENTO NA FICHA DA PESSOA.
19184
19352
  //
@@ -19315,9 +19483,9 @@ function createAgendaPlugin(options) {
19315
19483
  icon: "Calendar",
19316
19484
  // Standard layout: General | Integrations (Google Calendar addon) | Properties.
19317
19485
  component: (() => {
19318
- const AgendaSettingsTab = () => React21.createElement(PluginSettingsPanel, {
19486
+ const AgendaSettingsTab = () => React22.createElement(PluginSettingsPanel, {
19319
19487
  title: "Agenda",
19320
- generalSettings: React21.createElement(AgendaGeneralSettings),
19488
+ generalSettings: React22.createElement(AgendaGeneralSettings),
19321
19489
  registries,
19322
19490
  hostPluginId: "agenda",
19323
19491
  routeBase: "/settings/agenda"