@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/components/BookingPaymentPanel.d.ts.map +1 -1
- package/dist/components/SeriesDeleteChoice.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +324 -156
- package/dist/index.js.map +1 -1
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/views/UnitTurnQueueWidget.d.ts +14 -0
- package/dist/views/UnitTurnQueueWidget.d.ts.map +1 -0
- package/package.json +6 -6
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
|
|
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] =
|
|
765
|
-
const [title, setTitle] =
|
|
766
|
-
const [description, setDescription] =
|
|
767
|
-
const [mode, setMode] =
|
|
768
|
-
const [specificDate, setSpecificDate] =
|
|
769
|
-
const [allDay, setAllDay] =
|
|
770
|
-
const [startsAt, setStartsAt] =
|
|
771
|
-
const [endsAt, setEndsAt] =
|
|
772
|
-
const [days2, setDays] =
|
|
773
|
-
const [validFrom, setValidFrom] =
|
|
774
|
-
const [validTo, setValidTo] =
|
|
775
|
-
const [weekInterval, setWeekInterval] =
|
|
776
|
-
const [saving, setSaving] =
|
|
777
|
-
|
|
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] =
|
|
968
|
-
const [falhou, setFalhou] =
|
|
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
|
-
|
|
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] =
|
|
1028
|
-
const [selectedId, setSelectedId] =
|
|
1029
|
-
|
|
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 =
|
|
1034
|
-
|
|
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] =
|
|
1699
|
-
const [failed, setFailed] =
|
|
1700
|
-
|
|
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] =
|
|
1753
|
-
const [term, setTerm] =
|
|
1754
|
-
const [options, setOptions] =
|
|
1755
|
-
const initializedFor =
|
|
1756
|
-
const onChangeRef =
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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] =
|
|
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.
|
|
3487
|
-
const btn = "w-
|
|
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__ */
|
|
3503
|
-
|
|
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] =
|
|
4229
|
-
const [schedulesFor, setSchedulesFor] =
|
|
4230
|
-
const [editingBlock, setEditingBlock] =
|
|
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
|
-
|
|
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] =
|
|
5590
|
-
const [preenchendo, setPreenchendo] =
|
|
5591
|
-
const [preenchidos, setPreenchidos] =
|
|
5592
|
-
const [pendencias, setPendencias] =
|
|
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 =
|
|
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 =
|
|
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] =
|
|
5871
|
-
const [busyId, setBusyId] =
|
|
5872
|
-
const [myPersonId, setMyPersonId] =
|
|
5873
|
-
const [startRefusal, setStartRefusal] =
|
|
5874
|
-
const [checkouts, setCheckouts] =
|
|
5875
|
-
const [conferindo, setConferindo] =
|
|
5876
|
-
const [checkoutRefusal, setCheckoutRefusal] =
|
|
5877
|
-
const [panel, setPanel] =
|
|
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
|
-
|
|
5986
|
+
React22.useEffect(() => {
|
|
5887
5987
|
void fetchBookings(todayRange(), [...CHECKIN_STATUSES]);
|
|
5888
5988
|
}, [fetchBookings]);
|
|
5889
|
-
const [shownStatuses, setShownStatuses] =
|
|
5890
|
-
const [shownPros, setShownPros] =
|
|
5891
|
-
const prosToday =
|
|
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 =
|
|
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] =
|
|
5910
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
6054
|
+
React22.useEffect(() => {
|
|
5955
6055
|
void recarregarCheckouts();
|
|
5956
6056
|
}, [recarregarCheckouts]);
|
|
5957
|
-
|
|
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
|
-
|
|
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] =
|
|
6354
|
-
const [booking, setBooking] =
|
|
6355
|
-
const [indo, setIndo] =
|
|
6356
|
-
const [recusa, setRecusa] =
|
|
6357
|
-
|
|
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] =
|
|
8819
|
-
const [statusLocal, setStatusLocal] =
|
|
8820
|
-
|
|
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] =
|
|
8827
|
-
|
|
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] =
|
|
8839
|
-
const [recusaInicio, setRecusaInicio] =
|
|
8840
|
-
const [rows, setRows] =
|
|
8841
|
-
const [record, setRecord] =
|
|
8842
|
-
const [erro, setErro] =
|
|
8843
|
-
const [contratando, setContratando] =
|
|
8844
|
-
const [servicos, setServicos] =
|
|
8845
|
-
const [marcados, setMarcados] =
|
|
8846
|
-
const [salvando, setSalvando] =
|
|
8847
|
-
const [confirmando, setConfirmando] =
|
|
8848
|
-
const [encerrando, setEncerrando] =
|
|
8849
|
-
const [avisoFinal, setAvisoFinal] =
|
|
8850
|
-
const [preenchendo, setPreenchendo] =
|
|
8851
|
-
const [avaliando, setAvaliando] =
|
|
8852
|
-
const [lancando, setLancando] =
|
|
8853
|
-
const [executando, setExecutando] =
|
|
8854
|
-
const [vinculando, setVinculando] =
|
|
8855
|
-
const [candidatos, setCandidatos] =
|
|
8856
|
-
const [buscaInsumo, setBuscaInsumo] =
|
|
8857
|
-
const [insumos, setInsumos] =
|
|
8858
|
-
const [usados, setUsados] =
|
|
8859
|
-
const [busca, setBusca] =
|
|
8860
|
-
const [catalogo, setCatalogo] =
|
|
8861
|
-
const [indicados, setIndicados] =
|
|
8862
|
-
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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] =
|
|
9714
|
-
const [aberto, setAberto] =
|
|
9715
|
-
const [emCurso, setEmCurso] =
|
|
9716
|
-
const recarregar =
|
|
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
|
-
|
|
9824
|
+
React22.useEffect(() => {
|
|
9725
9825
|
void recarregar();
|
|
9726
9826
|
}, [recarregar]);
|
|
9727
|
-
|
|
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] =
|
|
9826
|
-
|
|
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] =
|
|
15274
|
-
const [busy, setBusy] =
|
|
15275
|
-
const refresh =
|
|
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
|
-
|
|
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] =
|
|
15445
|
-
const [snapshot, setSnapshot] =
|
|
15446
|
-
const [loading, setLoading] =
|
|
15447
|
-
const [error, setError] =
|
|
15448
|
-
const [, tick] =
|
|
15449
|
-
const load =
|
|
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
|
-
|
|
15626
|
+
React22.useEffect(() => {
|
|
15465
15627
|
void load();
|
|
15466
15628
|
}, [load]);
|
|
15467
|
-
|
|
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
|
-
|
|
15644
|
+
React22.useEffect(() => provider2.subscribe(() => {
|
|
15483
15645
|
void load();
|
|
15484
15646
|
}), [provider2, load]);
|
|
15485
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 = (() =>
|
|
15624
|
-
const HeaderControl = (() =>
|
|
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 = () =>
|
|
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
|
-
|
|
19145
|
+
React22.createElement(
|
|
18984
19146
|
AgendaContextProvider,
|
|
18985
19147
|
{ config, provider: provider2, store },
|
|
18986
|
-
|
|
19148
|
+
React22.createElement(PersonAttendancesWidget, { personId: props.item?.id })
|
|
18987
19149
|
)
|
|
18988
19150
|
);
|
|
18989
19151
|
PersonAttendancesBound.displayName = "AgendaPersonAttendances";
|
|
18990
|
-
const PersonAttendanceBlockBound = (props) =>
|
|
19152
|
+
const PersonAttendanceBlockBound = (props) => React22.createElement(
|
|
18991
19153
|
AgendaContextProvider,
|
|
18992
19154
|
{ config, provider: provider2, store },
|
|
18993
|
-
|
|
19155
|
+
React22.createElement(PersonAttendanceBlock, { personId: props.item?.id })
|
|
18994
19156
|
);
|
|
18995
19157
|
PersonAttendanceBlockBound.displayName = "AgendaPersonAttendanceBlock";
|
|
18996
|
-
const PersonServicesBound = (props) =>
|
|
19158
|
+
const PersonServicesBound = (props) => React22.createElement(
|
|
18997
19159
|
AgendaContextProvider,
|
|
18998
19160
|
{ config, provider: provider2, store },
|
|
18999
|
-
|
|
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: (() =>
|
|
19317
|
+
component: (() => React22.createElement(
|
|
19156
19318
|
AgendaPage,
|
|
19157
19319
|
{ config, provider: provider2, store },
|
|
19158
|
-
|
|
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: (() =>
|
|
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 = () =>
|
|
19486
|
+
const AgendaSettingsTab = () => React22.createElement(PluginSettingsPanel, {
|
|
19319
19487
|
title: "Agenda",
|
|
19320
|
-
generalSettings:
|
|
19488
|
+
generalSettings: React22.createElement(AgendaGeneralSettings),
|
|
19321
19489
|
registries,
|
|
19322
19490
|
hostPluginId: "agenda",
|
|
19323
19491
|
routeBase: "/settings/agenda"
|