@fayz-ai/plugin-agenda 0.21.0 → 0.21.1
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.d.ts.map +1 -1
- package/dist/index.js +254 -253
- package/dist/index.js.map +1 -1
- package/dist/lib/turn-queue-plugin.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { createMockAgendaProvider, createSupabasePublicBookingProvider } from './chunk-A77PUFY5.js';
|
|
2
2
|
export { createMockAgendaProvider } from './chunk-A77PUFY5.js';
|
|
3
|
-
import
|
|
3
|
+
import React21, { 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,
|
|
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';
|
|
5
|
+
import { createPluginContext, fayzConnectorOAuth, UNIT_DETAIL_ZONES, setScheduleBlockConfig, getScheduleBlockConfig, registerPersonActions, PluginSettingsPanel, adminNavigateTo, 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';
|
|
8
6
|
import FullCalendar from '@fullcalendar/react';
|
|
9
7
|
import dayGridPlugin from '@fullcalendar/daygrid';
|
|
10
8
|
import timeGridPlugin from '@fullcalendar/timegrid';
|
|
@@ -13,109 +11,13 @@ import interactionPlugin from '@fullcalendar/interaction';
|
|
|
13
11
|
import listPlugin from '@fullcalendar/list';
|
|
14
12
|
import ptBrLocale from '@fullcalendar/core/locales/pt-br';
|
|
15
13
|
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 { Card, CardHeader, CardTitle, CardDescription, CardContent, Skeleton, Switch, Input, Button, toast, 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';
|
|
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
|
-
|
|
119
21
|
// src/lib/message-events.ts
|
|
120
22
|
var BOOKING_FIELDS = [
|
|
121
23
|
{ name: "customer_name", label: "Cliente", type: "string", example: "Maria Silva" },
|
|
@@ -859,20 +761,20 @@ function BlockDialog({
|
|
|
859
761
|
}) {
|
|
860
762
|
const professionals = useAgendaStore((s) => s.agendaProfessionals);
|
|
861
763
|
const provider2 = useAgendaProvider();
|
|
862
|
-
const [assigneeId, setAssigneeId] =
|
|
863
|
-
const [title, setTitle] =
|
|
864
|
-
const [description, setDescription] =
|
|
865
|
-
const [mode, setMode] =
|
|
866
|
-
const [specificDate, setSpecificDate] =
|
|
867
|
-
const [allDay, setAllDay] =
|
|
868
|
-
const [startsAt, setStartsAt] =
|
|
869
|
-
const [endsAt, setEndsAt] =
|
|
870
|
-
const [days2, setDays] =
|
|
871
|
-
const [validFrom, setValidFrom] =
|
|
872
|
-
const [validTo, setValidTo] =
|
|
873
|
-
const [weekInterval, setWeekInterval] =
|
|
874
|
-
const [saving, setSaving] =
|
|
875
|
-
|
|
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(() => {
|
|
876
778
|
if (!open) return;
|
|
877
779
|
if (editing) {
|
|
878
780
|
setAssigneeId(editing.assigneeId);
|
|
@@ -1062,10 +964,10 @@ function BlockDialog({
|
|
|
1062
964
|
}
|
|
1063
965
|
function LostSlotsCard({ range, selectedStatuses, onPick }) {
|
|
1064
966
|
const provider2 = useAgendaProvider();
|
|
1065
|
-
const [counts, setCounts] =
|
|
1066
|
-
const [falhou, setFalhou] =
|
|
967
|
+
const [counts, setCounts] = React21.useState(null);
|
|
968
|
+
const [falhou, setFalhou] = React21.useState(false);
|
|
1067
969
|
const key = range ? `${range.start}|${range.end}` : null;
|
|
1068
|
-
|
|
970
|
+
React21.useEffect(() => {
|
|
1069
971
|
if (!range || !provider2.getLostCounts) {
|
|
1070
972
|
setCounts(null);
|
|
1071
973
|
return;
|
|
@@ -1122,14 +1024,14 @@ function ScheduleGridDialog({ open, onClose, professionalId }) {
|
|
|
1122
1024
|
const t2 = useTranslation();
|
|
1123
1025
|
const professionals = useAgendaStore((s) => s.professionals);
|
|
1124
1026
|
const invalidateSchedules2 = useAgendaStore((s) => s.invalidateSchedules);
|
|
1125
|
-
const [search, setSearch] =
|
|
1126
|
-
const [selectedId, setSelectedId] =
|
|
1127
|
-
|
|
1027
|
+
const [search, setSearch] = React21.useState("");
|
|
1028
|
+
const [selectedId, setSelectedId] = React21.useState(null);
|
|
1029
|
+
React21.useEffect(() => {
|
|
1128
1030
|
if (!open) return;
|
|
1129
1031
|
setSelectedId((current) => professionalId ?? current ?? professionals[0]?.id ?? null);
|
|
1130
1032
|
}, [open, professionalId, professionals]);
|
|
1131
|
-
const selectedRef =
|
|
1132
|
-
|
|
1033
|
+
const selectedRef = React21.useRef(null);
|
|
1034
|
+
React21.useEffect(() => {
|
|
1133
1035
|
if (!open || !selectedId) return;
|
|
1134
1036
|
const id = window.setTimeout(() => {
|
|
1135
1037
|
selectedRef.current?.scrollIntoView({ block: "nearest" });
|
|
@@ -1793,9 +1695,9 @@ function ClientHistoryPanel({ clientId, currentBookingId, formatCurrency: format
|
|
|
1793
1695
|
const t2 = useTranslation();
|
|
1794
1696
|
const config = useAgendaConfig();
|
|
1795
1697
|
const provider2 = useAgendaProvider();
|
|
1796
|
-
const [bookings, setBookings] =
|
|
1797
|
-
const [failed, setFailed] =
|
|
1798
|
-
|
|
1698
|
+
const [bookings, setBookings] = React21.useState(null);
|
|
1699
|
+
const [failed, setFailed] = React21.useState(false);
|
|
1700
|
+
React21.useEffect(() => {
|
|
1799
1701
|
let alive = true;
|
|
1800
1702
|
if (!provider2.getClientHistory) {
|
|
1801
1703
|
setBookings([]);
|
|
@@ -1847,13 +1749,13 @@ function ClientHistoryPanel({ clientId, currentBookingId, formatCurrency: format
|
|
|
1847
1749
|
}
|
|
1848
1750
|
function BookingInterestsPanel({ clientId, bookingId, value, onChange }) {
|
|
1849
1751
|
const provider2 = useAgendaProvider();
|
|
1850
|
-
const [crmInterests, setCrmInterests] =
|
|
1851
|
-
const [term, setTerm] =
|
|
1852
|
-
const [options, setOptions] =
|
|
1853
|
-
const initializedFor =
|
|
1854
|
-
const onChangeRef =
|
|
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);
|
|
1855
1757
|
onChangeRef.current = onChange;
|
|
1856
|
-
|
|
1758
|
+
React21.useEffect(() => {
|
|
1857
1759
|
if (!clientId) {
|
|
1858
1760
|
setCrmInterests([]);
|
|
1859
1761
|
initializedFor.current = null;
|
|
@@ -1886,7 +1788,7 @@ function BookingInterestsPanel({ clientId, bookingId, value, onChange }) {
|
|
|
1886
1788
|
vivo = false;
|
|
1887
1789
|
};
|
|
1888
1790
|
}, [clientId, bookingId, provider2]);
|
|
1889
|
-
|
|
1791
|
+
React21.useEffect(() => {
|
|
1890
1792
|
if (!term.trim()) {
|
|
1891
1793
|
setOptions([]);
|
|
1892
1794
|
return;
|
|
@@ -2241,13 +2143,13 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
|
|
|
2241
2143
|
const [markingWaiting, setMarkingWaiting] = useState(false);
|
|
2242
2144
|
const [preenchendoForm, setPreenchendoForm] = useState(null);
|
|
2243
2145
|
const [formsVersao, setFormsVersao] = useState(0);
|
|
2244
|
-
const professionalOptions =
|
|
2146
|
+
const professionalOptions = React21.useMemo(() => {
|
|
2245
2147
|
const doLocal = professionalsForLocation(professionals, locationId, professionalId);
|
|
2246
2148
|
if (!professionalId || doLocal.some((p) => p.id === professionalId)) return doLocal;
|
|
2247
2149
|
const nome = loadedBookings.find((b) => b.id === bookingId)?.professionalName;
|
|
2248
2150
|
return [...doLocal, { id: professionalId, name: nome ?? "\u2014", avatarUrl: null }];
|
|
2249
2151
|
}, [professionals, professionalId, locationId, loadedBookings, bookingId]);
|
|
2250
|
-
|
|
2152
|
+
React21.useEffect(() => {
|
|
2251
2153
|
const fora = shouldClearProfessional(professionals, locationId, professionalId);
|
|
2252
2154
|
if (!fora) return;
|
|
2253
2155
|
setProfessionalId("");
|
|
@@ -2553,7 +2455,7 @@ function AppointmentModal({ open, mode, bookingId, prefill, initialTab, external
|
|
|
2553
2455
|
setCheckingSeries(false);
|
|
2554
2456
|
};
|
|
2555
2457
|
}, [open, seriesDates, professionalId, startTime, totalDuration, bookingId, activeUnitId, provider2]);
|
|
2556
|
-
const [feriado, setFeriado] =
|
|
2458
|
+
const [feriado, setFeriado] = React21.useState(null);
|
|
2557
2459
|
useEffect(() => {
|
|
2558
2460
|
if (!open || !date || !provider2.getHolidays) {
|
|
2559
2461
|
setFeriado(null);
|
|
@@ -4325,9 +4227,9 @@ function CalendarView() {
|
|
|
4325
4227
|
const [serviceFilter, setServiceFilter] = useState([]);
|
|
4326
4228
|
const [hideEmptyGrids, setHideEmptyGrids] = useState(false);
|
|
4327
4229
|
const [schedulesOpen, setSchedulesOpen] = useState(false);
|
|
4328
|
-
const [blockFor, setBlockFor] =
|
|
4329
|
-
const [schedulesFor, setSchedulesFor] =
|
|
4330
|
-
const [editingBlock, setEditingBlock] =
|
|
4230
|
+
const [blockFor, setBlockFor] = React21.useState(null);
|
|
4231
|
+
const [schedulesFor, setSchedulesFor] = React21.useState(null);
|
|
4232
|
+
const [editingBlock, setEditingBlock] = React21.useState(null);
|
|
4331
4233
|
const isMobile = useIsMobile();
|
|
4332
4234
|
const resourceCapable = professionals.length > 0;
|
|
4333
4235
|
const viewOptions = resourceCapable ? RESOURCE_VIEW_OPTIONS : PLAIN_VIEW_OPTIONS;
|
|
@@ -5670,7 +5572,7 @@ function getAgendaTenantId() {
|
|
|
5670
5572
|
function AgendaPage({ config, provider: provider2, store, children }) {
|
|
5671
5573
|
const runtime = usePluginRuntimeOptional();
|
|
5672
5574
|
const tenantId = runtime?.context.tenant?.id;
|
|
5673
|
-
|
|
5575
|
+
React21.useEffect(() => {
|
|
5674
5576
|
setAgendaTenantId(tenantId);
|
|
5675
5577
|
return () => setAgendaTenantId(void 0);
|
|
5676
5578
|
}, [tenantId]);
|
|
@@ -5686,10 +5588,10 @@ function CheckinDialog({
|
|
|
5686
5588
|
onClose,
|
|
5687
5589
|
formatCurrency: formatCurrency2 = brl
|
|
5688
5590
|
}) {
|
|
5689
|
-
const [busy, setBusy] =
|
|
5690
|
-
const [preenchendo, setPreenchendo] =
|
|
5691
|
-
const [preenchidos, setPreenchidos] =
|
|
5692
|
-
const [pendencias, setPendencias] =
|
|
5591
|
+
const [busy, setBusy] = React21.useState(false);
|
|
5592
|
+
const [preenchendo, setPreenchendo] = React21.useState(null);
|
|
5593
|
+
const [preenchidos, setPreenchidos] = React21.useState(0);
|
|
5594
|
+
const [pendencias, setPendencias] = React21.useState([]);
|
|
5693
5595
|
const blocking = requirements.filter((r) => r.blocking);
|
|
5694
5596
|
const paid = booking.paymentStatus === "paid";
|
|
5695
5597
|
const hasBill = !!booking.paymentStatus && booking.paymentStatus !== "none";
|
|
@@ -5799,7 +5701,7 @@ function CheckinDialog({
|
|
|
5799
5701
|
}
|
|
5800
5702
|
function PaymentStatusBadge(props) {
|
|
5801
5703
|
const t2 = useTranslation();
|
|
5802
|
-
const labels =
|
|
5704
|
+
const labels = React21.useMemo(() => ({
|
|
5803
5705
|
none: t2("agenda.payment.title"),
|
|
5804
5706
|
pending: t2("agenda.payment.pending"),
|
|
5805
5707
|
partial: t2("agenda.payment.partial"),
|
|
@@ -5959,7 +5861,7 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
|
|
|
5959
5861
|
const config = useAgendaConfig();
|
|
5960
5862
|
const provider2 = useAgendaProvider();
|
|
5961
5863
|
const statusLabel = (v) => config.statuses.find((x) => x.value === v)?.label ?? v;
|
|
5962
|
-
const checkinStatuses =
|
|
5864
|
+
const checkinStatuses = React21.useMemo(
|
|
5963
5865
|
() => CHECKIN_STATUSES.map((v) => config.statuses.find((x) => x.value === v)).filter((x) => !!x),
|
|
5964
5866
|
[config.statuses]
|
|
5965
5867
|
);
|
|
@@ -5967,14 +5869,14 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
|
|
|
5967
5869
|
const loading = useAgendaStore((s) => s.bookingsLoading);
|
|
5968
5870
|
const fetchBookings = useAgendaStore((s) => s.fetchBookings);
|
|
5969
5871
|
const updateBookingStatus = useAgendaStore((s) => s.updateBookingStatus);
|
|
5970
|
-
const [term, setTerm] =
|
|
5971
|
-
const [busyId, setBusyId] =
|
|
5972
|
-
const [myPersonId, setMyPersonId] =
|
|
5973
|
-
const [startRefusal, setStartRefusal] =
|
|
5974
|
-
const [checkouts, setCheckouts] =
|
|
5975
|
-
const [conferindo, setConferindo] =
|
|
5976
|
-
const [checkoutRefusal, setCheckoutRefusal] =
|
|
5977
|
-
const [panel, setPanel] =
|
|
5872
|
+
const [term, setTerm] = React21.useState("");
|
|
5873
|
+
const [busyId, setBusyId] = React21.useState(null);
|
|
5874
|
+
const [myPersonId, setMyPersonId] = React21.useState(void 0);
|
|
5875
|
+
const [startRefusal, setStartRefusal] = React21.useState(null);
|
|
5876
|
+
const [checkouts, setCheckouts] = React21.useState([]);
|
|
5877
|
+
const [conferindo, setConferindo] = React21.useState(null);
|
|
5878
|
+
const [checkoutRefusal, setCheckoutRefusal] = React21.useState(null);
|
|
5879
|
+
const [panel, setPanel] = React21.useState(null);
|
|
5978
5880
|
const reviewing = panel?.kind === "review" ? panel.booking : null;
|
|
5979
5881
|
const editing = panel?.kind === "edit" ? panel.booking : null;
|
|
5980
5882
|
const charging = panel?.kind === "charge" ? panel.booking : null;
|
|
@@ -5983,17 +5885,17 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
|
|
|
5983
5885
|
const setCharging = (b) => setPanel(b ? { kind: "charge", booking: b } : null);
|
|
5984
5886
|
const can = usePermissionOptional();
|
|
5985
5887
|
const canReceive = can("financial", "create") || can("financial.receivables", "create");
|
|
5986
|
-
|
|
5888
|
+
React21.useEffect(() => {
|
|
5987
5889
|
void fetchBookings(todayRange(), [...CHECKIN_STATUSES]);
|
|
5988
5890
|
}, [fetchBookings]);
|
|
5989
|
-
const [shownStatuses, setShownStatuses] =
|
|
5990
|
-
const [shownPros, setShownPros] =
|
|
5991
|
-
const prosToday =
|
|
5891
|
+
const [shownStatuses, setShownStatuses] = React21.useState([]);
|
|
5892
|
+
const [shownPros, setShownPros] = React21.useState([]);
|
|
5893
|
+
const prosToday = React21.useMemo(() => {
|
|
5992
5894
|
const seen = /* @__PURE__ */ new Map();
|
|
5993
5895
|
for (const b of bookings) if (b.professionalName) seen.set(b.professionalName, b.professionalName);
|
|
5994
5896
|
return [...seen.keys()].sort();
|
|
5995
5897
|
}, [bookings]);
|
|
5996
|
-
const cards =
|
|
5898
|
+
const cards = React21.useMemo(() => {
|
|
5997
5899
|
const q = term.trim().toLowerCase();
|
|
5998
5900
|
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));
|
|
5999
5901
|
}, [bookings, term, shownStatuses, shownPros]);
|
|
@@ -6006,8 +5908,8 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
|
|
|
6006
5908
|
return out;
|
|
6007
5909
|
}
|
|
6008
5910
|
const money = (n) => n.toLocaleString("pt-BR", { style: "currency", currency: "BRL" });
|
|
6009
|
-
const [lost, setLost] =
|
|
6010
|
-
|
|
5911
|
+
const [lost, setLost] = React21.useState({ cancelled: 0, noShow: 0 });
|
|
5912
|
+
React21.useEffect(() => {
|
|
6011
5913
|
if (!provider2.getLostCounts) return;
|
|
6012
5914
|
let cancelado = false;
|
|
6013
5915
|
provider2.getLostCounts(todayRange()).then((c) => {
|
|
@@ -6018,7 +5920,7 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
|
|
|
6018
5920
|
cancelado = true;
|
|
6019
5921
|
};
|
|
6020
5922
|
}, [provider2]);
|
|
6021
|
-
const openAmount =
|
|
5923
|
+
const openAmount = React21.useMemo(
|
|
6022
5924
|
() => bookings.filter((b) => b.orderTotal && b.paymentStatus && b.paymentStatus !== "paid").reduce((soma, b) => soma + (b.orderTotal ?? 0), 0),
|
|
6023
5925
|
[bookings]
|
|
6024
5926
|
);
|
|
@@ -6030,7 +5932,7 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
|
|
|
6030
5932
|
amount: m.paidAmount
|
|
6031
5933
|
}));
|
|
6032
5934
|
}
|
|
6033
|
-
|
|
5935
|
+
React21.useEffect(() => {
|
|
6034
5936
|
let vivo = true;
|
|
6035
5937
|
(async () => {
|
|
6036
5938
|
try {
|
|
@@ -6044,17 +5946,17 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
|
|
|
6044
5946
|
vivo = false;
|
|
6045
5947
|
};
|
|
6046
5948
|
}, []);
|
|
6047
|
-
const recarregarCheckouts =
|
|
5949
|
+
const recarregarCheckouts = React21.useCallback(async () => {
|
|
6048
5950
|
try {
|
|
6049
5951
|
setCheckouts(await provider2.getPendingCheckouts?.() ?? []);
|
|
6050
5952
|
} catch {
|
|
6051
5953
|
setCheckouts([]);
|
|
6052
5954
|
}
|
|
6053
5955
|
}, []);
|
|
6054
|
-
|
|
5956
|
+
React21.useEffect(() => {
|
|
6055
5957
|
void recarregarCheckouts();
|
|
6056
5958
|
}, [recarregarCheckouts]);
|
|
6057
|
-
|
|
5959
|
+
React21.useEffect(() => {
|
|
6058
5960
|
const parar = provider2.subscribeToBookingChanges?.(() => {
|
|
6059
5961
|
void recarregarCheckouts();
|
|
6060
5962
|
void fetchBookings(todayRange(), [...CHECKIN_STATUSES]);
|
|
@@ -6437,7 +6339,7 @@ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
|
|
|
6437
6339
|
function CheckinPage({ config, provider: provider2, store, onReceive, onOpenBooking }) {
|
|
6438
6340
|
const runtime = usePluginRuntimeOptional();
|
|
6439
6341
|
const tenantId = runtime?.context.tenant?.id;
|
|
6440
|
-
|
|
6342
|
+
React21.useEffect(() => {
|
|
6441
6343
|
setAgendaTenantId(tenantId);
|
|
6442
6344
|
return () => setAgendaTenantId(void 0);
|
|
6443
6345
|
}, [tenantId]);
|
|
@@ -6450,11 +6352,11 @@ function hoje2() {
|
|
|
6450
6352
|
}
|
|
6451
6353
|
function PersonAttendanceBlock({ personId }) {
|
|
6452
6354
|
const provider2 = useAgendaProvider();
|
|
6453
|
-
const [pessoa, setPessoa] =
|
|
6454
|
-
const [booking, setBooking] =
|
|
6455
|
-
const [indo, setIndo] =
|
|
6456
|
-
const [recusa, setRecusa] =
|
|
6457
|
-
|
|
6355
|
+
const [pessoa, setPessoa] = React21.useState(void 0);
|
|
6356
|
+
const [booking, setBooking] = React21.useState(null);
|
|
6357
|
+
const [indo, setIndo] = React21.useState(false);
|
|
6358
|
+
const [recusa, setRecusa] = React21.useState(null);
|
|
6359
|
+
React21.useEffect(() => {
|
|
6458
6360
|
let vivo = true;
|
|
6459
6361
|
(async () => {
|
|
6460
6362
|
const eu = await provider2.getCurrentPersonId?.() ?? null;
|
|
@@ -8915,16 +8817,16 @@ function TarefaPane({ titulo, aoVoltar, children }) {
|
|
|
8915
8817
|
}
|
|
8916
8818
|
function AttendanceRuler({ booking, open, onClose, onFinished, frame = "modal" }) {
|
|
8917
8819
|
const provider2 = useAgendaProvider();
|
|
8918
|
-
const [versao, setVersao] =
|
|
8919
|
-
const [statusLocal, setStatusLocal] =
|
|
8920
|
-
|
|
8820
|
+
const [versao, setVersao] = React21.useState(0);
|
|
8821
|
+
const [statusLocal, setStatusLocal] = React21.useState(booking.status);
|
|
8822
|
+
React21.useEffect(() => {
|
|
8921
8823
|
setStatusLocal(booking.status);
|
|
8922
8824
|
}, [booking.id, booking.status]);
|
|
8923
8825
|
const emCurso = statusLocal === "in_progress";
|
|
8924
8826
|
const naoIniciado = statusLocal === "scheduled" || statusLocal === "confirmed" || statusLocal === "waiting";
|
|
8925
8827
|
const somenteLeitura = !emCurso;
|
|
8926
|
-
const [eu, setEu] =
|
|
8927
|
-
|
|
8828
|
+
const [eu, setEu] = React21.useState(void 0);
|
|
8829
|
+
React21.useEffect(() => {
|
|
8928
8830
|
if (!naoIniciado) return;
|
|
8929
8831
|
let vivo = true;
|
|
8930
8832
|
(async () => {
|
|
@@ -8935,31 +8837,31 @@ function AttendanceRuler({ booking, open, onClose, onFinished, frame = "modal" }
|
|
|
8935
8837
|
vivo = false;
|
|
8936
8838
|
};
|
|
8937
8839
|
}, [naoIniciado, provider2]);
|
|
8938
|
-
const [iniciando, setIniciando] =
|
|
8939
|
-
const [recusaInicio, setRecusaInicio] =
|
|
8940
|
-
const [rows, setRows] =
|
|
8941
|
-
const [record, setRecord] =
|
|
8942
|
-
const [erro, setErro] =
|
|
8943
|
-
const [contratando, setContratando] =
|
|
8944
|
-
const [servicos, setServicos] =
|
|
8945
|
-
const [marcados, setMarcados] =
|
|
8946
|
-
const [salvando, setSalvando] =
|
|
8947
|
-
const [confirmando, setConfirmando] =
|
|
8948
|
-
const [encerrando, setEncerrando] =
|
|
8949
|
-
const [avisoFinal, setAvisoFinal] =
|
|
8950
|
-
const [preenchendo, setPreenchendo] =
|
|
8951
|
-
const [avaliando, setAvaliando] =
|
|
8952
|
-
const [lancando, setLancando] =
|
|
8953
|
-
const [executando, setExecutando] =
|
|
8954
|
-
const [vinculando, setVinculando] =
|
|
8955
|
-
const [candidatos, setCandidatos] =
|
|
8956
|
-
const [buscaInsumo, setBuscaInsumo] =
|
|
8957
|
-
const [insumos, setInsumos] =
|
|
8958
|
-
const [usados, setUsados] =
|
|
8959
|
-
const [busca, setBusca] =
|
|
8960
|
-
const [catalogo, setCatalogo] =
|
|
8961
|
-
const [indicados, setIndicados] =
|
|
8962
|
-
|
|
8840
|
+
const [iniciando, setIniciando] = React21.useState(false);
|
|
8841
|
+
const [recusaInicio, setRecusaInicio] = React21.useState(null);
|
|
8842
|
+
const [rows, setRows] = React21.useState(null);
|
|
8843
|
+
const [record, setRecord] = React21.useState(null);
|
|
8844
|
+
const [erro, setErro] = React21.useState(null);
|
|
8845
|
+
const [contratando, setContratando] = React21.useState(false);
|
|
8846
|
+
const [servicos, setServicos] = React21.useState([]);
|
|
8847
|
+
const [marcados, setMarcados] = React21.useState({});
|
|
8848
|
+
const [salvando, setSalvando] = React21.useState(false);
|
|
8849
|
+
const [confirmando, setConfirmando] = React21.useState(null);
|
|
8850
|
+
const [encerrando, setEncerrando] = React21.useState(false);
|
|
8851
|
+
const [avisoFinal, setAvisoFinal] = React21.useState(null);
|
|
8852
|
+
const [preenchendo, setPreenchendo] = React21.useState(null);
|
|
8853
|
+
const [avaliando, setAvaliando] = React21.useState(false);
|
|
8854
|
+
const [lancando, setLancando] = React21.useState(null);
|
|
8855
|
+
const [executando, setExecutando] = React21.useState(null);
|
|
8856
|
+
const [vinculando, setVinculando] = React21.useState(null);
|
|
8857
|
+
const [candidatos, setCandidatos] = React21.useState(null);
|
|
8858
|
+
const [buscaInsumo, setBuscaInsumo] = React21.useState("");
|
|
8859
|
+
const [insumos, setInsumos] = React21.useState([]);
|
|
8860
|
+
const [usados, setUsados] = React21.useState([]);
|
|
8861
|
+
const [busca, setBusca] = React21.useState("");
|
|
8862
|
+
const [catalogo, setCatalogo] = React21.useState([]);
|
|
8863
|
+
const [indicados, setIndicados] = React21.useState([]);
|
|
8864
|
+
React21.useEffect(() => {
|
|
8963
8865
|
if (!open) return;
|
|
8964
8866
|
let vivo = true;
|
|
8965
8867
|
(async () => {
|
|
@@ -9016,7 +8918,7 @@ function AttendanceRuler({ booking, open, onClose, onFinished, frame = "modal" }
|
|
|
9016
8918
|
const d = ORDEM.indexOf(a.kind) - ORDEM.indexOf(b.kind);
|
|
9017
8919
|
return d !== 0 ? d : a.id.localeCompare(b.id);
|
|
9018
8920
|
};
|
|
9019
|
-
const doAtendimento =
|
|
8921
|
+
const doAtendimento = React21.useMemo(() => {
|
|
9020
8922
|
const m = /* @__PURE__ */ new Map();
|
|
9021
8923
|
for (const r of (rows ?? []).filter((x) => x.scope === "attendance")) {
|
|
9022
8924
|
const k = `${r.kind}:${r.refId ?? ""}`;
|
|
@@ -9029,7 +8931,7 @@ function AttendanceRuler({ booking, open, onClose, onFinished, frame = "modal" }
|
|
|
9029
8931
|
}
|
|
9030
8932
|
return [...m.values()].sort(porOrdem);
|
|
9031
8933
|
}, [rows]);
|
|
9032
|
-
const porServico =
|
|
8934
|
+
const porServico = React21.useMemo(() => {
|
|
9033
8935
|
const m = /* @__PURE__ */ new Map();
|
|
9034
8936
|
for (const r of rows ?? []) {
|
|
9035
8937
|
if (r.scope === "attendance") continue;
|
|
@@ -9054,7 +8956,7 @@ function AttendanceRuler({ booking, open, onClose, onFinished, frame = "modal" }
|
|
|
9054
8956
|
}, [rows]);
|
|
9055
8957
|
const faltaParaTerminar = (rows ?? []).filter((r) => r.blocksFinish && r.status === "pending");
|
|
9056
8958
|
const tarefaAberta = Boolean(vinculando || executando || lancando || avaliando || confirmando || contratando || preenchendo?.refId && booking.clientId);
|
|
9057
|
-
|
|
8959
|
+
React21.useEffect(() => {
|
|
9058
8960
|
if (!avaliando) return;
|
|
9059
8961
|
let vivo = true;
|
|
9060
8962
|
const t2 = setTimeout(async () => {
|
|
@@ -9070,7 +8972,7 @@ function AttendanceRuler({ booking, open, onClose, onFinished, frame = "modal" }
|
|
|
9070
8972
|
clearTimeout(t2);
|
|
9071
8973
|
};
|
|
9072
8974
|
}, [avaliando, busca]);
|
|
9073
|
-
|
|
8975
|
+
React21.useEffect(() => {
|
|
9074
8976
|
if (!lancando) return;
|
|
9075
8977
|
let vivo = true;
|
|
9076
8978
|
const t2 = setTimeout(async () => {
|
|
@@ -9094,7 +8996,7 @@ function AttendanceRuler({ booking, open, onClose, onFinished, frame = "modal" }
|
|
|
9094
8996
|
return [...xs, ...kit.filter((k) => !tem.has(k.productId)).map((k) => ({ productId: k.productId, name: k.name, qtd: k.quantity, unitId: k.unitId }))];
|
|
9095
8997
|
});
|
|
9096
8998
|
}
|
|
9097
|
-
|
|
8999
|
+
React21.useEffect(() => {
|
|
9098
9000
|
if (!vinculando) return;
|
|
9099
9001
|
let vivo = true;
|
|
9100
9002
|
(async () => {
|
|
@@ -9810,10 +9712,10 @@ var STATUS_LABEL = {
|
|
|
9810
9712
|
};
|
|
9811
9713
|
function PersonAttendancesWidget({ personId }) {
|
|
9812
9714
|
const provider2 = useAgendaProvider();
|
|
9813
|
-
const [linhas, setLinhas] =
|
|
9814
|
-
const [aberto, setAberto] =
|
|
9815
|
-
const [emCurso, setEmCurso] =
|
|
9816
|
-
const recarregar =
|
|
9715
|
+
const [linhas, setLinhas] = React21.useState(null);
|
|
9716
|
+
const [aberto, setAberto] = React21.useState(null);
|
|
9717
|
+
const [emCurso, setEmCurso] = React21.useState(null);
|
|
9718
|
+
const recarregar = React21.useCallback(async () => {
|
|
9817
9719
|
try {
|
|
9818
9720
|
const r = await provider2.getPersonAttendances?.(personId);
|
|
9819
9721
|
setLinhas(r ?? []);
|
|
@@ -9821,10 +9723,10 @@ function PersonAttendancesWidget({ personId }) {
|
|
|
9821
9723
|
setLinhas([]);
|
|
9822
9724
|
}
|
|
9823
9725
|
}, [personId, provider2]);
|
|
9824
|
-
|
|
9726
|
+
React21.useEffect(() => {
|
|
9825
9727
|
void recarregar();
|
|
9826
9728
|
}, [recarregar]);
|
|
9827
|
-
|
|
9729
|
+
React21.useEffect(() => {
|
|
9828
9730
|
let vivo = true;
|
|
9829
9731
|
(async () => {
|
|
9830
9732
|
const eu = await provider2.getCurrentPersonId?.() ?? null;
|
|
@@ -9922,8 +9824,8 @@ function PersonAttendancesWidget({ personId }) {
|
|
|
9922
9824
|
}
|
|
9923
9825
|
function PersonServicesWidget({ personId }) {
|
|
9924
9826
|
const provider2 = useAgendaProvider();
|
|
9925
|
-
const [rows, setRows] =
|
|
9926
|
-
|
|
9827
|
+
const [rows, setRows] = React21.useState(null);
|
|
9828
|
+
React21.useEffect(() => {
|
|
9927
9829
|
let alive = true;
|
|
9928
9830
|
(async () => {
|
|
9929
9831
|
try {
|
|
@@ -15432,9 +15334,9 @@ function failureMessage(error) {
|
|
|
15432
15334
|
}
|
|
15433
15335
|
function AvailabilityHeaderControl({ provider: provider2 }) {
|
|
15434
15336
|
const unitId = useActiveUnitId();
|
|
15435
|
-
const [status, setStatus] =
|
|
15436
|
-
const [busy, setBusy] =
|
|
15437
|
-
const refresh =
|
|
15337
|
+
const [status, setStatus] = React21.useState(null);
|
|
15338
|
+
const [busy, setBusy] = React21.useState(false);
|
|
15339
|
+
const refresh = React21.useCallback(async () => {
|
|
15438
15340
|
if (!unitId) {
|
|
15439
15341
|
setStatus(null);
|
|
15440
15342
|
return;
|
|
@@ -15449,7 +15351,7 @@ function AvailabilityHeaderControl({ provider: provider2 }) {
|
|
|
15449
15351
|
setStatus(null);
|
|
15450
15352
|
}
|
|
15451
15353
|
}, [provider2, unitId]);
|
|
15452
|
-
|
|
15354
|
+
React21.useEffect(() => {
|
|
15453
15355
|
void refresh();
|
|
15454
15356
|
return provider2.subscribe(() => {
|
|
15455
15357
|
void refresh();
|
|
@@ -15603,12 +15505,12 @@ function EmptyUnit() {
|
|
|
15603
15505
|
}
|
|
15604
15506
|
function TurnQueuePage({ provider: provider2, agendaProvider, title, subtitle }) {
|
|
15605
15507
|
const unitId = useActiveUnitId();
|
|
15606
|
-
const [agendaProfessionals, setAgendaProfessionals] =
|
|
15607
|
-
const [snapshot, setSnapshot] =
|
|
15608
|
-
const [loading, setLoading] =
|
|
15609
|
-
const [error, setError] =
|
|
15610
|
-
const [, tick] =
|
|
15611
|
-
const load =
|
|
15508
|
+
const [agendaProfessionals, setAgendaProfessionals] = React21.useState([]);
|
|
15509
|
+
const [snapshot, setSnapshot] = React21.useState(null);
|
|
15510
|
+
const [loading, setLoading] = React21.useState(false);
|
|
15511
|
+
const [error, setError] = React21.useState(null);
|
|
15512
|
+
const [, tick] = React21.useState(0);
|
|
15513
|
+
const load = React21.useCallback(async () => {
|
|
15612
15514
|
if (!unitId) {
|
|
15613
15515
|
setSnapshot(null);
|
|
15614
15516
|
return;
|
|
@@ -15623,10 +15525,10 @@ function TurnQueuePage({ provider: provider2, agendaProvider, title, subtitle })
|
|
|
15623
15525
|
setLoading(false);
|
|
15624
15526
|
}
|
|
15625
15527
|
}, [provider2, unitId]);
|
|
15626
|
-
|
|
15528
|
+
React21.useEffect(() => {
|
|
15627
15529
|
void load();
|
|
15628
15530
|
}, [load]);
|
|
15629
|
-
|
|
15531
|
+
React21.useEffect(() => {
|
|
15630
15532
|
if (!unitId) {
|
|
15631
15533
|
setAgendaProfessionals([]);
|
|
15632
15534
|
return;
|
|
@@ -15641,19 +15543,19 @@ function TurnQueuePage({ provider: provider2, agendaProvider, title, subtitle })
|
|
|
15641
15543
|
active = false;
|
|
15642
15544
|
};
|
|
15643
15545
|
}, [agendaProvider, unitId]);
|
|
15644
|
-
|
|
15546
|
+
React21.useEffect(() => provider2.subscribe(() => {
|
|
15645
15547
|
void load();
|
|
15646
15548
|
}), [provider2, load]);
|
|
15647
|
-
|
|
15549
|
+
React21.useEffect(() => {
|
|
15648
15550
|
const timer = window.setInterval(() => tick((value) => value + 1), 6e4);
|
|
15649
15551
|
return () => window.clearInterval(timer);
|
|
15650
15552
|
}, []);
|
|
15651
|
-
const rows =
|
|
15553
|
+
const rows = React21.useMemo(() => [...snapshot?.entries ?? []].sort((a, b) => {
|
|
15652
15554
|
const rank = { available: 0, serving: 1, unavailable: 2 };
|
|
15653
15555
|
return rank[a.status] - rank[b.status] || a.position - b.position;
|
|
15654
15556
|
}), [snapshot]);
|
|
15655
15557
|
const queueRows = rows.filter((row) => row.status !== "unavailable");
|
|
15656
|
-
const professionals =
|
|
15558
|
+
const professionals = React21.useMemo(() => {
|
|
15657
15559
|
const byId = /* @__PURE__ */ new Map();
|
|
15658
15560
|
for (const professional of agendaProfessionals) {
|
|
15659
15561
|
byId.set(professional.id, {
|
|
@@ -15676,7 +15578,7 @@ function TurnQueuePage({ provider: provider2, agendaProvider, title, subtitle })
|
|
|
15676
15578
|
}, [agendaProfessionals, snapshot]);
|
|
15677
15579
|
const available = professionals.filter((row) => row.status === "available").length;
|
|
15678
15580
|
const serving = professionals.filter((row) => row.status === "serving").length;
|
|
15679
|
-
const addWalkIn =
|
|
15581
|
+
const addWalkIn = React21.useCallback((professionalId) => {
|
|
15680
15582
|
if (!unitId) return;
|
|
15681
15583
|
window.location.hash = "/agenda";
|
|
15682
15584
|
window.setTimeout(() => {
|
|
@@ -15768,6 +15670,103 @@ function TurnQueuePage({ provider: provider2, agendaProvider, title, subtitle })
|
|
|
15768
15670
|
] })
|
|
15769
15671
|
] }) });
|
|
15770
15672
|
}
|
|
15673
|
+
function UnitTurnQueueWidget({ item }) {
|
|
15674
|
+
const unitId = item?.id;
|
|
15675
|
+
const [row, setRow] = React21.useState(null);
|
|
15676
|
+
const [loading, setLoading] = React21.useState(true);
|
|
15677
|
+
const [saving, setSaving] = React21.useState(false);
|
|
15678
|
+
const [radius, setRadius] = React21.useState("80");
|
|
15679
|
+
React21.useEffect(() => {
|
|
15680
|
+
if (!unitId) return;
|
|
15681
|
+
let alive = true;
|
|
15682
|
+
const supabase = getSupabaseClientOptional();
|
|
15683
|
+
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 }) => {
|
|
15684
|
+
if (!alive) return;
|
|
15685
|
+
const next = data ?? { is_active: false, geofence_required: true, geofence_radius_meters: 80 };
|
|
15686
|
+
setRow(next);
|
|
15687
|
+
setRadius(String(next.geofence_radius_meters));
|
|
15688
|
+
setLoading(false);
|
|
15689
|
+
});
|
|
15690
|
+
return () => {
|
|
15691
|
+
alive = false;
|
|
15692
|
+
};
|
|
15693
|
+
}, [unitId]);
|
|
15694
|
+
const save = async (patch) => {
|
|
15695
|
+
if (!unitId || !row) return;
|
|
15696
|
+
const next = { ...row, ...patch };
|
|
15697
|
+
setSaving(true);
|
|
15698
|
+
const supabase = getSupabaseClientOptional();
|
|
15699
|
+
const { data, error } = await supabase.rpc("agenda_turn_queue_configure", {
|
|
15700
|
+
p_unit_id: unitId,
|
|
15701
|
+
p_enabled: next.is_active,
|
|
15702
|
+
p_geofence_required: next.geofence_required,
|
|
15703
|
+
p_radius_meters: next.geofence_radius_meters
|
|
15704
|
+
});
|
|
15705
|
+
setSaving(false);
|
|
15706
|
+
if (error) {
|
|
15707
|
+
toast.error("N\xE3o foi poss\xEDvel salvar a lista da vez", { description: error.message });
|
|
15708
|
+
return;
|
|
15709
|
+
}
|
|
15710
|
+
setRow({ is_active: data.is_active, geofence_required: data.geofence_required, geofence_radius_meters: data.geofence_radius_meters });
|
|
15711
|
+
toast.success("Lista da vez salva");
|
|
15712
|
+
};
|
|
15713
|
+
const radiusNumber = Number(radius);
|
|
15714
|
+
const radiusInvalid = !Number.isInteger(radiusNumber) || radiusNumber < 10 || radiusNumber > 1e3;
|
|
15715
|
+
const hasCoordinate = item?.latitude != null && item?.longitude != null;
|
|
15716
|
+
return /* @__PURE__ */ jsxs(Card, { children: [
|
|
15717
|
+
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
15718
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: "Lista da vez" }),
|
|
15719
|
+
/* @__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." })
|
|
15720
|
+
] }),
|
|
15721
|
+
/* @__PURE__ */ jsx(CardContent, { className: "space-y-4", children: loading || !row ? /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
15722
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" }),
|
|
15723
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-10 w-full" })
|
|
15724
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
15725
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-4 rounded-md border border-input bg-background px-3 py-2.5", children: [
|
|
15726
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Ativar lista da vez nesta unidade" }),
|
|
15727
|
+
/* @__PURE__ */ jsx(Switch, { checked: row.is_active, disabled: saving, onCheckedChange: (v) => void save({ is_active: v }) })
|
|
15728
|
+
] }),
|
|
15729
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
15730
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
15731
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Localiza\xE7\xE3o da lista da vez" }),
|
|
15732
|
+
/* @__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." })
|
|
15733
|
+
] }),
|
|
15734
|
+
/* @__PURE__ */ jsxs("label", { className: "flex items-center justify-between gap-4 rounded-md border border-input bg-background px-3 py-2.5", children: [
|
|
15735
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm", children: "Exigir que o profissional esteja na unidade" }),
|
|
15736
|
+
/* @__PURE__ */ jsx(Switch, { checked: row.geofence_required, disabled: saving, onCheckedChange: (v) => void save({ geofence_required: v }) })
|
|
15737
|
+
] }),
|
|
15738
|
+
/* @__PURE__ */ jsx("div", { className: "fz-container", children: /* @__PURE__ */ jsxs("div", { className: "fz-fields fz-fields-2 gap-4", children: [
|
|
15739
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
15740
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Coordenada" }),
|
|
15741
|
+
/* @__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" }),
|
|
15742
|
+
/* @__PURE__ */ jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
15743
|
+
"Vem do endere\xE7o da unidade.",
|
|
15744
|
+
" ",
|
|
15745
|
+
/* @__PURE__ */ jsx("button", { type: "button", className: "underline decoration-dashed underline-offset-2", onClick: () => adminNavigateTo(`/settings/locations/${item?.id}/edit`), children: "Editar endere\xE7o" })
|
|
15746
|
+
] })
|
|
15747
|
+
] }),
|
|
15748
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
|
|
15749
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "Raio (m)" }),
|
|
15750
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
15751
|
+
/* @__PURE__ */ jsx(Input, { type: "number", min: 10, max: 1e3, value: radius, onChange: (e) => setRadius(e.target.value), className: "tabular-nums" }),
|
|
15752
|
+
/* @__PURE__ */ jsx(
|
|
15753
|
+
Button,
|
|
15754
|
+
{
|
|
15755
|
+
variant: "outline",
|
|
15756
|
+
disabled: saving || radiusInvalid || radiusNumber === row.geofence_radius_meters,
|
|
15757
|
+
onClick: () => void save({ geofence_radius_meters: radiusNumber }),
|
|
15758
|
+
children: "Salvar"
|
|
15759
|
+
}
|
|
15760
|
+
)
|
|
15761
|
+
] }),
|
|
15762
|
+
radiusInvalid && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive", children: "Use um raio entre 10 e 1000 metros." })
|
|
15763
|
+
] })
|
|
15764
|
+
] }) }),
|
|
15765
|
+
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." })
|
|
15766
|
+
] })
|
|
15767
|
+
] }) })
|
|
15768
|
+
] });
|
|
15769
|
+
}
|
|
15771
15770
|
|
|
15772
15771
|
// src/lib/turn-queue-plugin.ts
|
|
15773
15772
|
function createAgendaTurnQueuePlugin(options = {}) {
|
|
@@ -15782,8 +15781,8 @@ function createAgendaTurnQueuePlugin(options = {}) {
|
|
|
15782
15781
|
const menu = options.labels?.menu ?? "Pr\xF3ximo da vez";
|
|
15783
15782
|
const title = options.labels?.title ?? menu;
|
|
15784
15783
|
const subtitle = options.labels?.subtitle ?? "Ordem ao vivo dos profissionais dispon\xEDveis nesta unidade";
|
|
15785
|
-
const Page = (() =>
|
|
15786
|
-
const HeaderControl = (() =>
|
|
15784
|
+
const Page = (() => React21.createElement(TurnQueuePage, { provider: provider2, agendaProvider, title, subtitle }));
|
|
15785
|
+
const HeaderControl = (() => React21.createElement(AvailabilityHeaderControl, { provider: provider2 }));
|
|
15787
15786
|
return {
|
|
15788
15787
|
id: "agenda-turn-queue",
|
|
15789
15788
|
name: menu,
|
|
@@ -15816,6 +15815,14 @@ function createAgendaTurnQueuePlugin(options = {}) {
|
|
|
15816
15815
|
permission: { feature: "agenda", action: "read" },
|
|
15817
15816
|
title
|
|
15818
15817
|
}],
|
|
15818
|
+
// Lives on the add-on, not on agenda: the unit tab exists only while this
|
|
15819
|
+
// module is enabled in settings/modules.
|
|
15820
|
+
widgets: [{
|
|
15821
|
+
id: "agenda-unit-turn-queue",
|
|
15822
|
+
zone: UNIT_DETAIL_ZONES.turnQueue,
|
|
15823
|
+
component: UnitTurnQueueWidget,
|
|
15824
|
+
order: 0
|
|
15825
|
+
}],
|
|
15819
15826
|
headerControls: [{
|
|
15820
15827
|
id: "agenda-turn-queue.availability",
|
|
15821
15828
|
component: HeaderControl,
|
|
@@ -19137,28 +19144,28 @@ function createAgendaPlugin(options) {
|
|
|
19137
19144
|
}
|
|
19138
19145
|
}
|
|
19139
19146
|
]);
|
|
19140
|
-
const PageComponent = () =>
|
|
19147
|
+
const PageComponent = () => React21.createElement(AgendaPage, { config, provider: provider2, store });
|
|
19141
19148
|
const PersonAttendancesBound = (props) => (
|
|
19142
19149
|
// O provider do PLUGIN, sem a moldura da página: dentro de uma aba a
|
|
19143
19150
|
// `AgendaPage` traria a altura cheia e a rolagem própria do calendário, e a
|
|
19144
19151
|
// lista passaria a rolar dentro de uma caixa que já rola.
|
|
19145
|
-
|
|
19152
|
+
React21.createElement(
|
|
19146
19153
|
AgendaContextProvider,
|
|
19147
19154
|
{ config, provider: provider2, store },
|
|
19148
|
-
|
|
19155
|
+
React21.createElement(PersonAttendancesWidget, { personId: props.item?.id })
|
|
19149
19156
|
)
|
|
19150
19157
|
);
|
|
19151
19158
|
PersonAttendancesBound.displayName = "AgendaPersonAttendances";
|
|
19152
|
-
const PersonAttendanceBlockBound = (props) =>
|
|
19159
|
+
const PersonAttendanceBlockBound = (props) => React21.createElement(
|
|
19153
19160
|
AgendaContextProvider,
|
|
19154
19161
|
{ config, provider: provider2, store },
|
|
19155
|
-
|
|
19162
|
+
React21.createElement(PersonAttendanceBlock, { personId: props.item?.id })
|
|
19156
19163
|
);
|
|
19157
19164
|
PersonAttendanceBlockBound.displayName = "AgendaPersonAttendanceBlock";
|
|
19158
|
-
const PersonServicesBound = (props) =>
|
|
19165
|
+
const PersonServicesBound = (props) => React21.createElement(
|
|
19159
19166
|
AgendaContextProvider,
|
|
19160
19167
|
{ config, provider: provider2, store },
|
|
19161
|
-
|
|
19168
|
+
React21.createElement(PersonServicesWidget, { personId: props.item?.id })
|
|
19162
19169
|
);
|
|
19163
19170
|
PersonServicesBound.displayName = "AgendaPersonServices";
|
|
19164
19171
|
return {
|
|
@@ -19314,10 +19321,10 @@ function createAgendaPlugin(options) {
|
|
|
19314
19321
|
},
|
|
19315
19322
|
{
|
|
19316
19323
|
path: "/agenda/confirmations",
|
|
19317
|
-
component: (() =>
|
|
19324
|
+
component: (() => React21.createElement(
|
|
19318
19325
|
AgendaPage,
|
|
19319
19326
|
{ config, provider: provider2, store },
|
|
19320
|
-
|
|
19327
|
+
React21.createElement(ConfirmationsView)
|
|
19321
19328
|
)),
|
|
19322
19329
|
fullBleed: true,
|
|
19323
19330
|
permission: { feature: "agenda", action: "read" },
|
|
@@ -19325,7 +19332,7 @@ function createAgendaPlugin(options) {
|
|
|
19325
19332
|
},
|
|
19326
19333
|
{
|
|
19327
19334
|
path: "/checkin",
|
|
19328
|
-
component: (() =>
|
|
19335
|
+
component: (() => React21.createElement(CheckinPage, {
|
|
19329
19336
|
config,
|
|
19330
19337
|
provider: provider2,
|
|
19331
19338
|
store,
|
|
@@ -19341,12 +19348,6 @@ function createAgendaPlugin(options) {
|
|
|
19341
19348
|
}
|
|
19342
19349
|
],
|
|
19343
19350
|
widgets: [
|
|
19344
|
-
{
|
|
19345
|
-
id: "agenda-unit-turn-queue",
|
|
19346
|
-
zone: UNIT_DETAIL_ZONES.turnQueue,
|
|
19347
|
-
component: UnitTurnQueueWidget,
|
|
19348
|
-
order: 0
|
|
19349
|
-
},
|
|
19350
19351
|
{
|
|
19351
19352
|
// O HISTÓRICO DE ATENDIMENTO NA FICHA DA PESSOA.
|
|
19352
19353
|
//
|
|
@@ -19483,9 +19484,9 @@ function createAgendaPlugin(options) {
|
|
|
19483
19484
|
icon: "Calendar",
|
|
19484
19485
|
// Standard layout: General | Integrations (Google Calendar addon) | Properties.
|
|
19485
19486
|
component: (() => {
|
|
19486
|
-
const AgendaSettingsTab = () =>
|
|
19487
|
+
const AgendaSettingsTab = () => React21.createElement(PluginSettingsPanel, {
|
|
19487
19488
|
title: "Agenda",
|
|
19488
|
-
generalSettings:
|
|
19489
|
+
generalSettings: React21.createElement(AgendaGeneralSettings),
|
|
19489
19490
|
registries,
|
|
19490
19491
|
hostPluginId: "agenda",
|
|
19491
19492
|
routeBase: "/settings/agenda"
|