@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,1278 @@
1
+ import { useZoneFilled } from './chunk-VFQPXKXP.js';
2
+ import { useAgendaProvider } from './chunk-XTNSK4RJ.js';
3
+ import React from 'react';
4
+ import { Stethoscope, RotateCcw, Check, CircleDashed, UserRound, Package, Clock, Mic, ArrowLeft, FileText, Link2, Receipt, ClipboardCheck } from 'lucide-react';
5
+ import { Button, Checkbox, Input, ModalTask } from '@fayz-ai/ui';
6
+ import { formatCurrency, describeError } from '@fayz-ai/core';
7
+ import { WidgetSlot } from '@fayz-ai/admin';
8
+ import { FORMS_ZONES } from '@fayz-ai/forms/views/zones';
9
+ import { listAttendanceContracts, ensureAttendanceContracts, ContractSignPane } from '@fayz-ai/plugin-forms';
10
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
11
+
12
+ var KIND_LABEL = {
13
+ form: "Formul\xE1rio",
14
+ evaluation: "Avalia\xE7\xE3o",
15
+ products: "Produtos utilizados",
16
+ invoice_item: "Conta do atendimento",
17
+ revision_link: "V\xEDnculo da revis\xE3o",
18
+ contract: "Contrato",
19
+ session_term: "Termo de sess\xE3o"
20
+ };
21
+ var KIND_ICON = {
22
+ form: FileText,
23
+ evaluation: ClipboardCheck,
24
+ products: Package,
25
+ invoice_item: Receipt,
26
+ revision_link: Link2,
27
+ contract: FileText,
28
+ session_term: FileText
29
+ };
30
+ function requirementLabel(r) {
31
+ if (r.kind === "form") return r.refName ?? KIND_LABEL.form;
32
+ if ((r.kind === "contract" || r.kind === "session_term") && r.refName) return r.refName;
33
+ return KIND_LABEL[r.kind] ?? r.kind;
34
+ }
35
+ function contratoComoBola(c) {
36
+ return {
37
+ id: `contract:${c.id}`,
38
+ sessionId: null,
39
+ serviceId: null,
40
+ serviceName: null,
41
+ kind: c.isSessionTerm ? "session_term" : "contract",
42
+ scope: "attendance",
43
+ status: c.status === "signed" ? "resolved" : "pending",
44
+ refId: c.id,
45
+ refName: c.templateName,
46
+ blocksStart: false,
47
+ blocksFinish: c.blocksFinish,
48
+ validUntil: null
49
+ };
50
+ }
51
+ function Bola({ r, onClick }) {
52
+ const Icon = KIND_ICON[r.kind] ?? CircleDashed;
53
+ const done = r.status === "resolved";
54
+ const off = r.status === "not_applicable";
55
+ const Wrap = onClick ? "button" : "div";
56
+ return /* @__PURE__ */ jsxs(
57
+ Wrap,
58
+ {
59
+ type: onClick ? "button" : void 0,
60
+ onClick,
61
+ className: `flex w-16 shrink-0 flex-col items-center gap-1 text-center ${onClick ? "cursor-pointer" : ""}`,
62
+ children: [
63
+ /* @__PURE__ */ jsx(
64
+ "span",
65
+ {
66
+ className: `flex h-11 w-11 items-center justify-center rounded-full border ${done ? "border-success bg-success/15 text-success" : off ? "border-dashed border-border bg-muted/40 text-muted-foreground" : "border-warning bg-warning/10 text-warning-foreground"}`,
67
+ children: done ? /* @__PURE__ */ jsx(Check, { className: "h-5 w-5", "aria-hidden": true }) : /* @__PURE__ */ jsx(Icon, { className: "h-5 w-5", "aria-hidden": true })
68
+ }
69
+ ),
70
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] leading-tight text-muted-foreground", children: requirementLabel(r) })
71
+ ]
72
+ }
73
+ );
74
+ }
75
+ var RECUSA_CONTRATO = {
76
+ sem_ficha_de_profissional: "Este login n\xE3o tem ficha de profissional.",
77
+ nao_e_seu_atendimento: "Este atendimento \xE9 de outro profissional.",
78
+ atendimento_nao_esta_em_curso: "O atendimento precisa estar em curso para contratar.",
79
+ nada_marcado: "Marque pelo menos um servi\xE7o."
80
+ };
81
+ function TarefaPane({ titulo, aoVoltar, children }) {
82
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col", children: [
83
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 border-b px-5 py-3", children: [
84
+ /* @__PURE__ */ jsx(
85
+ Button,
86
+ {
87
+ variant: "bare",
88
+ size: "bare",
89
+ type: "button",
90
+ onClick: aoVoltar,
91
+ "aria-label": "Voltar para as etapas",
92
+ className: "-ml-1 rounded-button p-1 text-muted-foreground hover:bg-muted hover:text-foreground",
93
+ children: /* @__PURE__ */ jsx(ArrowLeft, { className: "h-4 w-4", "aria-hidden": true })
94
+ }
95
+ ),
96
+ /* @__PURE__ */ jsx("h2", { className: "min-w-0 truncate text-sm font-medium", children: titulo })
97
+ ] }),
98
+ children
99
+ ] });
100
+ }
101
+ function AttendanceRuler({ booking, open, onClose, onFinished, frame = "modal" }) {
102
+ const provider = useAgendaProvider();
103
+ const [versao, setVersao] = React.useState(0);
104
+ const [statusLocal, setStatusLocal] = React.useState(booking.status);
105
+ React.useEffect(() => {
106
+ setStatusLocal(booking.status);
107
+ }, [booking.id, booking.status]);
108
+ const emCurso = statusLocal === "in_progress";
109
+ const naoIniciado = statusLocal === "scheduled" || statusLocal === "confirmed" || statusLocal === "waiting";
110
+ const somenteLeitura = !emCurso;
111
+ const [eu, setEu] = React.useState(void 0);
112
+ React.useEffect(() => {
113
+ if (!naoIniciado) return;
114
+ let vivo = true;
115
+ (async () => {
116
+ const p = await provider.getCurrentPersonId?.() ?? null;
117
+ if (vivo) setEu(p);
118
+ })();
119
+ return () => {
120
+ vivo = false;
121
+ };
122
+ }, [naoIniciado, provider]);
123
+ const [iniciando, setIniciando] = React.useState(false);
124
+ const [recusaInicio, setRecusaInicio] = React.useState(null);
125
+ const [rows, setRows] = React.useState(null);
126
+ const [record, setRecord] = React.useState(null);
127
+ const [erro, setErro] = React.useState(null);
128
+ const [contratando, setContratando] = React.useState(false);
129
+ const [servicos, setServicos] = React.useState([]);
130
+ const [marcados, setMarcados] = React.useState({});
131
+ const [salvando, setSalvando] = React.useState(false);
132
+ const [confirmando, setConfirmando] = React.useState(null);
133
+ const [encerrando, setEncerrando] = React.useState(false);
134
+ const [avisoFinal, setAvisoFinal] = React.useState(null);
135
+ const [contratos, setContratos] = React.useState([]);
136
+ const [assinando, setAssinando] = React.useState(null);
137
+ const filaAssinatura = React.useRef({ ids: [], depois: null });
138
+ const [preenchendo, setPreenchendo] = React.useState(null);
139
+ const formsFillable = useZoneFilled(FORMS_ZONES.formFill);
140
+ const [avaliando, setAvaliando] = React.useState(false);
141
+ const [lancando, setLancando] = React.useState(null);
142
+ const [executando, setExecutando] = React.useState(null);
143
+ const [vinculando, setVinculando] = React.useState(null);
144
+ const [candidatos, setCandidatos] = React.useState(null);
145
+ const [buscaInsumo, setBuscaInsumo] = React.useState("");
146
+ const [insumos, setInsumos] = React.useState([]);
147
+ const [usados, setUsados] = React.useState([]);
148
+ const [busca, setBusca] = React.useState("");
149
+ const [catalogo, setCatalogo] = React.useState([]);
150
+ const [indicados, setIndicados] = React.useState([]);
151
+ React.useEffect(() => {
152
+ if (!open) return;
153
+ let vivo = true;
154
+ (async () => {
155
+ try {
156
+ if (emCurso) {
157
+ try {
158
+ await provider.refreshAttendanceRuler?.(booking.id);
159
+ } catch {
160
+ }
161
+ }
162
+ if (emCurso && booking.clientId) {
163
+ try {
164
+ await garantirContratos([]);
165
+ } catch {
166
+ }
167
+ }
168
+ const [r, reg, cs] = await Promise.all([
169
+ provider.getAttendanceRuler?.(booking.id),
170
+ provider.getAttendanceRecord?.(booking.id).catch(() => []),
171
+ listAttendanceContracts(booking.id).catch(() => [])
172
+ ]);
173
+ if (vivo) setContratos(cs);
174
+ if (vivo) {
175
+ setRows([
176
+ ...(r ?? []).filter((x) => !(x.kind === "form" && x.status === "not_applicable")),
177
+ ...cs.map(contratoComoBola)
178
+ ]);
179
+ setRecord(reg ?? []);
180
+ setErro(null);
181
+ }
182
+ } catch (e) {
183
+ if (vivo) setErro(describeError(e, "N\xE3o foi poss\xEDvel ler a r\xE9gua."));
184
+ }
185
+ })();
186
+ return () => {
187
+ vivo = false;
188
+ };
189
+ }, [open, booking.id, versao, emCurso]);
190
+ const autoLinked = React.useRef(false);
191
+ React.useEffect(() => {
192
+ const escolhida = booking.metadata?.revisionOf?.sessionId;
193
+ const pendente = rows?.find((r) => r.kind === "revision_link" && r.status === "pending" && r.sessionId);
194
+ if (!emCurso || !escolhida || !pendente?.sessionId || autoLinked.current || !provider.linkRevision) return;
195
+ autoLinked.current = true;
196
+ provider.linkRevision(pendente.sessionId, escolhida).then((r) => {
197
+ if (r.ok) setVersao((v) => v + 1);
198
+ }).catch(() => {
199
+ });
200
+ }, [rows, emCurso, booking.metadata, provider]);
201
+ async function garantirContratos(contratados, indicados2 = []) {
202
+ if (!booking.clientId) return [];
203
+ const sess = await provider.getAttendanceSessions?.(booking.id) ?? [];
204
+ const daSessao = sess.filter((x) => x.serviceId && x.status !== "executed" && x.status !== "cancelled" && x.serviceKind !== "evaluation").map((x) => ({ serviceId: x.serviceId, name: x.serviceName ?? "Servi\xE7o", quantity: 1, unitPrice: 0 }));
205
+ return ensureAttendanceContracts({
206
+ bookingId: booking.id,
207
+ personId: booking.clientId,
208
+ unitId: booking.locationId ?? null,
209
+ professionalId: booking.professionalId ?? null,
210
+ contracted: contratados,
211
+ indicated: indicados2,
212
+ sessionServices: daSessao
213
+ });
214
+ }
215
+ async function proximaAssinatura(lista) {
216
+ const fila = filaAssinatura.current;
217
+ while (fila.ids.length) {
218
+ const c = lista.find((x) => x.id === fila.ids[0] && x.status === "pending");
219
+ fila.ids.shift();
220
+ if (c) {
221
+ setAssinando(c);
222
+ return;
223
+ }
224
+ }
225
+ setAssinando(null);
226
+ const depois = fila.depois;
227
+ fila.depois = null;
228
+ if (depois) await depois();
229
+ }
230
+ async function abrirContratacao() {
231
+ const cands = await provider.getContractCandidates?.(booking.id) ?? [];
232
+ const itens = cands.map((c) => ({ id: c.serviceId, ...c }));
233
+ setServicos(itens);
234
+ setMarcados(Object.fromEntries(itens.map((i) => [i.serviceId, i.alreadyContracted ? 0 : i.quantity])));
235
+ setContratando(true);
236
+ }
237
+ async function contratar() {
238
+ const linhas = servicos.filter((sv) => sv.serviceId && (marcados[sv.serviceId] ?? 0) > 0).map((sv) => ({ serviceId: sv.serviceId, name: sv.name, quantity: marcados[sv.serviceId], unitPrice: sv.price }));
239
+ if (!linhas.length) return;
240
+ setSalvando(true);
241
+ try {
242
+ const r = await provider.contractServices?.(booking.id, linhas);
243
+ if (!r?.ok) {
244
+ setErro(RECUSA_CONTRATO[r?.reason ?? ""] ?? "N\xE3o foi poss\xEDvel contratar.");
245
+ return;
246
+ }
247
+ setContratando(false);
248
+ setVersao((v) => v + 1);
249
+ const indicados2 = servicos.filter((sv) => sv.fromIndication && sv.serviceId).map((sv) => ({ serviceId: sv.serviceId, name: sv.name, quantity: sv.quantity, unitPrice: sv.price }));
250
+ let lista = [];
251
+ try {
252
+ lista = await garantirContratos(linhas, indicados2);
253
+ } catch (e) {
254
+ setErro(e instanceof Error ? `Contratado, mas o contrato n\xE3o foi gerado: ${e.message}` : "Contratado, mas o contrato n\xE3o foi gerado.");
255
+ }
256
+ setContratos(lista);
257
+ const pendentes = lista.filter((c) => c.status === "pending").sort((a, b) => Number(a.isSessionTerm) - Number(b.isSessionTerm));
258
+ filaAssinatura.current = { ids: pendentes.map((c) => c.id), depois: abrirExecucao };
259
+ await proximaAssinatura(lista);
260
+ } finally {
261
+ setSalvando(false);
262
+ }
263
+ }
264
+ const ORDEM = ["form", "evaluation", "revision_link", "contract", "session_term", "products", "invoice_item"];
265
+ const porOrdem = (a, b) => {
266
+ const d = ORDEM.indexOf(a.kind) - ORDEM.indexOf(b.kind);
267
+ return d !== 0 ? d : a.id.localeCompare(b.id);
268
+ };
269
+ const doAtendimento = React.useMemo(() => {
270
+ const m = /* @__PURE__ */ new Map();
271
+ for (const r of (rows ?? []).filter((x) => x.scope === "attendance")) {
272
+ const k = `${r.kind}:${r.refId ?? ""}`;
273
+ const atual = m.get(k);
274
+ if (!atual) {
275
+ m.set(k, r);
276
+ continue;
277
+ }
278
+ if (atual.status === "resolved" && r.status !== "resolved") m.set(k, r);
279
+ }
280
+ return [...m.values()].sort(porOrdem);
281
+ }, [rows]);
282
+ const porServico = React.useMemo(() => {
283
+ const m = /* @__PURE__ */ new Map();
284
+ for (const r of rows ?? []) {
285
+ if (r.scope === "attendance") continue;
286
+ const k = r.sessionId ?? r.serviceId ?? "\u2014";
287
+ if (!m.has(k)) m.set(k, { nome: r.serviceName ?? "Servi\xE7o", itens: [] });
288
+ m.get(k).itens.push(r);
289
+ }
290
+ const grupos = [...m.values()];
291
+ const quantos = /* @__PURE__ */ new Map();
292
+ for (const g of grupos) quantos.set(g.nome, (quantos.get(g.nome) ?? 0) + 1);
293
+ const vistos = /* @__PURE__ */ new Map();
294
+ for (const g of grupos) {
295
+ g.itens.sort(porOrdem);
296
+ const n = quantos.get(g.nome) ?? 1;
297
+ if (n > 1) {
298
+ const i = (vistos.get(g.nome) ?? 0) + 1;
299
+ vistos.set(g.nome, i);
300
+ g.nome = `${g.nome} \xB7 ${i} de ${n}`;
301
+ }
302
+ }
303
+ return grupos;
304
+ }, [rows]);
305
+ const faltaParaTerminar = (rows ?? []).filter((r) => r.blocksFinish && r.status === "pending");
306
+ const tarefaAberta = Boolean(assinando || vinculando || executando || lancando || avaliando || confirmando || contratando || preenchendo?.refId && booking.clientId);
307
+ React.useEffect(() => {
308
+ if (!avaliando) return;
309
+ let vivo = true;
310
+ const t = setTimeout(async () => {
311
+ try {
312
+ const r = await provider.searchServices?.(busca);
313
+ if (vivo) setCatalogo(r ?? []);
314
+ } catch {
315
+ if (vivo) setCatalogo([]);
316
+ }
317
+ }, 250);
318
+ return () => {
319
+ vivo = false;
320
+ clearTimeout(t);
321
+ };
322
+ }, [avaliando, busca]);
323
+ React.useEffect(() => {
324
+ if (!lancando) return;
325
+ let vivo = true;
326
+ const t = setTimeout(async () => {
327
+ try {
328
+ const r = await provider.searchGoods?.(buscaInsumo);
329
+ if (vivo) setInsumos(r ?? []);
330
+ } catch {
331
+ if (vivo) setInsumos([]);
332
+ }
333
+ }, 250);
334
+ return () => {
335
+ vivo = false;
336
+ clearTimeout(t);
337
+ };
338
+ }, [lancando, buscaInsumo]);
339
+ async function trazerPadrao() {
340
+ if (!lancando) return;
341
+ const kit = await provider.getServiceKit?.(lancando.serviceId) ?? [];
342
+ setUsados((xs) => {
343
+ const tem = new Set(xs.map((x) => x.productId));
344
+ return [...xs, ...kit.filter((k) => !tem.has(k.productId)).map((k) => ({ productId: k.productId, name: k.name, qtd: k.quantity, unitId: k.unitId }))];
345
+ });
346
+ }
347
+ React.useEffect(() => {
348
+ if (!vinculando) return;
349
+ let vivo = true;
350
+ (async () => {
351
+ try {
352
+ const r = await provider.getRevisionCandidates?.(booking.id);
353
+ if (vivo) setCandidatos(r ?? []);
354
+ } catch {
355
+ if (vivo) setCandidatos([]);
356
+ }
357
+ })();
358
+ return () => {
359
+ vivo = false;
360
+ };
361
+ }, [vinculando, booking.id]);
362
+ async function ligarRevisao(parentSessionId) {
363
+ if (!vinculando?.sessionId) return;
364
+ setSalvando(true);
365
+ try {
366
+ const r = await provider.linkRevision?.(vinculando.sessionId, parentSessionId);
367
+ if (!r?.ok) {
368
+ setErro(r?.reason === "original_de_outro_paciente" ? "Esse procedimento \xE9 de outro paciente." : RECUSA_CONTRATO[r?.reason ?? ""] ?? "N\xE3o foi poss\xEDvel vincular a revis\xE3o.");
369
+ return;
370
+ }
371
+ setVinculando(null);
372
+ setCandidatos(null);
373
+ setVersao((v) => v + 1);
374
+ } finally {
375
+ setSalvando(false);
376
+ }
377
+ }
378
+ async function abrirExecucao() {
379
+ const sess = await provider.getAttendanceSessions?.(booking.id) ?? [];
380
+ const linhas = sess.filter((x) => x.serviceId).map((x) => ({ serviceId: x.serviceId, name: x.serviceName ?? "Servi\xE7o", feito: x.status === "executed" }));
381
+ if (linhas.length) setExecutando(linhas);
382
+ }
383
+ async function salvarExecucao() {
384
+ if (!executando) return;
385
+ setSalvando(true);
386
+ try {
387
+ const r = await provider.executeServices?.(
388
+ booking.id,
389
+ executando.filter((x) => x.feito).map((x) => x.serviceId)
390
+ );
391
+ if (!r?.ok) {
392
+ setErro(RECUSA_CONTRATO[r?.reason ?? ""] ?? "N\xE3o foi poss\xEDvel registrar a execu\xE7\xE3o.");
393
+ return;
394
+ }
395
+ setExecutando(null);
396
+ setVersao((v) => v + 1);
397
+ } finally {
398
+ setSalvando(false);
399
+ }
400
+ }
401
+ async function concluirProdutos() {
402
+ if (!lancando) return;
403
+ setSalvando(true);
404
+ try {
405
+ const r = await provider.recordUsedProducts?.(
406
+ booking.id,
407
+ usados.map((u) => ({ serviceId: lancando.serviceId, productId: u.productId, quantity: u.qtd, unitId: u.unitId }))
408
+ );
409
+ if (!r?.ok) {
410
+ setErro(RECUSA_CONTRATO[r?.reason ?? ""] ?? "N\xE3o foi poss\xEDvel lan\xE7ar os produtos.");
411
+ return;
412
+ }
413
+ setLancando(null);
414
+ setUsados([]);
415
+ setVersao((v) => v + 1);
416
+ } finally {
417
+ setSalvando(false);
418
+ }
419
+ }
420
+ const [atalhosPrepago, setAtalhosPrepago] = React.useState([]);
421
+ React.useEffect(() => {
422
+ if (!preenchendo || !booking.clientId || !provider.getPrepaidItems) {
423
+ setAtalhosPrepago([]);
424
+ return;
425
+ }
426
+ let vivo = true;
427
+ provider.getPrepaidItems(booking.clientId).then((r) => {
428
+ if (!vivo) return;
429
+ const vistos = /* @__PURE__ */ new Set();
430
+ setAtalhosPrepago(r.open.filter((o) => !vistos.has(o.serviceId) && vistos.add(o.serviceId)).map((o) => ({ serviceId: o.serviceId, name: o.serviceName, unitPrice: 0, hint: "j\xE1 pago" })));
431
+ }).catch(() => {
432
+ if (vivo) setAtalhosPrepago([]);
433
+ });
434
+ return () => {
435
+ vivo = false;
436
+ };
437
+ }, [preenchendo, booking.clientId, provider]);
438
+ async function salvouFormulario(r) {
439
+ setVersao((v) => v + 1);
440
+ if (!r.recommendations.length || !provider.completeEvaluation) return;
441
+ const res = await provider.completeEvaluation(
442
+ booking.id,
443
+ r.recommendations.map((l) => ({ serviceId: l.serviceId, quantity: l.quantity }))
444
+ );
445
+ if (!res?.ok) {
446
+ setErro(RECUSA_CONTRATO[res?.reason ?? ""] ?? "A avalia\xE7\xE3o foi salva, mas as indica\xE7\xF5es n\xE3o foram registradas.");
447
+ return;
448
+ }
449
+ setPreenchendo(null);
450
+ setVersao((v) => v + 1);
451
+ await abrirContratacao();
452
+ }
453
+ async function concluirAvaliacao() {
454
+ setSalvando(true);
455
+ try {
456
+ const r = await provider.completeEvaluation?.(
457
+ booking.id,
458
+ indicados.map((i) => ({ serviceId: i.id, quantity: i.qtd }))
459
+ );
460
+ if (!r?.ok) {
461
+ setErro(RECUSA_CONTRATO[r?.reason ?? ""] ?? "N\xE3o foi poss\xEDvel concluir a avalia\xE7\xE3o.");
462
+ return;
463
+ }
464
+ setAvaliando(false);
465
+ setIndicados([]);
466
+ setVersao((v) => v + 1);
467
+ } finally {
468
+ setSalvando(false);
469
+ }
470
+ }
471
+ function pedirGravacao() {
472
+ window.dispatchEvent(new CustomEvent("scribe:start", {
473
+ detail: {
474
+ appointmentId: booking.id,
475
+ subjectId: booking.clientId ?? void 0,
476
+ subjectName: booking.clientName ?? void 0
477
+ }
478
+ }));
479
+ }
480
+ async function finalizar(force) {
481
+ setEncerrando(true);
482
+ try {
483
+ const r = await provider.finishAttendance?.(booking.id, force);
484
+ if (r?.reason === "servicos_nao_executados") {
485
+ setConfirmando(r.itens ?? []);
486
+ return;
487
+ }
488
+ if (!r?.ok) {
489
+ const MOTIVO = {
490
+ exigencia_bloqueia: "Falta uma etapa que impede encerrar este atendimento.",
491
+ atendimento_nao_esta_em_curso: "Este atendimento j\xE1 foi encerrado.",
492
+ nao_e_seu_atendimento: "Este atendimento \xE9 de outro profissional.",
493
+ sem_ficha_de_profissional: "Seu login n\xE3o est\xE1 ligado a uma ficha de profissional.",
494
+ not_found: "Este agendamento n\xE3o existe mais."
495
+ };
496
+ setAvisoFinal(MOTIVO[r?.reason ?? ""] ?? "N\xE3o foi poss\xEDvel encerrar o atendimento.");
497
+ return;
498
+ }
499
+ setConfirmando(null);
500
+ onFinished?.();
501
+ onClose();
502
+ } finally {
503
+ setEncerrando(false);
504
+ }
505
+ }
506
+ const corpo = /* @__PURE__ */ jsx(Fragment, { children: tarefaAberta ? /* @__PURE__ */ jsxs(Fragment, { children: [
507
+ vinculando && /* @__PURE__ */ jsxs(TarefaPane, { titulo: /* @__PURE__ */ jsx(Fragment, { children: "O que esta revis\xE3o revisa?" }), aoVoltar: () => {
508
+ setVinculando(null);
509
+ setCandidatos(null);
510
+ }, children: [
511
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto px-5 py-3", children: candidatos === null ? /* @__PURE__ */ jsx("div", { className: "space-y-2", "aria-busy": "true", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx("div", { className: "h-10 animate-pulse rounded-lg bg-muted" }, i)) }) : candidatos.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-6 text-center text-xs text-muted-foreground", children: "Este paciente ainda n\xE3o tem procedimento executado para revisar." }) : /* @__PURE__ */ jsx("ul", { className: "divide-y rounded-lg border", children: candidatos.map((c) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
512
+ Button,
513
+ {
514
+ variant: "bare",
515
+ size: "bare",
516
+ type: "button",
517
+ disabled: salvando,
518
+ onClick: () => ligarRevisao(c.sessionId),
519
+ className: "font-normal justify-start rounded-none whitespace-normal flex w-full items-center gap-3 px-3 py-2 text-left hover:bg-muted disabled:opacity-40",
520
+ children: [
521
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
522
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm", children: c.serviceName ?? "Servi\xE7o" }),
523
+ /* @__PURE__ */ jsxs("p", { className: "text-[11px] text-muted-foreground", children: [
524
+ c.executedOn ? new Date(c.executedOn).toLocaleDateString("pt-BR") : "sem data",
525
+ c.daysSince != null ? ` \xB7 h\xE1 ${c.daysSince} dia${c.daysSince === 1 ? "" : "s"}` : ""
526
+ ] })
527
+ ] }),
528
+ /* @__PURE__ */ jsx("span", { className: `shrink-0 rounded-full px-2 py-0.5 text-[10px] font-medium ${c.withinDeadline === null ? "bg-muted text-muted-foreground" : c.withinDeadline ? "bg-success/15 text-success" : "bg-warning/15 text-warning-foreground"}`, children: c.withinDeadline === null ? "sem prazo" : c.withinDeadline ? `no prazo (${c.deadlineDays}d)` : `fora do prazo (${c.deadlineDays}d)` })
529
+ ]
530
+ }
531
+ ) }, c.sessionId)) }) }),
532
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end border-t px-5 py-2.5 pb-[calc(0.625rem+env(safe-area-inset-bottom))] sm:pb-2.5 shrink-0", children: /* @__PURE__ */ jsx(
533
+ Button,
534
+ {
535
+ variant: "bare",
536
+ size: "bare",
537
+ type: "button",
538
+ onClick: () => {
539
+ setVinculando(null);
540
+ setCandidatos(null);
541
+ },
542
+ className: "rounded-button px-2 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground",
543
+ children: "Agora n\xE3o"
544
+ }
545
+ ) })
546
+ ] }),
547
+ executando && /* @__PURE__ */ jsxs(TarefaPane, { titulo: /* @__PURE__ */ jsx(Fragment, { children: "Executar neste atendimento" }), aoVoltar: () => setExecutando(null), children: [
548
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-5 py-3", children: [
549
+ /* @__PURE__ */ jsx("p", { className: "pb-2 text-[11px] text-muted-foreground", children: "Marque os servi\xE7os que ser\xE3o executados agora. Os demais ficam contratados para execu\xE7\xE3o em outro atendimento." }),
550
+ /* @__PURE__ */ jsx("ul", { className: "divide-y rounded-lg border", children: executando.map((x) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-3 px-3 py-2", children: [
551
+ /* @__PURE__ */ jsx(
552
+ Checkbox,
553
+ {
554
+ checked: x.feito,
555
+ onChange: (feito) => setExecutando((xs) => (xs ?? []).map((y) => y.serviceId === x.serviceId ? { ...y, feito } : y)),
556
+ "aria-label": x.name
557
+ }
558
+ ),
559
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-sm", children: x.name }),
560
+ /* @__PURE__ */ jsx("span", { className: `shrink-0 text-[10px] ${x.feito ? "text-success" : "text-muted-foreground"}`, children: x.feito ? "hoje" : "fica para outra data" })
561
+ ] }, x.serviceId)) })
562
+ ] }),
563
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2 border-t px-5 py-2.5 pb-[calc(0.625rem+env(safe-area-inset-bottom))] sm:pb-2.5 shrink-0", children: [
564
+ /* @__PURE__ */ jsx(
565
+ Button,
566
+ {
567
+ variant: "bare",
568
+ size: "bare",
569
+ type: "button",
570
+ onClick: () => setExecutando(null),
571
+ className: "rounded-button px-2 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground",
572
+ children: "Agora n\xE3o"
573
+ }
574
+ ),
575
+ /* @__PURE__ */ jsx(
576
+ Button,
577
+ {
578
+ variant: "bare",
579
+ size: "bare",
580
+ type: "button",
581
+ onClick: salvarExecucao,
582
+ disabled: salvando,
583
+ className: "rounded-button bg-foreground px-3 py-1.5 text-xs font-semibold text-background disabled:opacity-40",
584
+ children: salvando ? "Registrando\u2026" : "Confirmar"
585
+ }
586
+ )
587
+ ] })
588
+ ] }),
589
+ lancando && /* @__PURE__ */ jsxs(TarefaPane, { titulo: /* @__PURE__ */ jsxs(Fragment, { children: [
590
+ "Produtos utilizados \u2014 ",
591
+ lancando.serviceName
592
+ ] }), aoVoltar: () => setLancando(null), children: [
593
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-5 py-3 space-y-3", children: [
594
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
595
+ /* @__PURE__ */ jsx("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx(
596
+ Input,
597
+ {
598
+ value: buscaInsumo,
599
+ onChange: (e) => setBuscaInsumo(e.target.value),
600
+ placeholder: "Buscar produto\u2026",
601
+ className: "h-auto px-2.5 py-1.5"
602
+ }
603
+ ) }),
604
+ /* @__PURE__ */ jsx(
605
+ Button,
606
+ {
607
+ variant: "bare",
608
+ size: "bare",
609
+ type: "button",
610
+ onClick: trazerPadrao,
611
+ className: "shrink-0 rounded-button border border-input px-2.5 py-1.5 text-xs font-medium hover:bg-muted",
612
+ children: "Padr\xE3o"
613
+ }
614
+ )
615
+ ] }),
616
+ usados.length > 0 && /* @__PURE__ */ jsx("ul", { className: "divide-y rounded-lg border", children: usados.map((u) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2 px-3 py-2", children: [
617
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-sm", children: u.name }),
618
+ /* @__PURE__ */ jsx("div", { className: "w-16 shrink-0", children: /* @__PURE__ */ jsx(
619
+ Input,
620
+ {
621
+ type: "number",
622
+ min: 0,
623
+ step: "0.01",
624
+ value: u.qtd,
625
+ onChange: (e) => setUsados((xs) => xs.map((x) => x.productId === u.productId ? { ...x, qtd: Math.max(0, Number(e.target.value) || 0) } : x)),
626
+ className: "h-auto px-1.5 py-1 text-center text-xs",
627
+ "aria-label": `Quantidade de ${u.name}`
628
+ }
629
+ ) }),
630
+ /* @__PURE__ */ jsx(
631
+ Button,
632
+ {
633
+ variant: "bare",
634
+ size: "bare",
635
+ type: "button",
636
+ onClick: () => setUsados((xs) => xs.filter((x) => x.productId !== u.productId)),
637
+ className: "font-normal shrink-0 text-xs text-destructive hover:underline",
638
+ children: "Tirar"
639
+ }
640
+ )
641
+ ] }, u.productId)) }),
642
+ /* @__PURE__ */ jsxs("ul", { className: "max-h-48 space-y-0.5 overflow-y-auto", children: [
643
+ insumos.filter((i) => !usados.some((u) => u.productId === i.id)).map((i) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
644
+ Button,
645
+ {
646
+ variant: "bare",
647
+ size: "bare",
648
+ type: "button",
649
+ onClick: () => setUsados((xs) => [...xs, { productId: i.id, name: i.name, qtd: 1, unitId: null }]),
650
+ className: "font-normal block justify-start w-full truncate rounded-lg px-2 py-1.5 text-left text-sm hover:bg-muted",
651
+ children: i.name
652
+ }
653
+ ) }, i.id)),
654
+ insumos.length === 0 && /* @__PURE__ */ jsx("li", { className: "px-2 py-3 text-center text-xs text-muted-foreground", children: buscaInsumo ? "Nenhum produto com esse nome." : "Nenhum produto cadastrado ainda." })
655
+ ] }),
656
+ /* @__PURE__ */ jsx("p", { className: "rounded-lg bg-warning/10 px-3 py-2 text-[11px] text-warning-foreground", children: "\u26A0 Baixar no estoque \u2014 o que for lan\xE7ado aqui vai para a fila da Auditoria de Uso. Nada \xE9 baixado agora." })
657
+ ] }),
658
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-t px-5 py-2.5 pb-[calc(0.625rem+env(safe-area-inset-bottom))] sm:pb-2.5 shrink-0", children: [
659
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: usados.length === 0 ? "Concluir sem lan\xE7ar nada \xE9 v\xE1lido." : `${usados.length} produto${usados.length > 1 ? "s" : ""}` }),
660
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
661
+ /* @__PURE__ */ jsx(
662
+ Button,
663
+ {
664
+ variant: "bare",
665
+ size: "bare",
666
+ type: "button",
667
+ onClick: () => setLancando(null),
668
+ className: "rounded-button px-2 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground",
669
+ children: "Agora n\xE3o"
670
+ }
671
+ ),
672
+ /* @__PURE__ */ jsx(
673
+ Button,
674
+ {
675
+ variant: "bare",
676
+ size: "bare",
677
+ type: "button",
678
+ onClick: concluirProdutos,
679
+ disabled: salvando,
680
+ className: "rounded-button bg-foreground px-3 py-1.5 text-xs font-semibold text-background disabled:opacity-40",
681
+ children: salvando ? "Lan\xE7ando\u2026" : "Concluir"
682
+ }
683
+ )
684
+ ] })
685
+ ] })
686
+ ] }),
687
+ avaliando && /* @__PURE__ */ jsxs(TarefaPane, { titulo: /* @__PURE__ */ jsx(Fragment, { children: "Avalia\xE7\xE3o \u2014 servi\xE7os indicados" }), aoVoltar: () => setAvaliando(false), children: [
688
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-5 py-3 space-y-3", children: [
689
+ indicados.length > 0 && /* @__PURE__ */ jsx("ul", { className: "divide-y rounded-lg border", children: indicados.map((i) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2 px-3 py-2", children: [
690
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-sm", children: i.name }),
691
+ /* @__PURE__ */ jsx("div", { className: "w-12 shrink-0", children: /* @__PURE__ */ jsx(
692
+ Input,
693
+ {
694
+ type: "number",
695
+ min: 1,
696
+ value: i.qtd,
697
+ onChange: (e) => setIndicados((xs) => xs.map((x) => x.id === i.id ? { ...x, qtd: Math.max(1, Math.floor(Number(e.target.value) || 1)) } : x)),
698
+ className: "h-auto px-1.5 py-1 text-center text-xs",
699
+ "aria-label": `Quantidade de ${i.name}`
700
+ }
701
+ ) }),
702
+ /* @__PURE__ */ jsx(
703
+ Button,
704
+ {
705
+ variant: "bare",
706
+ size: "bare",
707
+ type: "button",
708
+ onClick: () => setIndicados((xs) => xs.filter((x) => x.id !== i.id)),
709
+ className: "font-normal shrink-0 text-xs text-destructive hover:underline",
710
+ children: "Tirar"
711
+ }
712
+ )
713
+ ] }, i.id)) }),
714
+ /* @__PURE__ */ jsx(
715
+ Input,
716
+ {
717
+ value: busca,
718
+ onChange: (e) => setBusca(e.target.value),
719
+ placeholder: "Buscar servi\xE7o para indicar\u2026",
720
+ className: "h-auto px-2.5 py-1.5"
721
+ }
722
+ ),
723
+ /* @__PURE__ */ jsxs("ul", { className: "max-h-56 space-y-0.5 overflow-y-auto", children: [
724
+ catalogo.filter((c) => !indicados.some((i) => i.id === c.id)).map((c) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
725
+ Button,
726
+ {
727
+ variant: "bare",
728
+ size: "bare",
729
+ type: "button",
730
+ onClick: () => setIndicados((xs) => [...xs, { ...c, qtd: 1 }]),
731
+ className: "font-normal justify-start whitespace-normal flex w-full items-center gap-2 rounded-lg px-2 py-1.5 text-left hover:bg-muted",
732
+ children: [
733
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-sm", children: c.name }),
734
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", children: formatCurrency(c.price, "BRL") })
735
+ ]
736
+ }
737
+ ) }, c.id)),
738
+ catalogo.length === 0 && /* @__PURE__ */ jsx("li", { className: "px-2 py-3 text-center text-xs text-muted-foreground", children: busca ? "Nenhum servi\xE7o com esse nome." : "Carregando o cat\xE1logo\u2026" })
739
+ ] })
740
+ ] }),
741
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-t px-5 py-2.5 pb-[calc(0.625rem+env(safe-area-inset-bottom))] sm:pb-2.5 shrink-0", children: [
742
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: indicados.length === 0 ? "Concluir sem indicar nada \xE9 v\xE1lido." : `${indicados.length} indicado${indicados.length > 1 ? "s" : ""}` }),
743
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
744
+ /* @__PURE__ */ jsx(
745
+ Button,
746
+ {
747
+ variant: "bare",
748
+ size: "bare",
749
+ type: "button",
750
+ onClick: () => setAvaliando(false),
751
+ className: "rounded-button px-2 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground",
752
+ children: "Agora n\xE3o"
753
+ }
754
+ ),
755
+ /* @__PURE__ */ jsx(
756
+ Button,
757
+ {
758
+ variant: "bare",
759
+ size: "bare",
760
+ type: "button",
761
+ onClick: concluirAvaliacao,
762
+ disabled: salvando,
763
+ className: "rounded-button bg-foreground px-3 py-1.5 text-xs font-semibold text-background disabled:opacity-40",
764
+ children: salvando ? "Concluindo\u2026" : "Concluir avalia\xE7\xE3o"
765
+ }
766
+ )
767
+ ] })
768
+ ] })
769
+ ] }),
770
+ assinando && /* @__PURE__ */ jsx(TarefaPane, { titulo: /* @__PURE__ */ jsx(Fragment, { children: assinando.templateName }), aoVoltar: () => {
771
+ filaAssinatura.current = { ids: [], depois: null };
772
+ setAssinando(null);
773
+ }, children: /* @__PURE__ */ jsx(
774
+ ContractSignPane,
775
+ {
776
+ contract: assinando,
777
+ defaultSignerName: booking.clientName,
778
+ onSigned: async () => {
779
+ setVersao((v) => v + 1);
780
+ const lista = await listAttendanceContracts(booking.id).catch(() => contratos);
781
+ setContratos(lista);
782
+ await proximaAssinatura(lista);
783
+ },
784
+ onClose: () => {
785
+ filaAssinatura.current = { ids: [], depois: null };
786
+ setAssinando(null);
787
+ }
788
+ },
789
+ assinando.id
790
+ ) }),
791
+ preenchendo?.refId && booking.clientId && /* @__PURE__ */ jsx(
792
+ TarefaPane,
793
+ {
794
+ titulo: /* @__PURE__ */ jsx(Fragment, { children: preenchendo.refName ?? "Formul\xE1rio" }),
795
+ aoVoltar: () => setPreenchendo(null),
796
+ children: /* @__PURE__ */ jsx(
797
+ WidgetSlot,
798
+ {
799
+ zone: FORMS_ZONES.formFill,
800
+ frame: "inline",
801
+ templateId: preenchendo.refId,
802
+ personId: booking.clientId,
803
+ personName: booking.clientName,
804
+ subject: { type: "appointment", id: booking.id },
805
+ productId: preenchendo.scope === "service" ? preenchendo.serviceId : null,
806
+ professionalId: booking.professionalId ?? null,
807
+ recommendationShortcuts: atalhosPrepago,
808
+ onSaved: salvouFormulario,
809
+ onClose: () => setPreenchendo(null)
810
+ }
811
+ )
812
+ }
813
+ ),
814
+ confirmando && /* @__PURE__ */ jsxs(TarefaPane, { titulo: /* @__PURE__ */ jsx(Fragment, { children: "Finalizar atendimento?" }), aoVoltar: () => setConfirmando(null), children: [
815
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-5 py-3 space-y-2", children: [
816
+ confirmando.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
817
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-warning-foreground", children: confirmando.length === 1 ? "1 servi\xE7o ainda n\xE3o foi executado:" : `${confirmando.length} servi\xE7os ainda n\xE3o foram executados:` }),
818
+ /* @__PURE__ */ jsx("ul", { className: "list-disc pl-4 text-xs text-muted-foreground", children: confirmando.map((n) => /* @__PURE__ */ jsx("li", { children: n }, n)) }),
819
+ /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground", children: "Finalizar agora registra estes servi\xE7os como executados. Se n\xE3o foram realizados, cancele os itens na conta do cliente antes de encerrar." })
820
+ ] }),
821
+ /* @__PURE__ */ jsxs("p", { className: "text-[11px] text-muted-foreground", children: [
822
+ "Isso encerra o atendimento de ",
823
+ booking.clientName ?? "cliente",
824
+ " e avisa a recep\xE7\xE3o para receber. Voc\xEA poder\xE1 consult\xE1-lo no hist\xF3rico."
825
+ ] })
826
+ ] }),
827
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-t px-5 py-2.5 pb-[calc(0.625rem+env(safe-area-inset-bottom))] sm:pb-2.5 shrink-0", children: [
828
+ /* @__PURE__ */ jsx(
829
+ Button,
830
+ {
831
+ variant: "bare",
832
+ size: "bare",
833
+ type: "button",
834
+ onClick: () => setConfirmando(null),
835
+ className: "rounded-button px-2 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground",
836
+ children: "Cancelar"
837
+ }
838
+ ),
839
+ /* @__PURE__ */ jsx(
840
+ Button,
841
+ {
842
+ variant: "bare",
843
+ size: "bare",
844
+ type: "button",
845
+ onClick: () => finalizar(true),
846
+ disabled: encerrando,
847
+ className: "rounded-button bg-foreground px-3 py-1.5 text-xs font-semibold text-background disabled:opacity-40",
848
+ children: encerrando ? "Encerrando\u2026" : "Finalizar assim mesmo"
849
+ }
850
+ )
851
+ ] })
852
+ ] }),
853
+ contratando && /* @__PURE__ */ jsxs(TarefaPane, { titulo: /* @__PURE__ */ jsx(Fragment, { children: "O que o cliente deseja contratar?" }), aoVoltar: () => setContratando(false), children: [
854
+ /* @__PURE__ */ jsx("div", { className: "flex-1 overflow-y-auto px-5 py-3", children: servicos.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-6 text-center text-xs text-muted-foreground", children: "Este atendimento n\xE3o tem servi\xE7os lan\xE7ados." }) : /* @__PURE__ */ jsx("ul", { className: "divide-y", children: servicos.map((sv) => {
855
+ const id = sv.serviceId;
856
+ const qtd = marcados[id] ?? 0;
857
+ return /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-3 py-2.5", children: [
858
+ /* @__PURE__ */ jsx(
859
+ Checkbox,
860
+ {
861
+ checked: qtd > 0,
862
+ disabled: !id,
863
+ onChange: (on) => id && setMarcados((m) => ({ ...m, [id]: on ? Math.max(1, qtd) : 0 })),
864
+ "aria-label": sv.name
865
+ }
866
+ ),
867
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
868
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm", children: sv.name }),
869
+ /* @__PURE__ */ jsxs("p", { className: "text-[11px] text-muted-foreground", children: [
870
+ [
871
+ sv.fromInterest ? "Interesse do cliente" : null,
872
+ sv.fromIndication ? "Indicado pelo profissional" : null
873
+ ].filter(Boolean).join(" \xB7 "),
874
+ sv.alreadyContracted ? " \xB7 j\xE1 na conta" : "",
875
+ sv.quantity > 1 ? ` \xB7 ${sv.quantity} indicados` : ""
876
+ ] })
877
+ ] }),
878
+ /* @__PURE__ */ jsx("div", { className: "w-12 shrink-0", children: /* @__PURE__ */ jsx(
879
+ Input,
880
+ {
881
+ type: "number",
882
+ min: 0,
883
+ max: sv.quantity,
884
+ value: qtd,
885
+ disabled: !id || qtd === 0,
886
+ onChange: (e) => {
887
+ if (!id) return;
888
+ const n = Math.floor(Number(e.target.value) || 0);
889
+ setMarcados((m) => ({ ...m, [id]: Math.max(0, Math.min(sv.quantity, n)) }));
890
+ },
891
+ className: "h-auto px-1.5 py-1 text-center text-xs disabled:opacity-40",
892
+ "aria-label": `Quantidade de ${sv.name}`
893
+ }
894
+ ) }),
895
+ /* @__PURE__ */ jsx("span", { className: "w-20 shrink-0 text-right text-xs tabular-nums", children: formatCurrency(sv.price, "BRL") })
896
+ ] }, sv.id);
897
+ }) }) }),
898
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2 border-t px-5 py-2.5 pb-[calc(0.625rem+env(safe-area-inset-bottom))] sm:pb-2.5 shrink-0", children: [
899
+ /* @__PURE__ */ jsx(
900
+ Button,
901
+ {
902
+ variant: "bare",
903
+ size: "bare",
904
+ type: "button",
905
+ onClick: () => setContratando(false),
906
+ className: "rounded-button px-2 py-1.5 text-xs font-medium text-muted-foreground hover:text-foreground",
907
+ children: "Agora n\xE3o"
908
+ }
909
+ ),
910
+ /* @__PURE__ */ jsx(
911
+ Button,
912
+ {
913
+ variant: "bare",
914
+ size: "bare",
915
+ type: "button",
916
+ onClick: contratar,
917
+ disabled: salvando || !Object.values(marcados).some((q) => q > 0),
918
+ className: "rounded-button bg-foreground px-3 py-1.5 text-xs font-semibold text-background disabled:opacity-40",
919
+ children: salvando ? "Contratando\u2026" : (() => {
920
+ const escolhidos = servicos.filter((sv) => sv.serviceId && (marcados[sv.serviceId] ?? 0) > 0);
921
+ const total = escolhidos.reduce((t, sv) => t + sv.price * (marcados[sv.serviceId] ?? 0), 0);
922
+ return `Contratar (${escolhidos.length}) \xB7 ${formatCurrency(total, "BRL")}`;
923
+ })()
924
+ }
925
+ )
926
+ ] })
927
+ ] })
928
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
929
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 overflow-y-auto px-5 py-4 space-y-5", children: [
930
+ erro && /* @__PURE__ */ jsx("p", { className: "rounded-lg bg-destructive/10 px-3 py-2 text-xs text-destructive", children: erro }),
931
+ avisoFinal && /* @__PURE__ */ jsx("p", { className: "rounded-lg bg-warning/10 px-3 py-2 text-xs text-warning-foreground", children: avisoFinal }),
932
+ rows === null ? (
933
+ /* Skeleton shaped like WHAT IS COMING — and what is coming
934
+ follows from a state we already know before the query: in
935
+ progress brings the step circles; closed brings the record,
936
+ because 99.4% of migrated history carries no requirement at
937
+ all. Drawing circles and then swapping them for a text card
938
+ is the screen jumping under someone who started reading. */
939
+ emCurso ? /* @__PURE__ */ jsx("div", { className: "flex gap-3", "aria-busy": "true", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsxs("div", { className: "flex w-16 flex-col items-center gap-1", children: [
940
+ /* @__PURE__ */ jsx("div", { className: "h-11 w-11 animate-pulse rounded-full bg-muted" }),
941
+ /* @__PURE__ */ jsx("div", { className: "h-2 w-12 animate-pulse rounded bg-muted/70" })
942
+ ] }, i)) }) : /* @__PURE__ */ jsxs("div", { className: "space-y-2", "aria-busy": "true", children: [
943
+ /* @__PURE__ */ jsx("div", { className: "h-2.5 w-32 animate-pulse rounded bg-muted/70" }),
944
+ [0, 1].map((i) => /* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-xl border px-3 py-2.5", children: [
945
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
946
+ /* @__PURE__ */ jsx("div", { className: "h-3.5 w-40 animate-pulse rounded bg-muted" }),
947
+ /* @__PURE__ */ jsx("div", { className: "h-3 w-16 animate-pulse rounded bg-muted/70" })
948
+ ] }),
949
+ /* @__PURE__ */ jsx("div", { className: "h-2.5 w-52 animate-pulse rounded bg-muted/60" })
950
+ ] }, i))
951
+ ] })
952
+ ) : rows.length === 0 && !record?.length ? (
953
+ /* Régua vazia é resposta legítima e precisa DIZER por quê: sem
954
+ modelo de formulário e sem ficha técnica cadastrados, não há o
955
+ que exigir. Uma tela em branco faria procurar defeito. */
956
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Este atendimento n\xE3o tem exig\xEAncias cadastradas. Formul\xE1rios e fichas t\xE9cnicas dos servi\xE7os \xE9 que dizem o que cada procedimento pede." })
957
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
958
+ doAtendimento.length > 0 && /* @__PURE__ */ jsxs("section", { className: "space-y-2", children: [
959
+ /* @__PURE__ */ jsx("h3", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: "Etapas do atendimento" }),
960
+ /* @__PURE__ */ jsx("div", { className: "flex gap-3 overflow-x-auto pb-1", children: doAtendimento.map((r) => /* @__PURE__ */ jsx(
961
+ Bola,
962
+ {
963
+ r,
964
+ onClick: formsFillable && r.kind === "form" && r.refId && r.status === "pending" && booking.clientId ? () => setPreenchendo(r) : (r.kind === "contract" || r.kind === "session_term") && r.refId ? () => {
965
+ filaAssinatura.current = { ids: [], depois: null };
966
+ setAssinando(contratos.find((c) => c.id === r.refId) ?? null);
967
+ } : void 0
968
+ },
969
+ r.id
970
+ )) })
971
+ ] }),
972
+ porServico.map((s) => {
973
+ const feitas = s.itens.filter((i) => i.status === "resolved").length;
974
+ const validas = s.itens.filter((i) => i.status !== "not_applicable").length;
975
+ const completo = feitas === validas && validas > 0;
976
+ return /* @__PURE__ */ jsxs("section", { className: "space-y-2", children: [
977
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
978
+ /* @__PURE__ */ jsx("h3", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: s.nome }),
979
+ /* @__PURE__ */ jsxs("span", { className: `shrink-0 rounded-full px-2 py-0.5 text-[10px] font-medium ${completo ? "bg-success/15 text-success" : "bg-warning/15 text-warning-foreground"}`, children: [
980
+ completo ? "\u2713 " : "",
981
+ feitas,
982
+ "/",
983
+ validas
984
+ ] })
985
+ ] }),
986
+ /* @__PURE__ */ jsx("div", { className: "flex gap-3 overflow-x-auto pb-1", children: s.itens.map((r) => /* @__PURE__ */ jsx(
987
+ Bola,
988
+ {
989
+ r,
990
+ onClick: (
991
+ // Formulário pendente preenche em QUALQUER estado — antes de
992
+ // começar (a recepção) e depois de encerrar (o tardio do V1).
993
+ formsFillable && r.kind === "form" && r.refId && r.status === "pending" && booking.clientId ? () => setPreenchendo(r) : somenteLeitura ? void 0 : r.kind === "revision_link" && r.status === "pending" ? () => setVinculando(r) : r.kind === "products" && r.status === "pending" ? () => {
994
+ setUsados([]);
995
+ setLancando({ serviceId: r.serviceId ?? "", serviceName: s.nome });
996
+ } : r.kind === "evaluation" && r.status === "pending" ? () => setAvaliando(true) : r.kind === "invoice_item" && r.status === "pending" ? abrirContratacao : void 0
997
+ )
998
+ },
999
+ r.id
1000
+ )) })
1001
+ ] }, s.nome);
1002
+ }),
1003
+ !!record?.length && /* @__PURE__ */ jsxs("section", { className: "space-y-2", children: [
1004
+ /* @__PURE__ */ jsx("h3", { className: "text-[11px] font-semibold uppercase tracking-wide text-muted-foreground", children: emCurso ? "O que j\xE1 foi registrado" : "Registro do atendimento" }),
1005
+ /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: record.map((s) => /* @__PURE__ */ jsxs("li", { className: "rounded-xl border px-3 py-2.5", children: [
1006
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-baseline gap-x-2 gap-y-0.5", children: [
1007
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-sm font-medium", children: s.serviceName ?? "Servi\xE7o n\xE3o informado" }),
1008
+ s.price != null && /* @__PURE__ */ jsx("span", { className: "shrink-0 text-xs tabular-nums text-muted-foreground", children: formatCurrency(s.price, "BRL") })
1009
+ ] }),
1010
+ s.hadPerformer && /* @__PURE__ */ jsxs("p", { className: "mt-0.5 flex items-center gap-1.5 text-[11px] text-muted-foreground", children: [
1011
+ /* @__PURE__ */ jsx(UserRound, { className: "h-3 w-3 shrink-0", "aria-hidden": true }),
1012
+ s.performedByName ? `Executado por ${s.performedByName}` : "Executado por profissional de outra unidade"
1013
+ ] }),
1014
+ s.products.length > 0 && /* @__PURE__ */ jsxs("div", { className: "mt-2", children: [
1015
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: "Produtos utilizados" }),
1016
+ /* @__PURE__ */ jsx("ul", { className: "mt-1 space-y-0.5", children: s.products.map((p) => /* @__PURE__ */ jsxs("li", { className: "flex flex-wrap items-baseline gap-x-2 text-xs", children: [
1017
+ /* @__PURE__ */ jsx(Package, { className: "h-3 w-3 shrink-0 text-muted-foreground", "aria-hidden": true }),
1018
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: p.name }),
1019
+ /* @__PURE__ */ jsxs("span", { className: "shrink-0 tabular-nums", children: [
1020
+ p.quantity ?? "\u2014",
1021
+ p.unit ? ` ${p.unit}` : ""
1022
+ ] }),
1023
+ p.standard != null && p.standard !== p.quantity && /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-[10px] text-muted-foreground", children: [
1024
+ "padr\xE3o ",
1025
+ p.standard
1026
+ ] }),
1027
+ !p.audited && /* @__PURE__ */ jsx("span", { className: "shrink-0 rounded-full bg-warning/15 px-1.5 text-[10px] text-warning-foreground", children: "a conferir" })
1028
+ ] }, p.id)) })
1029
+ ] }),
1030
+ s.notes && /* @__PURE__ */ jsxs("div", { className: "mt-2", children: [
1031
+ /* @__PURE__ */ jsx("p", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: "Evolu\xE7\xE3o cl\xEDnica" }),
1032
+ /* @__PURE__ */ jsx("p", { className: "mt-0.5 whitespace-pre-wrap text-xs leading-relaxed", children: s.notes })
1033
+ ] })
1034
+ ] }, s.sessionId)) })
1035
+ ] })
1036
+ ] })
1037
+ ] }),
1038
+ /* @__PURE__ */ jsx("div", { className: "border-t fayz-glass-divider px-5 py-2.5 pb-[calc(0.625rem+env(safe-area-inset-bottom))] sm:pb-2.5 shrink-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
1039
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5 text-xs text-muted-foreground", children: [
1040
+ /* @__PURE__ */ jsx(Clock, { className: "h-3 w-3", "aria-hidden": true }),
1041
+ emCurso ? "Em atendimento" : naoIniciado ? "Ainda n\xE3o come\xE7ou" : "Atendimento encerrado"
1042
+ ] }),
1043
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
1044
+ naoIniciado && !!provider.startAttendance && eu !== void 0 && (() => {
1045
+ const deOutro = !!booking.professionalId && !!eu && booking.professionalId !== eu;
1046
+ const semFicha = eu === null;
1047
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
1048
+ (recusaInicio || semFicha || deOutro) && /* @__PURE__ */ jsx("span", { className: "truncate text-[11px] text-muted-foreground", children: recusaInicio ?? (semFicha ? "Atender exige um login ligado a uma ficha de profissional." : "Este atendimento \xE9 de outro profissional.") }),
1049
+ /* @__PURE__ */ jsx(
1050
+ Button,
1051
+ {
1052
+ variant: "bare",
1053
+ size: "bare",
1054
+ type: "button",
1055
+ disabled: semFicha || deOutro || iniciando,
1056
+ onClick: async () => {
1057
+ setIniciando(true);
1058
+ setRecusaInicio(null);
1059
+ try {
1060
+ const r = await provider.startAttendance?.(booking.id);
1061
+ if (!r?.ok) {
1062
+ setRecusaInicio(r?.reason === "nao_e_seu_atendimento" ? "Este atendimento \xE9 de outro profissional." : "N\xE3o foi poss\xEDvel iniciar o atendimento.");
1063
+ return;
1064
+ }
1065
+ setStatusLocal("in_progress");
1066
+ setVersao((v) => v + 1);
1067
+ } finally {
1068
+ setIniciando(false);
1069
+ }
1070
+ },
1071
+ className: "shrink-0 rounded-button bg-success px-3 py-1.5 text-xs font-semibold text-success-foreground disabled:opacity-40",
1072
+ children: iniciando ? "Abrindo\u2026" : "Iniciar atendimento"
1073
+ }
1074
+ )
1075
+ ] });
1076
+ })(),
1077
+ !somenteLeitura && faltaParaTerminar.length > 0 && /* @__PURE__ */ jsxs("span", { className: "truncate text-[11px] text-warning-foreground", children: [
1078
+ "Falta ",
1079
+ requirementLabel(faltaParaTerminar[0]),
1080
+ faltaParaTerminar.length > 1 ? ` e mais ${faltaParaTerminar.length - 1}` : ""
1081
+ ] }),
1082
+ !somenteLeitura && /* @__PURE__ */ jsx(
1083
+ Button,
1084
+ {
1085
+ variant: "bare",
1086
+ size: "bare",
1087
+ type: "button",
1088
+ onClick: pedirGravacao,
1089
+ "aria-label": "Gravar atendimento",
1090
+ title: "Gravar atendimento",
1091
+ className: "inline-flex shrink-0 items-center justify-center rounded-button border border-input p-1.5 hover:bg-muted",
1092
+ children: /* @__PURE__ */ jsx(Mic, { className: "h-3.5 w-3.5", "aria-hidden": true })
1093
+ }
1094
+ ),
1095
+ !somenteLeitura && /* @__PURE__ */ jsx(
1096
+ Button,
1097
+ {
1098
+ variant: "bare",
1099
+ size: "bare",
1100
+ type: "button",
1101
+ onClick: abrirExecucao,
1102
+ className: "shrink-0 rounded-button border border-input px-2.5 py-1.5 text-xs font-medium hover:bg-muted",
1103
+ children: "Executar\u2026"
1104
+ }
1105
+ ),
1106
+ !somenteLeitura && /* @__PURE__ */ jsx(
1107
+ Button,
1108
+ {
1109
+ variant: "bare",
1110
+ size: "bare",
1111
+ type: "button",
1112
+ onClick: () => finalizar(false),
1113
+ disabled: encerrando || faltaParaTerminar.length > 0,
1114
+ className: "shrink-0 rounded-button bg-foreground px-3 py-1.5 text-xs font-semibold text-background disabled:opacity-40",
1115
+ children: encerrando ? "Encerrando\u2026" : "Finalizar"
1116
+ }
1117
+ )
1118
+ ] })
1119
+ ] }) })
1120
+ ] }) });
1121
+ if (frame === "page") {
1122
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col overflow-hidden rounded-2xl border bg-card", children: [
1123
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-between gap-3 border-b px-5 py-3", children: /* @__PURE__ */ jsxs("span", { className: "truncate text-xs text-muted-foreground", children: [
1124
+ new Date(booking.startsAt).toLocaleString("pt-BR", {
1125
+ weekday: "short",
1126
+ day: "2-digit",
1127
+ month: "2-digit",
1128
+ hour: "2-digit",
1129
+ minute: "2-digit"
1130
+ }),
1131
+ booking.professionalName ? ` \xB7 ${booking.professionalName}` : ""
1132
+ ] }) }),
1133
+ corpo
1134
+ ] });
1135
+ }
1136
+ const tituloServicos = (booking.services ?? []).map((s) => s.name).join(", ");
1137
+ return /* @__PURE__ */ jsx(
1138
+ ModalTask,
1139
+ {
1140
+ open,
1141
+ title: `${tituloServicos || "Atendimento"} \xB7 ${new Date(booking.startsAt).toLocaleDateString("pt-BR", { day: "2-digit", month: "2-digit" })}`,
1142
+ hint: /* @__PURE__ */ jsxs(Fragment, { children: [
1143
+ booking.clientName ?? "Sem cliente",
1144
+ booking.professionalName ? ` \xB7 ${booking.professionalName}` : ""
1145
+ ] }),
1146
+ onClose,
1147
+ width: "max-w-lg",
1148
+ children: corpo
1149
+ }
1150
+ );
1151
+ }
1152
+ var STATUS_LABEL = {
1153
+ executed: "Executado",
1154
+ invoiced: "Faturado",
1155
+ in_service: "Em atendimento",
1156
+ scheduled: "Agendado",
1157
+ contracted: "Contratado",
1158
+ quoted: "Or\xE7ado",
1159
+ pending: "A executar"
1160
+ };
1161
+ function PersonAttendancesWidget({ personId }) {
1162
+ const provider = useAgendaProvider();
1163
+ const [linhas, setLinhas] = React.useState(null);
1164
+ const [aberto, setAberto] = React.useState(null);
1165
+ const [emCurso, setEmCurso] = React.useState(null);
1166
+ const recarregar = React.useCallback(async () => {
1167
+ try {
1168
+ const r = await provider.getPersonAttendances?.(personId);
1169
+ setLinhas(r ?? []);
1170
+ } catch {
1171
+ setLinhas([]);
1172
+ }
1173
+ }, [personId, provider]);
1174
+ React.useEffect(() => {
1175
+ void recarregar();
1176
+ }, [recarregar]);
1177
+ React.useEffect(() => {
1178
+ let vivo = true;
1179
+ (async () => {
1180
+ const eu = await provider.getCurrentPersonId?.() ?? null;
1181
+ if (!vivo || !eu) return;
1182
+ const d = /* @__PURE__ */ new Date();
1183
+ const iso = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
1184
+ try {
1185
+ const bs = await provider.getBookings({
1186
+ dateRange: { start: `${iso}T00:00:00`, end: `${iso}T23:59:59` },
1187
+ professionalIds: [eu],
1188
+ statuses: ["in_progress"]
1189
+ });
1190
+ if (vivo) setEmCurso(bs.find((b) => b.clientId === personId) ?? null);
1191
+ } catch {
1192
+ if (vivo) setEmCurso(null);
1193
+ }
1194
+ })();
1195
+ return () => {
1196
+ vivo = false;
1197
+ };
1198
+ }, [personId, provider]);
1199
+ if (linhas === null) {
1200
+ return /* @__PURE__ */ jsx("div", { className: "space-y-2", "aria-busy": "true", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx("div", { className: "h-14 animate-pulse rounded-xl bg-muted/50" }, i)) });
1201
+ }
1202
+ if (!linhas.length) {
1203
+ return (
1204
+ // "No attendance" and "no attendance HERE" are different facts. Sessions
1205
+ // are unit-scoped, so a patient treated at another branch reads as never
1206
+ // treated — and someone would open a brand-new record for her.
1207
+ /* @__PURE__ */ jsx("p", { className: "py-6 text-center text-xs text-muted-foreground", children: "Nenhum atendimento desta pessoa nesta unidade." })
1208
+ );
1209
+ }
1210
+ const historico = /* @__PURE__ */ jsxs(Fragment, { children: [
1211
+ /* @__PURE__ */ jsx("ul", { className: "space-y-2", children: linhas.map((l) => {
1212
+ const completo = l.totais > 0 && l.feitas === l.totais;
1213
+ return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
1214
+ Button,
1215
+ {
1216
+ variant: "bare",
1217
+ size: "bare",
1218
+ type: "button",
1219
+ onClick: async () => {
1220
+ const b = await provider.getBookingById(l.bookingId);
1221
+ if (b) setAberto(b);
1222
+ },
1223
+ className: "font-normal justify-start whitespace-normal flex w-full items-center gap-3 rounded-xl border border-border bg-card px-3 py-2 text-left hover:bg-muted/40",
1224
+ children: [
1225
+ /* @__PURE__ */ jsx(Stethoscope, { className: "h-4 w-4 shrink-0 text-muted-foreground", "aria-hidden": true }),
1226
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
1227
+ /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-1.5 truncate text-sm font-medium", children: [
1228
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 truncate", children: l.servicos || "Atendimento" }),
1229
+ l.isRevision && /* @__PURE__ */ jsx(RotateCcw, { className: "h-3 w-3 shrink-0 text-muted-foreground", "aria-hidden": true })
1230
+ ] }),
1231
+ /* @__PURE__ */ jsxs("p", { className: "truncate text-[11px] text-muted-foreground", children: [
1232
+ l.quando ? new Date(l.quando).toLocaleDateString("pt-BR") : "sem data",
1233
+ l.profissional ? ` \xB7 ${l.profissional}` : "",
1234
+ STATUS_LABEL[l.status] ? ` \xB7 ${STATUS_LABEL[l.status]}` : "",
1235
+ l.total != null ? ` \xB7 ${formatCurrency(l.total, "BRL")}` : ""
1236
+ ] })
1237
+ ] }),
1238
+ l.totais > 0 && /* @__PURE__ */ jsxs("span", { className: `inline-flex shrink-0 items-center gap-1 rounded-full px-2 py-0.5 text-[10px] font-medium ${completo ? "bg-success/15 text-success" : "bg-warning/15 text-warning-foreground"}`, children: [
1239
+ completo ? /* @__PURE__ */ jsx(Check, { className: "h-3 w-3", "aria-hidden": true }) : /* @__PURE__ */ jsx(CircleDashed, { className: "h-3 w-3", "aria-hidden": true }),
1240
+ l.feitas,
1241
+ "/",
1242
+ l.totais
1243
+ ] })
1244
+ ]
1245
+ }
1246
+ ) }, l.bookingId);
1247
+ }) }),
1248
+ linhas.some((l) => l.truncated) && /* @__PURE__ */ jsx("p", { className: "pt-2 text-center text-[11px] text-muted-foreground", children: "Mostrando os atendimentos mais recentes \u2014 este prontu\xE1rio tem mais do que cabe aqui." }),
1249
+ aberto && /* @__PURE__ */ jsx(AttendanceRuler, { booking: aberto, open: true, onClose: () => setAberto(null) })
1250
+ ] });
1251
+ if (!emCurso) return historico;
1252
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
1253
+ /* @__PURE__ */ jsx(
1254
+ AttendanceRuler,
1255
+ {
1256
+ booking: emCurso,
1257
+ open: true,
1258
+ frame: "page",
1259
+ onClose: () => {
1260
+ setEmCurso(null);
1261
+ void recarregar();
1262
+ },
1263
+ onFinished: () => {
1264
+ setEmCurso(null);
1265
+ void recarregar();
1266
+ }
1267
+ }
1268
+ ),
1269
+ /* @__PURE__ */ jsxs("div", { children: [
1270
+ /* @__PURE__ */ jsx("p", { className: "mb-2 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Atendimentos anteriores" }),
1271
+ historico
1272
+ ] })
1273
+ ] });
1274
+ }
1275
+
1276
+ export { PersonAttendancesWidget };
1277
+ //# sourceMappingURL=PersonAttendancesWidget-3NTWBGCU.js.map
1278
+ //# sourceMappingURL=PersonAttendancesWidget-3NTWBGCU.js.map