@fayz-ai/plugin-agenda 0.27.11 → 0.27.13

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.
Files changed (45) hide show
  1. package/dist/AgendaGeneralSettings-YBI43RB6.js +96 -0
  2. package/dist/AgendaGeneralSettings-YBI43RB6.js.map +1 -0
  3. package/dist/AgendaMessageTemplates-46ZLTFLM.js +31 -0
  4. package/dist/AgendaMessageTemplates-46ZLTFLM.js.map +1 -0
  5. package/dist/AgendaPage-43OXQDZE.js +3174 -0
  6. package/dist/AgendaPage-43OXQDZE.js.map +1 -0
  7. package/dist/CheckinPage-SGWK2TQZ.js +904 -0
  8. package/dist/CheckinPage-SGWK2TQZ.js.map +1 -0
  9. package/dist/ConfirmationsView-DZJSYBXA.js +361 -0
  10. package/dist/ConfirmationsView-DZJSYBXA.js.map +1 -0
  11. package/dist/MyClientsTodayWidget-ZGTXJJKN.js +127 -0
  12. package/dist/MyClientsTodayWidget-ZGTXJJKN.js.map +1 -0
  13. package/dist/PersonAttendanceBlock-PHZVHFQ4.js +105 -0
  14. package/dist/PersonAttendanceBlock-PHZVHFQ4.js.map +1 -0
  15. package/dist/PersonAttendancesWidget-3NTWBGCU.js +1278 -0
  16. package/dist/PersonAttendancesWidget-3NTWBGCU.js.map +1 -0
  17. package/dist/PersonServicesWidget-4RJGKX3K.js +80 -0
  18. package/dist/PersonServicesWidget-4RJGKX3K.js.map +1 -0
  19. package/dist/chunk-37INOUNW.js +20 -0
  20. package/dist/chunk-37INOUNW.js.map +1 -0
  21. package/dist/chunk-A2VD5DVN.js +12 -0
  22. package/dist/chunk-A2VD5DVN.js.map +1 -0
  23. package/dist/chunk-HGJI2JR6.js +169 -0
  24. package/dist/chunk-HGJI2JR6.js.map +1 -0
  25. package/dist/chunk-VFQPXKXP.js +11 -0
  26. package/dist/chunk-VFQPXKXP.js.map +1 -0
  27. package/dist/chunk-WNANVT4F.js +2386 -0
  28. package/dist/chunk-WNANVT4F.js.map +1 -0
  29. package/dist/chunk-X7PKHXT6.js +18 -0
  30. package/dist/chunk-X7PKHXT6.js.map +1 -0
  31. package/dist/chunk-XTNSK4RJ.js +12 -0
  32. package/dist/chunk-XTNSK4RJ.js.map +1 -0
  33. package/dist/components/PersonAttendanceBlock.d.ts.map +1 -1
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +2530 -19553
  36. package/dist/index.js.map +1 -1
  37. package/dist/migrations/integrations-google-calendar.browser.js +15 -0
  38. package/dist/migrations/integrations-google-calendar.browser.js.map +1 -0
  39. package/dist/migrations/integrations-google-calendar.js +1992 -0
  40. package/dist/migrations/integrations-google-calendar.js.map +1 -0
  41. package/dist/migrations/main.browser.js +29 -0
  42. package/dist/migrations/main.browser.js.map +1 -0
  43. package/dist/migrations/main.js +6440 -0
  44. package/dist/migrations/main.js.map +1 -0
  45. package/package.json +13 -7
@@ -0,0 +1,904 @@
1
+ import { useZoneFilled } from './chunk-VFQPXKXP.js';
2
+ import { StatusFilterList, AppointmentModal } from './chunk-WNANVT4F.js';
3
+ import { AgendaWorkspace, BookingCard, ClientContactButton, AgendaFilterRail, AdaptiveFilter } from './chunk-HGJI2JR6.js';
4
+ import { setAgendaTenantId } from './chunk-A2VD5DVN.js';
5
+ import { AgendaContextProvider, useAgendaConfig, useAgendaProvider, useAgendaStore } from './chunk-XTNSK4RJ.js';
6
+ import React3 from 'react';
7
+ import { usePluginRuntimeOptional, useTranslation, onAppPathChange, resolveDataProvider, formatCurrency } from '@fayz-ai/core';
8
+ import { CircleDollarSign, LogIn, CalendarDays, Play, CheckCircle2, AlarmClock, UserX, Clock, User } from 'lucide-react';
9
+ import { useFieldRules, deriveEntityKey, usePermissionOptional, WidgetSlot, openEntity, PaymentStatusBadge as PaymentStatusBadge$1, RightRailPage, CrudRecordModal } from '@fayz-ai/admin';
10
+ import { SubpageHeader, Button, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '@fayz-ai/ui';
11
+ import { FORMS_ZONES } from '@fayz-ai/forms/views/zones';
12
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
13
+ import { FINANCIAL_ZONES } from '@fayz-ai/financial/views/zones';
14
+
15
+ // src/lib/checkin-required.ts
16
+ function missingRequiredFields(fields, row, skip = ["name"]) {
17
+ if (!row) return [];
18
+ const out = [];
19
+ for (const f of fields) {
20
+ if (!f.required || skip.includes(f.key)) continue;
21
+ const column = f.key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`);
22
+ const has = Object.prototype.hasOwnProperty.call(row, column) || Object.prototype.hasOwnProperty.call(row, f.key);
23
+ if (!has) continue;
24
+ const v = row[column] ?? row[f.key];
25
+ if (v == null || typeof v === "string" && v.trim() === "") {
26
+ out.push({ key: f.key, label: typeof f.label === "string" ? f.label : f.key });
27
+ }
28
+ }
29
+ return out;
30
+ }
31
+ var brl = (n) => formatCurrency(n, "BRL");
32
+ function CheckinDialog({
33
+ booking,
34
+ statusLabel,
35
+ requirements = [],
36
+ onReceive,
37
+ onConfirm,
38
+ clientRecord,
39
+ onClose,
40
+ formatCurrency: formatCurrency3 = brl
41
+ }) {
42
+ const canCompleteInline = !!clientRecord && requirements.some((r) => r.field);
43
+ const [completing, setCompleting] = React3.useState(false);
44
+ const [busy, setBusy] = React3.useState(false);
45
+ const [preenchendo, setPreenchendo] = React3.useState(null);
46
+ const [preenchidos, setPreenchidos] = React3.useState(0);
47
+ const [pendencias, setPendencias] = React3.useState([]);
48
+ const blocking = requirements.filter((r) => r.blocking);
49
+ const checklistFilled = useZoneFilled(FORMS_ZONES.personChecklist);
50
+ const registrationChecks = React3.useMemo(() => requirements.length === 0 ? [{ id: "cadastro", label: "Dados obrigat\xF3rios", state: "done", detail: "completos" }] : requirements.map((r) => ({
51
+ id: `cadastro:${r.label}`,
52
+ label: r.label,
53
+ state: r.blocking ? "late" : "todo",
54
+ detail: r.detail,
55
+ action: r.field && canCompleteInline ? { label: "Completar cadastro", onClick: () => setCompleting(true) } : r.action
56
+ })), [requirements, canCompleteInline]);
57
+ const paid = booking.paymentStatus === "paid";
58
+ const hasBill = !!booking.paymentStatus && booking.paymentStatus !== "none";
59
+ const when = new Date(booking.startsAt).toLocaleString("pt-BR", {
60
+ weekday: "long",
61
+ day: "2-digit",
62
+ month: "2-digit",
63
+ hour: "2-digit",
64
+ minute: "2-digit"
65
+ });
66
+ return /* @__PURE__ */ jsxs(
67
+ RightRailPage,
68
+ {
69
+ title: `${onConfirm ? "Check-in" : "Detalhes"} \u2014 ${booking.clientName ?? "Sem cliente"}`,
70
+ open: true,
71
+ onClose,
72
+ width: "max-w-md",
73
+ children: [
74
+ /* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 space-y-3 overflow-y-auto px-4 py-3", children: [
75
+ /* @__PURE__ */ jsxs("section", { className: "rounded-xl border px-3 py-2", children: [
76
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-medium uppercase text-muted-foreground", children: "Agendamento" }),
77
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 flex flex-wrap items-center gap-2 text-xs", children: [
78
+ /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1", children: [
79
+ /* @__PURE__ */ jsx(Clock, { className: "h-3 w-3" }),
80
+ when,
81
+ booking.professionalName ? ` \xB7 ${booking.professionalName}` : ""
82
+ ] }),
83
+ /* @__PURE__ */ jsx("span", { className: "rounded-full bg-muted px-2 py-0.5 font-medium", children: statusLabel }),
84
+ booking.services?.length ? /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: booking.services.map((s) => s.name).join(", ") }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Servi\xE7o n\xE3o informado" })
85
+ ] })
86
+ ] }),
87
+ /* @__PURE__ */ jsxs("section", { className: "rounded-xl border px-3 py-2", children: [
88
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-medium uppercase text-muted-foreground", children: "Cadastro do cliente" }),
89
+ /* @__PURE__ */ jsx(
90
+ WidgetSlot,
91
+ {
92
+ zone: FORMS_ZONES.personChecklist,
93
+ version: preenchidos,
94
+ personId: booking.clientId,
95
+ className: "mt-2",
96
+ extras: registrationChecks,
97
+ onPending: setPendencias,
98
+ onFill: (templateId) => setPreenchendo(templateId)
99
+ }
100
+ ),
101
+ !checklistFilled && /* @__PURE__ */ jsx("ul", { className: "mt-2 space-y-0.5 text-xs", children: registrationChecks.map((c) => /* @__PURE__ */ jsxs("li", { className: c.state === "done" ? "text-muted-foreground" : "font-medium text-foreground", children: [
102
+ c.label,
103
+ c.detail ? ` \u2014 ${c.detail}` : ""
104
+ ] }, c.id)) }),
105
+ canCompleteInline && /* @__PURE__ */ jsx(
106
+ CrudRecordModal,
107
+ {
108
+ open: completing,
109
+ onClose: () => setCompleting(false),
110
+ onSaved: () => setCompleting(false),
111
+ entityDef: clientRecord.entityDef,
112
+ load: clientRecord.load,
113
+ save: clientRecord.save,
114
+ title: `Completar cadastro \u2014 ${booking.clientName ?? "cliente"}`,
115
+ highlightMissing: true
116
+ }
117
+ ),
118
+ preenchendo && booking.clientId && /* @__PURE__ */ jsx(
119
+ WidgetSlot,
120
+ {
121
+ zone: FORMS_ZONES.formFill,
122
+ frame: "inline",
123
+ templateId: preenchendo,
124
+ personId: booking.clientId,
125
+ personName: booking.clientName,
126
+ onSaved: () => setPreenchidos((n) => n + 1),
127
+ onClose: () => setPreenchendo(null)
128
+ }
129
+ )
130
+ ] }),
131
+ /* @__PURE__ */ jsxs("section", { className: "rounded-xl border px-3 py-2", children: [
132
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-medium uppercase text-muted-foreground", children: "Pagamento" }),
133
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center justify-between gap-2", children: [
134
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: paid ? "Este atendimento j\xE1 est\xE1 pago." : hasBill ? `Em aberto${booking.orderTotal ? ` \xB7 ${formatCurrency3(booking.orderTotal)}` : ""}.` : "Nenhuma fatura vinculada a este agendamento." }),
135
+ onReceive && !paid && /* @__PURE__ */ jsxs(
136
+ Button,
137
+ {
138
+ variant: "bare",
139
+ size: "bare",
140
+ onClick: () => onReceive(booking),
141
+ className: "inline-flex shrink-0 items-center gap-1.5 rounded-button border border-input px-3 py-1.5 text-xs font-medium hover:bg-muted",
142
+ children: [
143
+ /* @__PURE__ */ jsx(CircleDollarSign, { className: "h-3.5 w-3.5" }),
144
+ " Receber"
145
+ ]
146
+ }
147
+ )
148
+ ] })
149
+ ] })
150
+ ] }),
151
+ onConfirm ? /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-between gap-2 border-t px-4 py-3", children: [
152
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: pendencias.length ? `Falta ${pendencias.join(", ")}.` : "" }),
153
+ /* @__PURE__ */ jsxs(
154
+ Button,
155
+ {
156
+ variant: "bare",
157
+ size: "bare",
158
+ disabled: busy || blocking.length > 0 || pendencias.length > 0,
159
+ title: pendencias.length ? `Falta ${pendencias.join(", ")}` : void 0,
160
+ onClick: async () => {
161
+ setBusy(true);
162
+ try {
163
+ await onConfirm(booking);
164
+ } finally {
165
+ setBusy(false);
166
+ }
167
+ },
168
+ className: "inline-flex shrink-0 items-center gap-1.5 rounded-button bg-primary border border-primary px-4 py-1.5 text-xs font-medium text-primary-foreground hover:bg-primary/90 disabled:pointer-events-auto disabled:opacity-50",
169
+ children: [
170
+ /* @__PURE__ */ jsx(User, { className: "h-3.5 w-3.5" }),
171
+ " ",
172
+ busy ? "Concluindo\u2026" : pendencias.length ? "Concluir (h\xE1 pend\xEAncias)" : "Concluir check-in"
173
+ ]
174
+ }
175
+ )
176
+ ] }) : null
177
+ ]
178
+ }
179
+ );
180
+ }
181
+ function PaymentStatusBadge(props) {
182
+ const t = useTranslation();
183
+ const labels = React3.useMemo(() => ({
184
+ none: t("agenda.payment.title"),
185
+ pending: t("agenda.payment.pending"),
186
+ partial: t("agenda.payment.partial"),
187
+ paid: t("agenda.payment.paid"),
188
+ overdue: t("agenda.payment.overdue"),
189
+ cancelled: t("agenda.payment.cancelled")
190
+ }), [t]);
191
+ return /* @__PURE__ */ jsx(PaymentStatusBadge$1, { ...props, labels });
192
+ }
193
+ var CHEGOU = /* @__PURE__ */ new Set(["waiting", "in_progress", "completed"]);
194
+ function CheckinScoreboard({
195
+ bookings,
196
+ noShow = 0,
197
+ openAmount = 0,
198
+ formatCurrency: formatCurrency3,
199
+ selectedStatuses,
200
+ onPickStatus
201
+ }) {
202
+ const agora = Date.now();
203
+ const total = bookings.length;
204
+ const chegaram = bookings.filter((b) => CHEGOU.has(b.status)).length;
205
+ const aguardando = bookings.filter((b) => b.status === "waiting").length;
206
+ const emAtendimento = bookings.filter((b) => b.status === "in_progress").length;
207
+ const atrasados = bookings.filter(
208
+ (b) => (b.status === "scheduled" || b.status === "confirmed") && new Date(b.startsAt).getTime() < agora
209
+ ).length;
210
+ const pct = total > 0 ? Math.round(chegaram / total * 100) : 0;
211
+ const tudoCerto = total > 0 && chegaram === total;
212
+ const pastilhas = [
213
+ {
214
+ key: "atrasado",
215
+ label: "atrasados",
216
+ n: atrasados,
217
+ Icon: AlarmClock,
218
+ status: null,
219
+ tone: "border-destructive/40 bg-destructive/10 text-destructive",
220
+ hideWhenZero: true
221
+ },
222
+ {
223
+ key: "waiting",
224
+ label: "aguardando",
225
+ n: aguardando,
226
+ Icon: LogIn,
227
+ status: "waiting",
228
+ tone: "border-warning/40 bg-warning/10 text-warning-foreground",
229
+ hideWhenZero: false
230
+ },
231
+ {
232
+ key: "in_progress",
233
+ label: "em atendimento",
234
+ n: emAtendimento,
235
+ Icon: CheckCircle2,
236
+ status: "in_progress",
237
+ tone: "border-success/40 bg-success/10 text-success",
238
+ hideWhenZero: false
239
+ },
240
+ {
241
+ key: "no_show",
242
+ label: "faltaram",
243
+ n: noShow,
244
+ Icon: UserX,
245
+ status: "no_show",
246
+ tone: "border-border bg-muted text-muted-foreground",
247
+ hideWhenZero: true
248
+ }
249
+ ].filter((p) => !(p.hideWhenZero && p.n === 0));
250
+ return /* @__PURE__ */ jsxs("div", { className: `rounded-lg border p-3 transition-colors ${tudoCerto ? "border-success/40 bg-success/5" : "border-border bg-card"}`, children: [
251
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1", children: [
252
+ /* @__PURE__ */ jsxs("span", { className: "flex items-baseline gap-2", children: [
253
+ /* @__PURE__ */ jsxs("span", { className: `text-2xl font-semibold tabular-nums transition-colors ${tudoCerto ? "text-success" : "text-foreground"}`, children: [
254
+ pct,
255
+ "%"
256
+ ] }),
257
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
258
+ chegaram,
259
+ " de ",
260
+ total,
261
+ " chegaram"
262
+ ] })
263
+ ] }),
264
+ openAmount > 0 && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 text-xs text-muted-foreground", children: [
265
+ /* @__PURE__ */ jsx(CircleDollarSign, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": true }),
266
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums font-medium text-foreground", children: formatCurrency3(openAmount) }),
267
+ " ",
268
+ "a receber hoje"
269
+ ] })
270
+ ] }),
271
+ /* @__PURE__ */ jsx(
272
+ "div",
273
+ {
274
+ className: "h-1.5 overflow-hidden rounded-full bg-muted",
275
+ role: "progressbar",
276
+ "aria-valuenow": pct,
277
+ "aria-valuemin": 0,
278
+ "aria-valuemax": 100,
279
+ "aria-label": "Quantos j\xE1 chegaram hoje",
280
+ children: /* @__PURE__ */ jsx(
281
+ "div",
282
+ {
283
+ className: `h-full rounded-full transition-[width] duration-500 ease-out ${tudoCerto ? "bg-success" : "bg-primary"}`,
284
+ style: { width: `${pct}%` }
285
+ }
286
+ )
287
+ }
288
+ ),
289
+ pastilhas.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-2.5 flex flex-wrap items-center gap-1.5", children: pastilhas.map(({ key, label, n, Icon, status, tone }) => {
290
+ const ligado = status ? selectedStatuses.includes(status) : false;
291
+ const conteudo = /* @__PURE__ */ jsxs(Fragment, { children: [
292
+ /* @__PURE__ */ jsx(Icon, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": true }),
293
+ /* @__PURE__ */ jsx("span", { className: "tabular-nums font-semibold", children: n }),
294
+ /* @__PURE__ */ jsx("span", { children: label })
295
+ ] });
296
+ const base = `inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs ${tone}`;
297
+ return status ? /* @__PURE__ */ jsx(
298
+ Button,
299
+ {
300
+ variant: "bare",
301
+ size: "bare",
302
+ type: "button",
303
+ onClick: () => onPickStatus(status),
304
+ "aria-pressed": ligado,
305
+ className: `font-normal ${base} transition-all hover:brightness-95 active:scale-95 ${ligado ? "ring-1 ring-current" : ""}`,
306
+ children: conteudo
307
+ },
308
+ key
309
+ ) : /* @__PURE__ */ jsx("span", { className: base, children: conteudo }, key);
310
+ }) })
311
+ ] });
312
+ }
313
+ var CHECKIN_STATUSES = ["scheduled", "confirmed", "waiting", "in_progress"];
314
+ var STATUS_TONE = {
315
+ scheduled: "bg-muted text-muted-foreground",
316
+ confirmed: "bg-primary/10 text-primary",
317
+ waiting: "bg-warning/15 text-warning-foreground",
318
+ in_progress: "bg-success/15 text-success"
319
+ };
320
+ var STATUS_FLAG = {
321
+ scheduled: "bg-muted-foreground/30",
322
+ confirmed: "bg-primary",
323
+ waiting: "bg-warning",
324
+ in_progress: "bg-success"
325
+ };
326
+ function todayRange() {
327
+ const start = /* @__PURE__ */ new Date();
328
+ start.setHours(0, 0, 0, 0);
329
+ const end = /* @__PURE__ */ new Date();
330
+ end.setHours(23, 59, 59, 999);
331
+ return { start: start.toISOString(), end: end.toISOString() };
332
+ }
333
+ function waitedFor(iso) {
334
+ const min = Math.floor((Date.now() - new Date(iso).getTime()) / 6e4);
335
+ if (min < 1) return "agora mesmo";
336
+ if (min < 60) return `h\xE1 ${min} min`;
337
+ const h = Math.floor(min / 60);
338
+ return `h\xE1 ${h}h${min % 60 ? ` ${min % 60}min` : ""}`;
339
+ }
340
+ function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }) {
341
+ const t = useTranslation();
342
+ const config = useAgendaConfig();
343
+ const provider = useAgendaProvider();
344
+ const statusLabel = (v) => config.statuses.find((x) => x.value === v)?.label ?? v;
345
+ const checkinStatuses = React3.useMemo(
346
+ () => CHECKIN_STATUSES.map((v) => config.statuses.find((x) => x.value === v)).filter((x) => !!x),
347
+ [config.statuses]
348
+ );
349
+ const bookings = useAgendaStore((s) => s.bookings);
350
+ const loading = useAgendaStore((s) => s.bookingsLoading);
351
+ const fetchBookings = useAgendaStore((s) => s.fetchBookings);
352
+ const updateBookingStatus = useAgendaStore((s) => s.updateBookingStatus);
353
+ const [term, setTerm] = React3.useState("");
354
+ const [busyId, setBusyId] = React3.useState(null);
355
+ const [myPersonId, setMyPersonId] = React3.useState(void 0);
356
+ const [startRefusal, setStartRefusal] = React3.useState(null);
357
+ const [checkouts, setCheckouts] = React3.useState([]);
358
+ const [conferindo, setConferindo] = React3.useState(null);
359
+ const [checkoutRefusal, setCheckoutRefusal] = React3.useState(null);
360
+ const [panel, setPanel] = React3.useState(null);
361
+ const reviewing = panel?.kind === "review" ? panel.booking : null;
362
+ const clientDef = config.clientEntityDef;
363
+ const { applyRules } = useFieldRules(clientDef ? deriveEntityKey(clientDef) : "");
364
+ const clientFields = React3.useMemo(() => clientDef ? applyRules(clientDef.fields) : [], [clientDef, applyRules]);
365
+ const [clientRow, setClientRow] = React3.useState(null);
366
+ const [clientRowVersion, setClientRowVersion] = React3.useState(0);
367
+ React3.useEffect(() => {
368
+ const id = reviewing?.clientId;
369
+ if (!id || !config.contactLookup?.getById) {
370
+ setClientRow(null);
371
+ return;
372
+ }
373
+ let alive = true;
374
+ config.contactLookup.getById(id).then((r) => {
375
+ if (alive) setClientRow(r?.data ? { ...r.data, id } : null);
376
+ }).catch(() => {
377
+ if (alive) setClientRow(null);
378
+ });
379
+ return () => {
380
+ alive = false;
381
+ };
382
+ }, [reviewing?.clientId, config.contactLookup, clientRowVersion]);
383
+ React3.useEffect(() => {
384
+ const again = () => setClientRowVersion((n) => n + 1);
385
+ window.addEventListener("focus", again);
386
+ const off = onAppPathChange(again);
387
+ return () => {
388
+ window.removeEventListener("focus", again);
389
+ off();
390
+ };
391
+ }, []);
392
+ const clientProvider = React3.useMemo(
393
+ () => clientDef ? resolveDataProvider(clientDef) : null,
394
+ [clientDef]
395
+ );
396
+ const clientRecord = clientDef && clientProvider && reviewing?.clientId ? {
397
+ // The house's rules (CPF required…) are what the check-in judged; the modal must agree.
398
+ entityDef: { ...clientDef, fields: clientFields },
399
+ load: async () => {
400
+ const res = await clientProvider.list({ filters: { id: reviewing.clientId }, pageSize: 1, countMode: "none" });
401
+ return res?.data?.[0] ?? null;
402
+ },
403
+ save: async (data) => {
404
+ await clientProvider.update(reviewing.clientId, data);
405
+ setClientRowVersion((n) => n + 1);
406
+ }
407
+ } : void 0;
408
+ const editing = panel?.kind === "edit" ? panel.booking : null;
409
+ const charging = panel?.kind === "charge" ? panel.booking : null;
410
+ const setReviewing = (b) => setPanel(b ? { kind: "review", booking: b } : null);
411
+ const setEditing = (b) => setPanel(b ? { kind: "edit", booking: b } : null);
412
+ const setCharging = (b) => setPanel(b ? { kind: "charge", booking: b } : null);
413
+ const can = usePermissionOptional();
414
+ const accountMounted = useZoneFilled(FINANCIAL_ZONES.clientAccount);
415
+ const canReceive = accountMounted && (can("financial", "create") || can("financial.receivables", "create"));
416
+ React3.useEffect(() => {
417
+ void fetchBookings(todayRange(), [...CHECKIN_STATUSES]);
418
+ }, [fetchBookings]);
419
+ const [shownStatuses, setShownStatuses] = React3.useState([]);
420
+ const [shownPros, setShownPros] = React3.useState([]);
421
+ const prosToday = React3.useMemo(() => {
422
+ const seen = /* @__PURE__ */ new Map();
423
+ for (const b of bookings) if (b.professionalName) seen.set(b.professionalName, b.professionalName);
424
+ return [...seen.keys()].sort();
425
+ }, [bookings]);
426
+ const cards = React3.useMemo(() => {
427
+ const q = term.trim().toLowerCase();
428
+ 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));
429
+ }, [bookings, term, shownStatuses, shownPros]);
430
+ const waiting = cards.filter((b) => b.status === "waiting").length;
431
+ function requirementsFor(b) {
432
+ const out = [];
433
+ if (b.clientId && b.clientId === clientRow?.id) {
434
+ for (const m of missingRequiredFields(clientFields, clientRow)) {
435
+ out.push({
436
+ label: m.label,
437
+ detail: "obrigat\xF3rio no cadastro",
438
+ blocking: true,
439
+ field: clientFields.find((f) => f.key === m.key),
440
+ action: { label: "Completar cadastro", onClick: () => openEntity(b.clientId, "person", "customer") }
441
+ });
442
+ }
443
+ }
444
+ if (!b.clientName) out.push({ label: "Cliente", detail: "o agendamento n\xE3o nomeia ningu\xE9m", blocking: true });
445
+ if (!b.professionalId) out.push({ label: "Profissional", detail: "ningu\xE9m para executar" });
446
+ if (!b.services?.length) out.push({ label: "Servi\xE7o", detail: "nada a executar nem a cobrar" });
447
+ return out;
448
+ }
449
+ const money = (n) => formatCurrency(n, "BRL");
450
+ const [lost, setLost] = React3.useState({ cancelled: 0, noShow: 0 });
451
+ React3.useEffect(() => {
452
+ if (!provider.getLostCounts) return;
453
+ let cancelado = false;
454
+ provider.getLostCounts(todayRange()).then((c) => {
455
+ if (!cancelado) setLost(c);
456
+ }).catch(() => {
457
+ });
458
+ return () => {
459
+ cancelado = true;
460
+ };
461
+ }, [provider]);
462
+ const openAmount = React3.useMemo(
463
+ () => bookings.filter((b) => b.orderTotal && b.paymentStatus && b.paymentStatus !== "paid").reduce((soma, b) => soma + (b.orderTotal ?? 0), 0),
464
+ [bookings]
465
+ );
466
+ async function loadPaymentLines(orderId) {
467
+ const detail = await config.financialBridge?.getPaymentDetail(orderId);
468
+ return (detail?.movements ?? []).filter((m) => m.paymentDate).map((m) => ({
469
+ label: [m.paymentMethodTypeName, m.cardBrand, m.cardInstallments && m.cardInstallments > 1 ? `${m.cardInstallments}x` : null].filter(Boolean).join(" \xB7 ") || "Pagamento",
470
+ date: m.paymentDate ?? null,
471
+ amount: m.paidAmount
472
+ }));
473
+ }
474
+ React3.useEffect(() => {
475
+ let vivo = true;
476
+ (async () => {
477
+ try {
478
+ const id = await provider.getCurrentPersonId?.();
479
+ if (vivo) setMyPersonId(id ?? null);
480
+ } catch {
481
+ if (vivo) setMyPersonId(null);
482
+ }
483
+ })();
484
+ return () => {
485
+ vivo = false;
486
+ };
487
+ }, []);
488
+ const recarregarCheckouts = React3.useCallback(async () => {
489
+ try {
490
+ setCheckouts(await provider.getPendingCheckouts?.() ?? []);
491
+ } catch {
492
+ setCheckouts([]);
493
+ }
494
+ }, []);
495
+ React3.useEffect(() => {
496
+ void recarregarCheckouts();
497
+ }, [recarregarCheckouts]);
498
+ React3.useEffect(() => {
499
+ const parar = provider.subscribeToBookingChanges?.(() => {
500
+ void recarregarCheckouts();
501
+ void fetchBookings(todayRange(), [...CHECKIN_STATUSES]);
502
+ });
503
+ const rede = setInterval(() => {
504
+ if (document.visibilityState === "visible") void recarregarCheckouts();
505
+ }, 3e4);
506
+ const aoVoltar = () => {
507
+ if (document.visibilityState === "visible") void recarregarCheckouts();
508
+ };
509
+ document.addEventListener("visibilitychange", aoVoltar);
510
+ return () => {
511
+ parar?.();
512
+ clearInterval(rede);
513
+ document.removeEventListener("visibilitychange", aoVoltar);
514
+ };
515
+ }, [recarregarCheckouts]);
516
+ const RECUSA_CHECKOUT = {
517
+ not_found: "Este atendimento n\xE3o existe mais.",
518
+ sem_sessao: "Sess\xE3o expirada. Entre de novo para conferir.",
519
+ atendimento_nao_terminou: "O atendimento ainda n\xE3o foi encerrado \u2014 n\xE3o h\xE1 o que conferir."
520
+ };
521
+ async function concluirCheckout(b) {
522
+ const chave = b.clientId ?? b.id;
523
+ const doCliente = checkouts.filter((c) => (c.clientId ?? c.id) === chave);
524
+ setConferindo(b.id);
525
+ setCheckoutRefusal(null);
526
+ try {
527
+ for (const c of doCliente) {
528
+ const r = await provider.confirmCheckout?.(c.id);
529
+ if (r && r.ok !== true) {
530
+ setCheckoutRefusal(RECUSA_CHECKOUT[r.reason ?? ""] ?? "N\xE3o foi poss\xEDvel concluir o checkout.");
531
+ return;
532
+ }
533
+ }
534
+ await recarregarCheckouts();
535
+ } catch (e) {
536
+ setCheckoutRefusal(e?.message ?? "N\xE3o foi poss\xEDvel concluir o checkout.");
537
+ } finally {
538
+ setConferindo(null);
539
+ }
540
+ }
541
+ const RECUSA = {
542
+ sem_ficha_de_profissional: "Este login n\xE3o tem ficha de profissional. S\xF3 quem atende inicia o atendimento.",
543
+ nao_e_seu_atendimento: "Este atendimento \xE9 de outro profissional.",
544
+ nao_e_de_hoje: "S\xF3 d\xE1 para iniciar um atendimento de hoje.",
545
+ ja_encerrado: "Este atendimento j\xE1 foi encerrado.",
546
+ not_found: "Este agendamento n\xE3o existe mais."
547
+ };
548
+ function abrirAtendimento(b) {
549
+ if (onOpenAttendance) {
550
+ onOpenAttendance(b.id);
551
+ return;
552
+ }
553
+ if (b.clientId) openEntity(b.clientId, "person", "customer", "attendance");
554
+ }
555
+ async function startAttendance(b) {
556
+ setBusyId(b.id);
557
+ setStartRefusal(null);
558
+ try {
559
+ const r = await provider.startAttendance?.(b.id);
560
+ if (!r?.ok) {
561
+ setStartRefusal(RECUSA[r?.reason ?? ""] ?? "N\xE3o foi poss\xEDvel iniciar o atendimento.");
562
+ return;
563
+ }
564
+ await fetchBookings(todayRange(), [...CHECKIN_STATUSES]);
565
+ abrirAtendimento(b);
566
+ } finally {
567
+ setBusyId(null);
568
+ }
569
+ }
570
+ async function confirmCheckin(b) {
571
+ setBusyId(b.id);
572
+ try {
573
+ await updateBookingStatus(b.id, "waiting");
574
+ await fetchBookings(todayRange(), [...CHECKIN_STATUSES]);
575
+ setReviewing(null);
576
+ } finally {
577
+ setBusyId(null);
578
+ }
579
+ }
580
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
581
+ /* @__PURE__ */ jsx(
582
+ SubpageHeader,
583
+ {
584
+ title: "Check-in",
585
+ subtitle: waiting ? `${waiting} aguardando` : void 0
586
+ }
587
+ ),
588
+ /* @__PURE__ */ jsx(
589
+ AgendaWorkspace,
590
+ {
591
+ rail: /* @__PURE__ */ jsx(
592
+ AgendaFilterRail,
593
+ {
594
+ search: {
595
+ value: term,
596
+ onChange: setTerm,
597
+ placeholder: "Buscar cliente ou profissional"
598
+ },
599
+ sections: [
600
+ {
601
+ id: "professionals",
602
+ title: t("agenda.sidebar.professionals"),
603
+ count: shownPros.length,
604
+ content: /* @__PURE__ */ jsxs(Fragment, { children: [
605
+ loading && prosToday.length === 0 && [0, 1, 2].map((i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2.5 px-1 py-1", children: [
606
+ /* @__PURE__ */ jsx("div", { className: "h-4 w-4 animate-pulse rounded-sm bg-muted" }),
607
+ /* @__PURE__ */ jsx("div", { className: "h-3.5 w-24 animate-pulse rounded bg-muted/70" })
608
+ ] }, i)),
609
+ /* @__PURE__ */ jsx(
610
+ AdaptiveFilter,
611
+ {
612
+ options: prosToday.map((name) => ({ value: name, label: name, avatarUrl: null })),
613
+ value: shownPros,
614
+ onChange: setShownPros,
615
+ allLabel: t("agenda.sidebar.allProfessionals")
616
+ }
617
+ )
618
+ ] })
619
+ },
620
+ {
621
+ id: "status",
622
+ title: t("agenda.sidebar.status"),
623
+ count: shownStatuses.length,
624
+ // Aberto por padrão: com a cor ao lado de cada nome, esta lista
625
+ // é também a LEGENDA dos selos da fila. Recolhida, o selo
626
+ // colorido do cartão fica sem dicionário.
627
+ defaultOpen: true,
628
+ content: /* @__PURE__ */ jsx(
629
+ StatusFilterList,
630
+ {
631
+ statuses: checkinStatuses,
632
+ selected: shownStatuses,
633
+ onToggle: (value) => setShownStatuses((cur) => cur.includes(value) ? cur.filter((x) => x !== value) : [...cur, value])
634
+ }
635
+ )
636
+ }
637
+ ]
638
+ }
639
+ ),
640
+ children: /* @__PURE__ */ jsxs("div", { className: "min-w-0 space-y-2", children: [
641
+ /* @__PURE__ */ jsx(
642
+ CheckinScoreboard,
643
+ {
644
+ bookings,
645
+ noShow: lost.noShow,
646
+ openAmount,
647
+ formatCurrency: money,
648
+ selectedStatuses: shownStatuses,
649
+ onPickStatus: (st) => setShownStatuses((cur) => cur.includes(st) ? cur.filter((x) => x !== st) : [...cur, st])
650
+ }
651
+ ),
652
+ /* @__PURE__ */ jsx("p", { className: "pb-1 pt-0.5 text-xs text-muted-foreground", children: "Receba o cliente, complete o cadastro e marque como aguardando." }),
653
+ checkouts.length > 0 && (() => {
654
+ const porCliente = [...new Map(checkouts.map((b) => [b.clientId ?? b.id, b])).values()];
655
+ return /* @__PURE__ */ jsxs("section", { className: "space-y-2 rounded-2xl border border-warning/40 bg-warning/5 p-3", children: [
656
+ /* @__PURE__ */ jsxs("h2", { className: "flex items-center gap-2 text-xs font-semibold text-warning-foreground", children: [
657
+ /* @__PURE__ */ jsx(CircleDollarSign, { className: "h-3.5 w-3.5", "aria-hidden": true }),
658
+ porCliente.length === 1 ? "1 checkout pendente" : `${porCliente.length} checkouts pendentes`
659
+ ] }),
660
+ checkoutRefusal && /* @__PURE__ */ jsx("p", { className: "rounded-lg bg-destructive/10 px-3 py-2 text-xs text-destructive", children: checkoutRefusal }),
661
+ /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: porCliente.map((b) => /* @__PURE__ */ jsxs("li", { className: "flex flex-wrap items-center gap-2 rounded-xl border border-border bg-card px-3 py-2", children: [
662
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 basis-full sm:basis-0 sm:flex-1", children: [
663
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium", children: b.clientName ?? "Cliente" }),
664
+ /* @__PURE__ */ jsxs("p", { className: "truncate text-[11px] text-muted-foreground", children: [
665
+ b.professionalName ?? "Sem profissional",
666
+ " \xB7 ",
667
+ b.startsAt.slice(11, 16),
668
+ b.orderTotal != null && b.orderTotal > 0 ? ` \xB7 ${money(b.orderTotal)}` : "",
669
+ (() => {
670
+ const n = checkouts.filter((c) => (c.clientId ?? c.id) === (b.clientId ?? b.id)).length;
671
+ return n > 1 ? ` \xB7 ${n} atendimentos` : "";
672
+ })()
673
+ ] })
674
+ ] }),
675
+ accountMounted && b.orderTotal != null && b.orderTotal > 0 && /* @__PURE__ */ jsx(
676
+ Button,
677
+ {
678
+ variant: "bare",
679
+ size: "bare",
680
+ type: "button",
681
+ onClick: () => setCharging(b),
682
+ className: "shrink-0 rounded-button border border-input px-2.5 py-1.5 text-xs font-medium hover:bg-muted",
683
+ children: "Ver conta"
684
+ }
685
+ ),
686
+ (() => {
687
+ const est = b.metadata?.checkout;
688
+ if (est?.conferido) {
689
+ return /* @__PURE__ */ jsxs("span", { className: "shrink-0 rounded-button bg-warning/15 px-2.5 py-1.5 text-[11px] font-medium text-warning-foreground", children: [
690
+ "Conferido \xB7 falta receber",
691
+ est.emAberto ? ` ${money(est.emAberto)}` : ""
692
+ ] });
693
+ }
694
+ return /* @__PURE__ */ jsx(
695
+ Button,
696
+ {
697
+ variant: "bare",
698
+ size: "bare",
699
+ type: "button",
700
+ disabled: conferindo === b.id,
701
+ onClick: () => concluirCheckout(b),
702
+ className: "shrink-0 rounded-button bg-foreground px-3 py-1.5 text-xs font-semibold text-background disabled:opacity-40",
703
+ children: conferindo === b.id ? "Conferindo\u2026" : "Concluir checkout"
704
+ }
705
+ );
706
+ })()
707
+ ] }, b.id)) })
708
+ ] });
709
+ })(),
710
+ startRefusal && /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2 rounded-lg bg-warning/10 px-3 py-2 text-xs text-warning-foreground", children: [
711
+ /* @__PURE__ */ jsx("span", { className: "flex-1", children: startRefusal }),
712
+ /* @__PURE__ */ jsx(Button, { variant: "bare", size: "bare", type: "button", onClick: () => setStartRefusal(null), className: "shrink-0 text-xs font-medium hover:underline", children: "Fechar" })
713
+ ] }),
714
+ loading && cards.length === 0 ? (
715
+ /* Esqueleto com a FORMA da linha: a fila não salta quando os dados
716
+ chegam, e a recepção já sabe onde olhar antes de haver o que ler. */
717
+ /* @__PURE__ */ jsx("ul", { className: "space-y-2", "aria-busy": "true", "aria-label": "Carregando a fila de hoje", children: [0, 1, 2, 3].map((i) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-3 rounded-2xl border border-border bg-card px-4 py-3", children: [
718
+ /* @__PURE__ */ jsx("div", { className: "h-4 w-12 animate-pulse rounded bg-muted" }),
719
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1 space-y-1.5", children: [
720
+ /* @__PURE__ */ jsx("div", { className: "h-3.5 w-40 animate-pulse rounded bg-muted" }),
721
+ /* @__PURE__ */ jsx("div", { className: "h-3 w-24 animate-pulse rounded bg-muted/70" })
722
+ ] }),
723
+ /* @__PURE__ */ jsx("div", { className: "h-4 w-16 animate-pulse rounded bg-muted" }),
724
+ /* @__PURE__ */ jsx("div", { className: "h-6 w-20 animate-pulse rounded-full bg-muted" }),
725
+ /* @__PURE__ */ jsx("div", { className: "h-7 w-24 animate-pulse rounded-button bg-muted" })
726
+ ] }, i)) })
727
+ ) : cards.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "rounded-2xl border-2 border-dashed border-muted px-6 py-12 text-center", children: [
728
+ /* @__PURE__ */ jsx("div", { className: "mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-xl bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx(LogIn, { className: "h-6 w-6" }) }),
729
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: "Ningu\xE9m na fila" }),
730
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: term ? "Nenhum atendimento de hoje casa com a busca." : "N\xE3o h\xE1 atendimentos para hoje." })
731
+ ] }) : /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: cards.map((b) => {
732
+ const arrived = b.status === "waiting" || b.status === "in_progress";
733
+ const late = !arrived && b.status !== "completed" && new Date(b.startsAt).getTime() < Date.now();
734
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 300, children: /* @__PURE__ */ jsx(
735
+ BookingCard,
736
+ {
737
+ booking: b,
738
+ clientKind: "customer",
739
+ busy: busyId === b.id,
740
+ selected: panel?.booking.id === b.id,
741
+ showMoney: false,
742
+ late,
743
+ flagClass: late ? "bg-destructive" : STATUS_FLAG[b.status] ?? "bg-border",
744
+ badge: /* @__PURE__ */ jsxs("span", { className: `rounded-full px-2 py-0.5 text-[10px] font-medium ${STATUS_TONE[b.status] ?? ""}`, children: [
745
+ statusLabel(b.status),
746
+ arrived && b.checkedInAt && /* @__PURE__ */ jsxs("span", { className: "ml-1 opacity-70", children: [
747
+ "\xB7 ",
748
+ waitedFor(b.checkedInAt)
749
+ ] })
750
+ ] }),
751
+ aside: (
752
+ /* Quanto é, e em que pé está. A recepção cobra na saída e
753
+ precisa do valor na linha; procurá-lo abrindo o
754
+ agendamento é um clique por cliente numa tela feita para
755
+ não ter nenhum. Sem fatura levantada mostra só o número:
756
+ um silêncio é mais honesto que "em aberto". */
757
+ b.orderTotal != null && b.orderTotal > 0 ? /* @__PURE__ */ jsx(
758
+ PaymentStatusBadge,
759
+ {
760
+ status: b.paymentStatus ?? "none",
761
+ totalAmount: b.orderTotal,
762
+ formatCurrency: money,
763
+ compact: true,
764
+ onLoadDetails: b.orderId ? () => loadPaymentLines(b.orderId) : void 0,
765
+ onReceive: canReceive ? () => setCharging(b) : void 0,
766
+ onOpenDetails: () => setCharging(b)
767
+ }
768
+ ) : void 0
769
+ ),
770
+ actions: /* @__PURE__ */ jsxs(Fragment, { children: [
771
+ /* @__PURE__ */ jsxs(Tooltip, { children: [
772
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
773
+ Button,
774
+ {
775
+ variant: "bare",
776
+ size: "bare",
777
+ type: "button",
778
+ onClick: () => onOpenBooking ? onOpenBooking(b) : setEditing(b),
779
+ "aria-label": "Abrir agendamento",
780
+ className: "inline-flex items-center justify-center rounded-button border border-input p-1.5 hover:bg-muted",
781
+ children: /* @__PURE__ */ jsx(CalendarDays, { className: "h-3.5 w-3.5", "aria-hidden": true })
782
+ }
783
+ ) }),
784
+ /* @__PURE__ */ jsx(TooltipContent, { children: "Abrir agendamento" })
785
+ ] }),
786
+ b.clientPhone && /* @__PURE__ */ jsx(ClientContactButton, { phone: b.clientPhone, compact: true }),
787
+ arrived && b.status !== "completed" && myPersonId !== void 0 && (myPersonId === null || b.professionalId === myPersonId) && /* @__PURE__ */ jsxs(
788
+ Button,
789
+ {
790
+ variant: "bare",
791
+ size: "bare",
792
+ type: "button",
793
+ disabled: myPersonId === null || busyId === b.id,
794
+ onClick: () => b.status === "in_progress" ? abrirAtendimento(b) : startAttendance(b),
795
+ title: myPersonId === null ? RECUSA.sem_ficha_de_profissional : void 0,
796
+ className: "inline-flex items-center gap-1.5 rounded-button bg-foreground px-3 py-1.5 text-xs font-semibold text-background disabled:pointer-events-auto disabled:opacity-40",
797
+ children: [
798
+ /* @__PURE__ */ jsx(Play, { className: "h-3.5 w-3.5", "aria-hidden": true }),
799
+ b.status === "in_progress" ? "Continuar" : "Iniciar atendimento"
800
+ ]
801
+ }
802
+ ),
803
+ b.status === "completed" ? null : arrived ? /* @__PURE__ */ jsxs(
804
+ Button,
805
+ {
806
+ variant: "bare",
807
+ size: "bare",
808
+ type: "button",
809
+ onClick: () => setReviewing(b),
810
+ className: "inline-flex items-center gap-1.5 rounded-button border border-input px-3 py-1.5 text-xs font-medium hover:bg-muted",
811
+ children: [
812
+ /* @__PURE__ */ jsx(CheckCircle2, { className: "h-3.5 w-3.5", "aria-hidden": true }),
813
+ "Detalhes"
814
+ ]
815
+ }
816
+ ) : /* @__PURE__ */ jsxs(
817
+ Button,
818
+ {
819
+ variant: "bare",
820
+ size: "bare",
821
+ type: "button",
822
+ disabled: busyId === b.id,
823
+ onClick: () => setReviewing(b),
824
+ className: "inline-flex items-center gap-1.5 rounded-button bg-foreground px-3 py-1.5 text-xs font-semibold text-background disabled:opacity-40",
825
+ children: [
826
+ /* @__PURE__ */ jsx(LogIn, { className: "h-3.5 w-3.5", "aria-hidden": true }),
827
+ "Check-in"
828
+ ]
829
+ }
830
+ )
831
+ ] })
832
+ }
833
+ ) }) }, b.id);
834
+ }) })
835
+ ] })
836
+ }
837
+ ),
838
+ reviewing && /* @__PURE__ */ jsx(
839
+ CheckinDialog,
840
+ {
841
+ booking: reviewing,
842
+ statusLabel: statusLabel(reviewing.status),
843
+ requirements: requirementsFor(reviewing),
844
+ onReceive: canReceive ? (b) => {
845
+ setReviewing(null);
846
+ setCharging(b);
847
+ } : void 0,
848
+ onConfirm: reviewing.status === "waiting" || reviewing.status === "in_progress" ? void 0 : confirmCheckin,
849
+ clientRecord,
850
+ onClose: () => setReviewing(null)
851
+ }
852
+ ),
853
+ editing && /* @__PURE__ */ jsx(
854
+ AppointmentModal,
855
+ {
856
+ open: true,
857
+ mode: "edit",
858
+ bookingId: editing.id,
859
+ onClose: () => {
860
+ setEditing(null);
861
+ void fetchBookings(todayRange(), [...CHECKIN_STATUSES]);
862
+ }
863
+ }
864
+ ),
865
+ charging?.clientId && /* @__PURE__ */ jsx(
866
+ WidgetSlot,
867
+ {
868
+ zone: FINANCIAL_ZONES.clientAccount,
869
+ personId: charging.clientId,
870
+ orderId: charging.orderId,
871
+ clientName: charging.clientName,
872
+ onGenerate: async () => {
873
+ if (onReceive) await onReceive(charging);
874
+ else {
875
+ const r = await provider.chargeCheckout?.(charging.id);
876
+ if (r && !r.ok) {
877
+ setCheckoutRefusal(RECUSA_CHECKOUT[r.reason ?? ""] ?? "N\xE3o foi poss\xEDvel gerar o recebimento.");
878
+ return;
879
+ }
880
+ }
881
+ await fetchBookings(todayRange(), [...CHECKIN_STATUSES]);
882
+ await recarregarCheckouts();
883
+ },
884
+ onClose: () => {
885
+ setCharging(null);
886
+ void fetchBookings(todayRange(), [...CHECKIN_STATUSES]);
887
+ }
888
+ }
889
+ )
890
+ ] });
891
+ }
892
+ function CheckinPage({ config, provider, store, onReceive, onOpenBooking }) {
893
+ const runtime = usePluginRuntimeOptional();
894
+ const tenantId = runtime?.context.tenant?.id;
895
+ React3.useEffect(() => {
896
+ setAgendaTenantId(tenantId);
897
+ return () => setAgendaTenantId(void 0);
898
+ }, [tenantId]);
899
+ return /* @__PURE__ */ jsx(AgendaContextProvider, { config, provider, store, children: /* @__PURE__ */ jsx(CheckinView, { onReceive, onOpenBooking }) });
900
+ }
901
+
902
+ export { CheckinPage };
903
+ //# sourceMappingURL=CheckinPage-SGWK2TQZ.js.map
904
+ //# sourceMappingURL=CheckinPage-SGWK2TQZ.js.map