@fayz-ai/plugin-agenda 0.27.10 → 0.27.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AgendaGeneralSettings-YBI43RB6.js +96 -0
- package/dist/AgendaGeneralSettings-YBI43RB6.js.map +1 -0
- package/dist/AgendaMessageTemplates-46ZLTFLM.js +31 -0
- package/dist/AgendaMessageTemplates-46ZLTFLM.js.map +1 -0
- package/dist/AgendaPage-43OXQDZE.js +3174 -0
- package/dist/AgendaPage-43OXQDZE.js.map +1 -0
- package/dist/CheckinPage-SGWK2TQZ.js +904 -0
- package/dist/CheckinPage-SGWK2TQZ.js.map +1 -0
- package/dist/ConfirmationsView-DZJSYBXA.js +361 -0
- package/dist/ConfirmationsView-DZJSYBXA.js.map +1 -0
- package/dist/MyClientsTodayWidget-ZGTXJJKN.js +127 -0
- package/dist/MyClientsTodayWidget-ZGTXJJKN.js.map +1 -0
- package/dist/PersonAttendanceBlock-PHZVHFQ4.js +105 -0
- package/dist/PersonAttendanceBlock-PHZVHFQ4.js.map +1 -0
- package/dist/PersonAttendancesWidget-3NTWBGCU.js +1278 -0
- package/dist/PersonAttendancesWidget-3NTWBGCU.js.map +1 -0
- package/dist/PersonServicesWidget-4RJGKX3K.js +80 -0
- package/dist/PersonServicesWidget-4RJGKX3K.js.map +1 -0
- package/dist/chunk-37INOUNW.js +20 -0
- package/dist/chunk-37INOUNW.js.map +1 -0
- package/dist/chunk-A2VD5DVN.js +12 -0
- package/dist/chunk-A2VD5DVN.js.map +1 -0
- package/dist/chunk-HGJI2JR6.js +169 -0
- package/dist/chunk-HGJI2JR6.js.map +1 -0
- package/dist/chunk-VFQPXKXP.js +11 -0
- package/dist/chunk-VFQPXKXP.js.map +1 -0
- package/dist/chunk-WNANVT4F.js +2386 -0
- package/dist/chunk-WNANVT4F.js.map +1 -0
- package/dist/chunk-X7PKHXT6.js +18 -0
- package/dist/chunk-X7PKHXT6.js.map +1 -0
- package/dist/chunk-XTNSK4RJ.js +12 -0
- package/dist/chunk-XTNSK4RJ.js.map +1 -0
- package/dist/components/PersonAttendanceBlock.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2530 -19553
- package/dist/index.js.map +1 -1
- package/dist/migrations/integrations-google-calendar.browser.js +15 -0
- package/dist/migrations/integrations-google-calendar.browser.js.map +1 -0
- package/dist/migrations/integrations-google-calendar.js +1992 -0
- package/dist/migrations/integrations-google-calendar.js.map +1 -0
- package/dist/migrations/main.browser.js +29 -0
- package/dist/migrations/main.browser.js.map +1 -0
- package/dist/migrations/main.js +6440 -0
- package/dist/migrations/main.js.map +1 -0
- package/package.json +13 -7
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/checkin-required.ts","../src/components/CheckinDialog.tsx","../src/components/PaymentStatusBadge.tsx","../src/components/CheckinScoreboard.tsx","../src/views/CheckinView.tsx","../src/views/CheckinPage.tsx"],"names":["formatCurrency","React","jsx","SharedPaymentStatusBadge","jsxs","CircleDollarSign","Button","useTranslation","Fragment","LogIn","CheckCircle2","WidgetSlot"],"mappings":"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","file":"CheckinPage-SGWK2TQZ.js","sourcesContent":["import type { FieldDef } from '@fayz-ai/core'\n\n/**\n * What the client record still owes before check-in, by the house's own rule (Configurações →\n * campos obrigatórios). V1 blocks the check-in on it (FAY-1613: CPF); the field rules already\n * existed in V2 and only the forms honoured them.\n *\n * Only fields the person row can answer are judged: a key with no column here (an address kept\n * in its own table) is skipped, because blocking on what we cannot read would invent a gap.\n */\nexport function missingRequiredFields(\n fields: ReadonlyArray<Pick<FieldDef, 'key' | 'label' | 'required'>>,\n row: Record<string, unknown> | null | undefined,\n skip: ReadonlyArray<string> = ['name'],\n): Array<{ key: string; label: string }> {\n if (!row) return []\n const out: Array<{ key: string; label: string }> = []\n for (const f of fields) {\n if (!f.required || skip.includes(f.key)) continue\n const column = f.key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`)\n const has = Object.prototype.hasOwnProperty.call(row, column) || Object.prototype.hasOwnProperty.call(row, f.key)\n if (!has) continue\n const v = row[column] ?? row[f.key]\n if (v == null || (typeof v === 'string' && v.trim() === '')) {\n out.push({ key: f.key, label: typeof f.label === 'string' ? f.label : f.key })\n }\n }\n return out\n}\n","import React from 'react'\nimport { CircleDollarSign, Clock, User } from 'lucide-react'\nimport { FORMS_ZONES, type PersonChecklistItem } from '@fayz-ai/forms/views/zones'\nimport { RightRailPage, WidgetSlot, CrudRecordModal } from '@fayz-ai/admin'\nimport type { EntityDef, FieldDef } from '@fayz-ai/core'\nimport { useZoneFilled } from '../hooks/useZoneFilled'\nimport type { CalendarBooking } from '../types'\nimport { Button } from '@fayz-ai/ui'\nimport { formatCurrency } from '@fayz-ai/core'\n\n// ---------------------------------------------------------------------------\n// Conferência de check-in.\n//\n// Receber alguém não é mudar um status. No V1 o clique em \"Check-in\" NÃO grava:\n// ele abre esta conferência, e o único PUT sai do botão do fim. Entre um e outro\n// a recepção resolve o que estaria faltando — cadastro, formulário, cobrança —\n// enquanto a pessoa ainda está na frente dela. Depois que ela senta, ninguém\n// mais volta atrás.\n//\n// Três blocos, na ordem em que a recepção olha: quem é o atendimento, se o\n// cadastro está de pé, e se o dinheiro está resolvido.\n//\n// E vai na COLUNA DA DIREITA da casca — a mesma que o assistente ocupa —, não\n// num modal nem numa gaveta própria. A fila continua visível ao lado enquanto se\n// confere uma pessoa, que é como a recepção trabalha: atende quem está na frente\n// sem perder de vista quem espera. `RightRailPage` já resolve isso e cai para\n// gaveta sozinho onde não existe rail; abrir a minha seria uma segunda gramática\n// disputando a mesma borda da tela.\n// ---------------------------------------------------------------------------\n\nexport interface CheckinRequirement {\n /** Nome do que falta — \"Anamnese\", \"Termo de consentimento\". */\n label: string\n /** Por que aparece: nunca preenchido, ou venceu. */\n detail: string\n /** Impede concluir, ou só avisa. */\n blocking?: boolean\n /** Where to fix it, e.g. open the client record. */\n action?: { label: string; onClick: () => void }\n /** A record field the reception can answer here, in the client record modal. */\n field?: FieldDef\n}\n\nexport interface CheckinDialogProps {\n booking: CalendarBooking\n statusLabel: string\n /** O que o cadastro do cliente ainda deve. Vazio = \"dados obrigatórios completos\". */\n requirements?: CheckinRequirement[]\n /** Abre a cobrança deste atendimento. Ausente = o host não tem para onde levar. */\n onReceive?: (b: CalendarBooking) => void\n /**\n * Grava. É o ÚNICO que grava.\n *\n * Ausente = a mesma conferência em modo LEITURA, para quem já chegou: o V1\n * troca \"Check-in\" por \"Detalhes\" no cartão de quem está aguardando ou em\n * atendimento, e é a mesma tela — o que muda é que não há o que concluir de\n * novo. Receber continua valendo; a recepção cobra na saída.\n */\n onConfirm?: (b: CalendarBooking) => Promise<void>\n /** The client record, edited in a modal over the check-in. Without it, `field` requirements fall back to their `action`. */\n clientRecord?: {\n entityDef: EntityDef\n load: () => Promise<Record<string, any> | null>\n save: (data: Record<string, any>) => Promise<void>\n }\n onClose: () => void\n formatCurrency?: (n: number) => string\n}\n\nconst brl = (n: number) => formatCurrency(n, 'BRL')\n\nexport function CheckinDialog({\n booking, statusLabel, requirements = [], onReceive, onConfirm, clientRecord, onClose, formatCurrency = brl,\n}: CheckinDialogProps) {\n const canCompleteInline = !!clientRecord && requirements.some((r) => r.field)\n const [completing, setCompleting] = React.useState(false)\n const [busy, setBusy] = React.useState(false)\n /**\n * O formulário que a faixa mandou preencher.\n *\n * A faixa já existia e já avisava — o `onFill` dela tem escrito, no próprio\n * contrato: *\"sem isto a faixa só informa\"*. E era o que acontecia: a\n * recepção lia \"1 formulário pendente\" com a cliente na frente e não tinha\n * por onde resolver. Esta é a hora certa, e é o argumento da própria tela:\n * *\"Receba o cliente, complete o cadastro\"*.\n */\n const [preenchendo, setPreenchendo] = React.useState<string | null>(null)\n /** Quantos formulários foram gravados aqui — a faixa relê a cada um. */\n const [preenchidos, setPreenchidos] = React.useState(0)\n /** O que a checagem do paciente ainda deve — vem da própria lista. */\n const [pendencias, setPendencias] = React.useState<string[]>([])\n const blocking = requirements.filter((r) => r.blocking)\n const checklistFilled = useZoneFilled(FORMS_ZONES.personChecklist)\n const registrationChecks = React.useMemo<PersonChecklistItem[]>(() => requirements.length === 0\n ? [{ id: 'cadastro', label: 'Dados obrigatórios', state: 'done', detail: 'completos' }]\n : requirements.map((r) => ({\n id: `cadastro:${r.label}`,\n label: r.label,\n state: r.blocking ? 'late' : 'todo',\n detail: r.detail,\n action: r.field && canCompleteInline\n ? { label: 'Completar cadastro', onClick: () => setCompleting(true) }\n : r.action,\n })), [requirements, canCompleteInline])\n const paid = booking.paymentStatus === 'paid'\n const hasBill = !!booking.paymentStatus && booking.paymentStatus !== 'none'\n\n const when = new Date(booking.startsAt).toLocaleString('pt-BR', {\n weekday: 'long', day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit',\n })\n\n return (\n <RightRailPage\n title={`${onConfirm ? 'Check-in' : 'Detalhes'} — ${booking.clientName ?? 'Sem cliente'}`}\n open\n onClose={onClose}\n width=\"max-w-md\"\n >\n {/* O corpo rola; o rodapé com o \"Concluir\" fica à vista. A anamnese\n inline estica o painel além da coluna, e sem `overflow-y-auto` os\n campos de baixo ficavam fora de alcance. */}\n <div className=\"min-h-0 flex-1 space-y-3 overflow-y-auto px-4 py-3\">\n {/* 1. o atendimento */}\n <section className=\"rounded-xl border px-3 py-2\">\n <p className=\"text-[10px] font-medium uppercase text-muted-foreground\">Agendamento</p>\n <div className=\"mt-1 flex flex-wrap items-center gap-2 text-xs\">\n <span className=\"inline-flex items-center gap-1\"><Clock className=\"h-3 w-3\" />\n {when}{booking.professionalName ? ` · ${booking.professionalName}` : ''}</span>\n <span className=\"rounded-full bg-muted px-2 py-0.5 font-medium\">{statusLabel}</span>\n {booking.services?.length ? (\n <span className=\"text-muted-foreground\">\n {booking.services.map((s) => s.name).join(', ')}\n </span>\n ) : (\n <span className=\"text-muted-foreground\">Serviço não informado</span>\n )}\n </div>\n </section>\n\n {/* 2. o cadastro — e o que ele ainda deve */}\n <section className=\"rounded-xl border px-3 py-2\">\n <p className=\"text-[10px] font-medium uppercase text-muted-foreground\">Cadastro do cliente</p>\n {/* O formulário exigido é parte do CADASTRO, não do serviço: vale\n para qualquer coisa que esta pessoa venha fazer, e a recepção é\n quem tem a chance de resolver — com ela na frente, antes de\n entrar na sala. É a mesma faixa do agendamento e da ficha. */}\n {/* A faixa é remontada quando o formulário é gravado: sem isso ela\n continuava dizendo \"1 formulário pendente · nunca preenchido\"\n depois de a recepção ter preenchido, com a cliente na frente.\n Quem acabou de resolver a pendência não pode ler que ela\n continua ali. */}\n {/* UMA CHECAGEM SÓ. Os dados obrigatórios do cadastro eram uma\n linha por cima da lista de formulários — duas listas dizendo a\n mesma coisa (\"o que falta nesta pessoa antes de atender\") em\n dois desenhos diferentes, e \"Dados obrigatórios completos\" em\n verde por cima de um formulário pendente em âmbar. Agora o\n cadastro entra como item da mesma lista. */}\n <WidgetSlot\n zone={FORMS_ZONES.personChecklist}\n version={preenchidos}\n personId={booking.clientId}\n className=\"mt-2\"\n extras={registrationChecks}\n onPending={setPendencias}\n onFill={(templateId: string) => setPreenchendo(templateId)}\n />\n {!checklistFilled && (\n <ul className=\"mt-2 space-y-0.5 text-xs\">\n {registrationChecks.map((c) => (\n <li key={c.id} className={c.state === 'done' ? 'text-muted-foreground' : 'font-medium text-foreground'}>\n {c.label}{c.detail ? ` — ${c.detail}` : ''}\n </li>\n ))}\n </ul>\n )}\n {canCompleteInline && (\n <CrudRecordModal\n open={completing}\n onClose={() => setCompleting(false)}\n onSaved={() => setCompleting(false)}\n entityDef={clientRecord!.entityDef}\n load={clientRecord!.load}\n save={clientRecord!.save}\n title={`Completar cadastro — ${booking.clientName ?? 'cliente'}`}\n highlightMissing\n />\n )}\n {preenchendo && booking.clientId && (\n <WidgetSlot\n zone={FORMS_ZONES.formFill}\n frame=\"inline\"\n templateId={preenchendo}\n personId={booking.clientId}\n personName={booking.clientName}\n onSaved={() => setPreenchidos((n) => n + 1)}\n onClose={() => setPreenchendo(null)}\n />\n )}\n </section>\n\n {/* 3. o dinheiro */}\n <section className=\"rounded-xl border px-3 py-2\">\n <p className=\"text-[10px] font-medium uppercase text-muted-foreground\">Pagamento</p>\n <div className=\"mt-1 flex items-center justify-between gap-2\">\n <p className=\"text-xs text-muted-foreground\">\n {paid\n ? 'Este atendimento já está pago.'\n : hasBill\n ? `Em aberto${booking.orderTotal ? ` · ${formatCurrency(booking.orderTotal)}` : ''}.`\n : 'Nenhuma fatura vinculada a este agendamento.'}\n </p>\n {onReceive && !paid && (\n <Button variant=\"bare\" size=\"bare\"\n onClick={() => onReceive(booking)}\n 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\"\n >\n <CircleDollarSign className=\"h-3.5 w-3.5\" /> Receber\n </Button>\n )}\n </div>\n </section>\n </div>\n {onConfirm ? (\n <div className=\"flex shrink-0 items-center justify-between gap-2 border-t px-4 py-3\">\n {/* O BOTÃO FICA TRAVADO ATÉ A CHECAGEM FECHAR, e diz o que falta.\n É o gesto do V1: \"Concluir (há pendências)\" cinza, com a lista\n logo acima. Receber o cliente com pendência aberta é justamente\n o que a tela existe para impedir — e o rótulo do botão precisa\n dizer isso, porque quem clica não lê a lista de novo. */}\n <p className=\"text-[11px] text-muted-foreground\">\n {pendencias.length ? `Falta ${pendencias.join(', ')}.` : ''}\n </p>\n <Button variant=\"bare\" size=\"bare\"\n disabled={busy || blocking.length > 0 || pendencias.length > 0}\n title={pendencias.length ? `Falta ${pendencias.join(', ')}` : undefined}\n onClick={async () => { setBusy(true); try { await onConfirm(booking) } finally { setBusy(false) } }}\n 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\"\n >\n <User className=\"h-3.5 w-3.5\" /> {busy\n ? 'Concluindo…'\n : pendencias.length ? 'Concluir (há pendências)' : 'Concluir check-in'}\n </Button>\n </div>\n ) : null}\n </RightRailPage>\n )\n}\n","import React from 'react'\nimport {\n PaymentStatusBadge as SharedPaymentStatusBadge,\n type PaymentStatusBadgeProps as SharedProps,\n type PaymentDetailLine,\n} from '@fayz-ai/admin'\nimport { useTranslation } from '@fayz-ai/core'\nimport type { BookingPaymentStatus } from '../bridges/financial-bridge'\n\n// ---------------------------------------------------------------------------\n// O selo mora agora em `@fayz-ai/admin`: \"ja foi pago?\" e a mesma pergunta num\n// atendimento, num pedido do quadro e num ticket da cozinha, e a resposta sai\n// da mesma view do Financeiro. O que fica aqui e a UNICA parte que era da\n// agenda — os rotulos traduzidos por `agenda.payment.*`.\n//\n// O call site (CheckinView) nao muda: mesmo nome, mesmas props.\n// ---------------------------------------------------------------------------\n\nexport type { PaymentDetailLine }\n\nexport function PaymentStatusBadge(\n props: Omit<SharedProps, 'status' | 'labels'> & { status: BookingPaymentStatus },\n) {\n const t = useTranslation()\n const labels = React.useMemo(() => ({\n none: t('agenda.payment.title'),\n pending: t('agenda.payment.pending'),\n partial: t('agenda.payment.partial'),\n paid: t('agenda.payment.paid'),\n overdue: t('agenda.payment.overdue'),\n cancelled: t('agenda.payment.cancelled'),\n }), [t])\n\n return <SharedPaymentStatusBadge {...props} labels={labels} />\n}\n","import React from 'react'\nimport { AlarmClock, CheckCircle2, CircleDollarSign, LogIn, UserX } from 'lucide-react'\nimport type { CalendarBooking } from '../types'\nimport { Button } from '@fayz-ai/ui'\n\n// ---------------------------------------------------------------------------\n// O PLACAR DO DIA — o mesmo gesto do placar de Confirmações, outra pergunta.\n//\n// Confirmar é RISCAR DA LISTA e o placar lá conta quanto falta riscar. Receber\n// é outra coisa: a recepção não termina o dia, ela ATRAVESSA o dia, e o que ela\n// precisa ver de relance não é quanto falta — é **quem está esperando** e\n// **quem já devia ter chegado**.\n//\n// ── O número que o V1 não tem, e que é o mais importante ───────────────────\n//\n// ATRASADOS: passou da hora e a pessoa não chegou. É o único número aqui que\n// pede uma ação AGORA — é ele que faz alguém pegar o telefone antes de o\n// horário virar falta. Os outros descrevem; este cobra. Por isso é o único em\n// vermelho, e o único que some quando é zero: um \"0 atrasados\" permanente\n// ensina a ignorar a cor vermelha justamente na hora em que ela importa.\n//\n// ── Por que estes cinco ────────────────────────────────────────────────────\n//\n// chegaram a barra: o dia andando, como o \"X% confirmados\"\n// atrasados quem devia estar aqui e não está → ação\n// aguardando chegou e ainda não sentou → a fila real\n// em atendimento está na sala agora → ocupação\n// faltaram decisão já tomada, não cobra nada → fecha a conta\n//\n// \"Em aberto\" entra ao lado como VALOR e não como contagem: à recepção\n// interessa quanto se vai cobrar hoje, não em quantos pedaços.\n//\n// Clicar numa pastilha de ESTADO liga o filtro daquele estado — a pergunta\n// seguinte (\"quais são?\") se responde na mesma tela. Atrasados não é um estado\n// no banco: é um recorte do relógio, e por isso não filtra.\n// ---------------------------------------------------------------------------\n\nexport interface CheckinScoreboardProps {\n bookings: CalendarBooking[]\n /** Faltas do dia — vêm de fora porque o filtro da fila não as carrega. */\n noShow?: number\n /** Soma em aberto hoje. */\n openAmount?: number\n formatCurrency: (v: number) => string\n selectedStatuses: string[]\n onPickStatus: (status: string) => void\n}\n\nconst CHEGOU = new Set(['waiting', 'in_progress', 'completed'])\n\nexport function CheckinScoreboard({\n bookings, noShow = 0, openAmount = 0, formatCurrency, selectedStatuses, onPickStatus,\n}: CheckinScoreboardProps) {\n const agora = Date.now()\n\n const total = bookings.length\n const chegaram = bookings.filter((b) => CHEGOU.has(b.status)).length\n const aguardando = bookings.filter((b) => b.status === 'waiting').length\n const emAtendimento = bookings.filter((b) => b.status === 'in_progress').length\n // Passou da hora e ainda não chegou. `scheduled` e `confirmed` são os dois\n // estados de quem ainda é esperado.\n const atrasados = bookings.filter(\n (b) => (b.status === 'scheduled' || b.status === 'confirmed') && new Date(b.startsAt).getTime() < agora,\n ).length\n\n const pct = total > 0 ? Math.round((chegaram / total) * 100) : 0\n const tudoCerto = total > 0 && chegaram === total\n\n const pastilhas = [\n { key: 'atrasado', label: 'atrasados', n: atrasados, Icon: AlarmClock, status: null,\n tone: 'border-destructive/40 bg-destructive/10 text-destructive', hideWhenZero: true },\n { key: 'waiting', label: 'aguardando', n: aguardando, Icon: LogIn, status: 'waiting',\n tone: 'border-warning/40 bg-warning/10 text-warning-foreground', hideWhenZero: false },\n { key: 'in_progress', label: 'em atendimento', n: emAtendimento, Icon: CheckCircle2, status: 'in_progress',\n tone: 'border-success/40 bg-success/10 text-success', hideWhenZero: false },\n { key: 'no_show', label: 'faltaram', n: noShow, Icon: UserX, status: 'no_show',\n tone: 'border-border bg-muted text-muted-foreground', hideWhenZero: true },\n ].filter((p) => !(p.hideWhenZero && p.n === 0))\n\n return (\n <div className={`rounded-lg border p-3 transition-colors ${\n tudoCerto ? 'border-success/40 bg-success/5' : 'border-border bg-card'\n }`}>\n <div className=\"mb-2 flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1\">\n <span className=\"flex items-baseline gap-2\">\n <span className={`text-2xl font-semibold tabular-nums transition-colors ${\n tudoCerto ? 'text-success' : 'text-foreground'\n }`}>{pct}%</span>\n <span className=\"text-xs text-muted-foreground\">\n {chegaram} de {total} chegaram\n </span>\n </span>\n {openAmount > 0 && (\n <span className=\"inline-flex items-center gap-1.5 text-xs text-muted-foreground\">\n <CircleDollarSign className=\"h-3.5 w-3.5 shrink-0\" aria-hidden />\n <span className=\"tabular-nums font-medium text-foreground\">{formatCurrency(openAmount)}</span>\n {' '}a receber hoje\n </span>\n )}\n </div>\n\n {/* A barra enche com transição, como a de Confirmações: o mesmo gesto\n visual para a mesma ideia — o dia andando. */}\n <div className=\"h-1.5 overflow-hidden rounded-full bg-muted\" role=\"progressbar\"\n aria-valuenow={pct} aria-valuemin={0} aria-valuemax={100} aria-label=\"Quantos já chegaram hoje\">\n <div\n className={`h-full rounded-full transition-[width] duration-500 ease-out ${\n tudoCerto ? 'bg-success' : 'bg-primary'\n }`}\n style={{ width: `${pct}%` }}\n />\n </div>\n\n {pastilhas.length > 0 && (\n <div className=\"mt-2.5 flex flex-wrap items-center gap-1.5\">\n {pastilhas.map(({ key, label, n, Icon, status, tone }) => {\n const ligado = status ? selectedStatuses.includes(status) : false\n const conteudo = (\n <>\n <Icon className=\"h-3.5 w-3.5 shrink-0\" aria-hidden />\n <span className=\"tabular-nums font-semibold\">{n}</span>\n <span>{label}</span>\n </>\n )\n const base = `inline-flex items-center gap-1.5 rounded-full border px-2.5 py-1 text-xs ${tone}`\n return status ? (\n <Button variant=\"bare\" size=\"bare\"\n key={key}\n type=\"button\"\n onClick={() => onPickStatus(status)}\n aria-pressed={ligado}\n className={`font-normal ${base} transition-all hover:brightness-95 active:scale-95 ${ligado ? 'ring-1 ring-current' : ''}`}\n >\n {conteudo}\n </Button>\n ) : (\n <span key={key} className={base}>{conteudo}</span>\n )\n })}\n </div>\n )}\n </div>\n )\n}\n","import React from 'react'\nimport { LogIn, CheckCircle2, CircleDollarSign, Search, CalendarDays, Play } from 'lucide-react'\nimport { openEntity } from '@fayz-ai/admin'\nimport { Button, SubpageHeader, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@fayz-ai/ui'\nimport { usePermissionOptional, PersonLink, WidgetSlot, useFieldRules, deriveEntityKey } from '@fayz-ai/admin'\nimport { missingRequiredFields } from '../lib/checkin-required'\nimport { CheckinDialog, type CheckinRequirement } from '../components/CheckinDialog'\nimport { AppointmentModal } from '../components/AppointmentModal'\nimport { PaymentStatusBadge, type PaymentDetailLine } from '../components/PaymentStatusBadge'\nimport { StatusFilterList, ProfessionalFilterList } from '../components/FilterRail'\nimport { AgendaFilterRail, AgendaWorkspace, AdaptiveFilter } from '../components/AgendaFilterRail'\nimport { FINANCIAL_ZONES } from '@fayz-ai/financial/views/zones'\nimport { useAgendaStore, useAgendaConfig, useAgendaProvider } from '../context'\nimport { BookingCard, ClientContactButton } from '../components/BookingCard'\nimport { CheckinScoreboard } from '../components/CheckinScoreboard'\nimport { useTranslation, formatCurrency, resolveDataProvider, onAppPathChange } from '@fayz-ai/core'\nimport { useZoneFilled } from '../hooks/useZoneFilled'\nimport type { CalendarBooking } from '../types'\n\n// ---------------------------------------------------------------------------\n// Check-in: quem chega HOJE.\n//\n// A recepção não trabalha num calendário. Ela trabalha numa fila: quem já\n// chegou, quem está devendo, quem pode sentar. O calendário responde \"o que\n// acontece esta semana\"; esta tela responde \"quem está na minha frente agora\",\n// que é outra pergunta e merece outra tela.\n//\n// AS TRÊS COISAS QUE ELA MOSTRA, e por quê:\n//\n// · só HOJE. Um check-in de amanhã não existe, e uma lista que inclui\n// amanhã obriga a recepção a filtrar com os olhos a cada cliente.\n// · o estado de PAGAMENTO em cada cartão. É o que a recepção precisa saber\n// ANTES de a cliente sentar — depois do serviço, cobrar é constrangimento.\n// · o botão de receber ao lado do de check-in, não numa tela adiante.\n//\n// E O QUE ELA NÃO FAZ: pagamento NÃO bloqueia o check-in. É decisão do V1 e é\n// a certa — a cliente está na porta, e recusar a entrada de quem tem saldo em\n// aberto transforma a recepcionista em cobradora. Quem chegou, chegou; o que\n// se deve continua devido e visível.\n// ---------------------------------------------------------------------------\n\n/** As quatro que interessam a quem recebe. `completed` já saiu; `cancelled` e\n * `no_show` não vão chegar. */\nconst CHECKIN_STATUSES = ['scheduled', 'confirmed', 'waiting', 'in_progress'] as const\n\nconst STATUS_TONE: Record<string, string> = {\n scheduled: 'bg-muted text-muted-foreground',\n confirmed: 'bg-primary/10 text-primary',\n waiting: 'bg-warning/15 text-warning-foreground',\n in_progress: 'bg-success/15 text-success',\n}\n\n/**\n * A FAIXA DA ESQUERDA — o estado lido sem ler.\n *\n * A mesma faixa que Confirmações usa, pela mesma razão: dá para varrer a coluna\n * e saber em que pé está cada linha antes de ler uma palavra. O selo escrito\n * continua à direita para quem não distingue as cores.\n *\n * Atrasado não é estado no banco, mas é o que manda na fila: quem passou da\n * hora ganha a faixa vermelha por cima da cor do seu estado.\n */\nconst STATUS_FLAG: Record<string, string> = {\n scheduled: 'bg-muted-foreground/30',\n confirmed: 'bg-primary',\n waiting: 'bg-warning',\n in_progress: 'bg-success',\n}\n\n/** O que a recepção precisa ler de relance. `none` é o caso de quem ainda não\n * foi faturado — silêncio, não alarme. */\n\nfunction todayRange() {\n const start = new Date(); start.setHours(0, 0, 0, 0)\n const end = new Date(); end.setHours(23, 59, 59, 999)\n return { start: start.toISOString(), end: end.toISOString() }\n}\n\n/** Há quanto tempo ela espera. O V1 mostra isto e é o que ordena a atenção da\n * recepção: quem chegou primeiro e ainda não sentou. */\nfunction waitedFor(iso: string): string {\n const min = Math.floor((Date.now() - new Date(iso).getTime()) / 60000)\n if (min < 1) return 'agora mesmo'\n if (min < 60) return `há ${min} min`\n const h = Math.floor(min / 60)\n return `há ${h}h${min % 60 ? ` ${min % 60}min` : ''}`\n}\n\nfunction hourOf(iso: string) {\n return new Date(iso).toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' })\n}\n\nexport interface CheckinViewProps {\n /** Abre o agendamento para EDITAR. O V1 tem esta ação na linha — a recepção\n * corrige um horário ou troca o profissional sem sair da fila. O host decide\n * COMO abrir porque o formulário de agendamento é da tela de agenda. */\n onOpenBooking?: (booking: CalendarBooking) => void\n /** Para onde vai o \"Iniciar atendimento\". Sem isto, a rota própria da régua. */\n onOpenAttendance?: (bookingId: string) => void\n /** Abre o recebimento para esta reserva. O host decide COMO — modal do\n * financeiro, ficha do pedido — porque quem tem o modal é ele. */\n onReceive?: (booking: CalendarBooking) => void\n}\n\nexport function CheckinView({ onReceive, onOpenBooking, onOpenAttendance }: CheckinViewProps) {\n const t = useTranslation()\n const config = useAgendaConfig()\n const provider = useAgendaProvider()\n // E a PALAVRA vem de lá também. Enquanto a fila tinha o seu próprio\n // dicionário, \"Agendada\" aqui e \"Agendado\" na agenda eram o mesmo estado com\n // dois nomes, e trocar de aba obrigava a reaprender o vocabulário.\n const statusLabel = (v: string) => config.statuses.find((x) => x.value === v)?.label ?? v\n // Os quatro estados da fila, montados a partir da configuração da agenda —\n // mesma ordem, mesma cor, mesma palavra. Um estado que a casa não usa não\n // vira caixa de seleção aqui.\n const checkinStatuses = React.useMemo(\n () => CHECKIN_STATUSES\n .map((v) => config.statuses.find((x) => x.value === v))\n .filter((x): x is NonNullable<typeof x> => !!x),\n [config.statuses],\n )\n const bookings = useAgendaStore((s) => s.bookings)\n const loading = useAgendaStore((s) => s.bookingsLoading)\n const fetchBookings = useAgendaStore((s) => s.fetchBookings)\n const updateBookingStatus = useAgendaStore((s) => s.updateBookingStatus)\n const [term, setTerm] = React.useState('')\n const [busyId, setBusyId] = React.useState<string | null>(null)\n /**\n * QUEM SOU EU como pessoa da equipe.\n *\n * `undefined` = ainda não perguntei (não desenha botão nenhum: um botão que\n * aparece e some é pior que um que demora). `null` = login sem ficha de\n * profissional — e aí o botão aparece DESABILITADO dizendo o motivo, que é\n * exatamente o que falta no V1: lá a ficha simplesmente não desenha nada.\n */\n const [myPersonId, setMyPersonId] = React.useState<string | null | undefined>(undefined)\n const [startRefusal, setStartRefusal] = React.useState<string | null>(null)\n /** O atendimento cuja régua está aberta. */\n /** A fila do BALCÃO: quem já saiu da sala e deve uma conta. */\n const [checkouts, setCheckouts] = React.useState<CalendarBooking[]>([])\n const [conferindo, setConferindo] = React.useState<string | null>(null)\n const [checkoutRefusal, setCheckoutRefusal] = React.useState<string | null>(null)\n // O clique na linha NÃO grava: abre a conferência. O PUT sai de lá.\n // UM painel por vez. A coluna da direita tem UMA página (`RightRailPage` usa\n // um slot só), e três estados independentes conseguiam reivindicá-la juntos —\n // o título vinha de um painel e o corpo de outro, de clientes diferentes.\n // Estados separados descrevem três coisas; a coluna é uma.\n const [panel, setPanel] = React.useState<\n { kind: 'review' | 'edit' | 'charge'; booking: CalendarBooking } | null\n >(null)\n const reviewing = panel?.kind === 'review' ? panel.booking : null\n\n // The client record, to judge the house's required fields at check-in. Re-read when the window\n // regains focus: \"Completar cadastro\" opens the record, and coming back must not show stale gaps.\n const clientDef = config.clientEntityDef\n const { applyRules } = useFieldRules(clientDef ? deriveEntityKey(clientDef) : '')\n const clientFields = React.useMemo(() => (clientDef ? applyRules(clientDef.fields) : []), [clientDef, applyRules])\n const [clientRow, setClientRow] = React.useState<Record<string, unknown> | null>(null)\n const [clientRowVersion, setClientRowVersion] = React.useState(0)\n React.useEffect(() => {\n const id = reviewing?.clientId\n if (!id || !config.contactLookup?.getById) { setClientRow(null); return }\n let alive = true\n config.contactLookup.getById(id)\n .then((r: any) => { if (alive) setClientRow(r?.data ? { ...r.data, id } : null) })\n .catch(() => { if (alive) setClientRow(null) })\n return () => { alive = false }\n }, [reviewing?.clientId, config.contactLookup, clientRowVersion])\n React.useEffect(() => {\n const again = () => setClientRowVersion((n) => n + 1)\n window.addEventListener('focus', again)\n const off = onAppPathChange(again)\n return () => { window.removeEventListener('focus', again); off() }\n }, [])\n // Writes through the entity's provider, like the record screen: it maps camelCase keys and custom fields.\n const clientProvider = React.useMemo(\n () => (clientDef ? resolveDataProvider(clientDef as any) : null),\n [clientDef],\n )\n const clientRecord = clientDef && clientProvider && reviewing?.clientId\n ? {\n // The house's rules (CPF required…) are what the check-in judged; the modal must agree.\n entityDef: { ...clientDef, fields: clientFields },\n load: async () => {\n const res: any = await clientProvider.list({ filters: { id: reviewing.clientId }, pageSize: 1, countMode: 'none' } as any)\n return res?.data?.[0] ?? null\n },\n save: async (data: Record<string, any>) => {\n await clientProvider.update(reviewing.clientId!, data as any)\n setClientRowVersion((n) => n + 1)\n },\n }\n : undefined\n const editing = panel?.kind === 'edit' ? panel.booking : null\n const charging = panel?.kind === 'charge' ? panel.booking : null\n const setReviewing = (b: CalendarBooking | null) => setPanel(b ? { kind: 'review', booking: b } : null)\n const setEditing = (b: CalendarBooking | null) => setPanel(b ? { kind: 'edit', booking: b } : null)\n const setCharging = (b: CalendarBooking | null) => setPanel(b ? { kind: 'charge', booking: b } : null)\n // Cobrar acontece AQUI, com a conta do cliente à vista — não numa lista de\n // contas a receber do outro lado do app.\n // Editar o agendamento acontece SEM SAIR da fila.\n // Receber levanta a fatura, e isso é do Financeiro. Sem a permissão a RPC\n // recusa com 42501 e a rota do Financeiro responde \"acesso restrito\" — não se\n // oferece um botão cujo único desfecho é uma porta fechada.\n const can = usePermissionOptional()\n const accountMounted = useZoneFilled(FINANCIAL_ZONES.clientAccount)\n const canReceive = accountMounted && (can('financial', 'create') || can('financial.receivables', 'create'))\n\n // A fila pede os SEUS estados, não os do calendário. `getCurrentBookingQuery`\n // injeta os chips de status da agenda, e `waiting` não está entre eles — então\n // a cliente que acabou de fazer check-in sumia da tela feita para acompanhá-la.\n React.useEffect(() => {\n void fetchBookings(todayRange(), [...CHECKIN_STATUSES])\n }, [fetchBookings])\n\n // Os estados que a recepção QUER ver, e não os que o código escolheu por ela.\n // O V1 deixa isso em caixas de seleção porque o dia muda: de manhã interessa\n // quem ainda vem, no fim da tarde interessa quem faltou.\n const [shownStatuses, setShownStatuses] = React.useState<string[]>([])\n const [shownPros, setShownPros] = React.useState<string[]>([])\n\n // Os profissionais de HOJE, tirados da própria fila — a lista completa da casa\n // teria gente que não trabalha hoje, e a recepção filtraria por um nome que\n // nunca vai aparecer.\n const prosToday = React.useMemo(() => {\n const seen = new Map<string, string>()\n for (const b of bookings) if (b.professionalName) seen.set(b.professionalName, b.professionalName)\n return [...seen.keys()].sort()\n }, [bookings])\n\n const cards = React.useMemo(() => {\n const q = term.trim().toLowerCase()\n return bookings\n // Nenhum marcado = todos, como na agenda.\n .filter((b) => !shownStatuses.length || shownStatuses.includes(b.status))\n // Nenhum marcado = todos aparecem, como no V1: um filtro vazio é \"não\n // filtrei\", não \"não quero ninguém\".\n .filter((b) => !shownPros.length || (b.professionalName && shownPros.includes(b.professionalName)))\n .filter((b) => !q || (b.clientName ?? '').toLowerCase().includes(q)\n || (b.professionalName ?? '').toLowerCase().includes(q))\n // Pela hora, que é a ordem em que as pessoas chegam.\n .sort((a, b) => a.startsAt.localeCompare(b.startsAt))\n }, [bookings, term, shownStatuses, shownPros])\n\n const waiting = cards.filter((b) => b.status === 'waiting').length\n\n /**\n * O que o cadastro do cliente ainda deve.\n *\n * Os FORMULÁRIOS exigidos são outra lista, e ela vem do banco\n * (`forms_person_requirements`) direto no diálogo, pelo `PersonFormsAlert` —\n * a mesma faixa das outras duas portas onde o V1 avisa. Aqui fica só o que a\n * própria reserva sabe dizer.\n */\n function requirementsFor(b: CalendarBooking): CheckinRequirement[] {\n const out: CheckinRequirement[] = []\n // The record's required fields, by the house's rule (V1 blocks the check-in without CPF).\n if (b.clientId && b.clientId === clientRow?.id) {\n for (const m of missingRequiredFields(clientFields, clientRow)) {\n out.push({\n label: m.label, detail: 'obrigatório no cadastro', blocking: true,\n field: clientFields.find((f) => f.key === m.key),\n action: { label: 'Completar cadastro', onClick: () => openEntity(b.clientId!, 'person', 'customer') },\n })\n }\n }\n if (!b.clientName) out.push({ label: 'Cliente', detail: 'o agendamento não nomeia ninguém', blocking: true })\n // Pelo `professionalId`, não pelo nome. \"Ninguém para executar\" é uma\n // AFIRMAÇÃO, e o nome pode faltar por outro motivo: a RLS de pessoa é por\n // alcance de unidade, e um profissional de outra unidade não volta na\n // consulta. Cobrar um responsável que existe manda a recepção procurar o\n // que não está perdido.\n if (!b.professionalId) out.push({ label: 'Profissional', detail: 'ninguém para executar' })\n if (!b.services?.length) out.push({ label: 'Serviço', detail: 'nada a executar nem a cobrar' })\n return out\n }\n\n const money = (n: number) => formatCurrency(n, 'BRL')\n\n // As faltas de HOJE. A fila não as carrega — o filtro nasce com os quatro\n // estados de quem ainda vai ser atendido —, então elas vêm da mesma contagem\n // de cabeçalho que a agenda usa no cartão de perdidos.\n const [lost, setLost] = React.useState<{ cancelled: number; noShow: number }>({ cancelled: 0, noShow: 0 })\n React.useEffect(() => {\n if (!provider.getLostCounts) return\n let cancelado = false\n provider.getLostCounts(todayRange())\n .then((c) => { if (!cancelado) setLost(c) })\n // Fica no que já havia. Cair para zero afirmaria que ninguém faltou hoje\n // porque uma consulta não voltou — e o placar mostra \"faltaram\" ao lado\n // de números que carregaram.\n .catch(() => {})\n return () => { cancelado = true }\n }, [provider])\n\n /** Quanto ainda se vai cobrar hoje. Pago não entra: já é história. */\n const openAmount = React.useMemo(\n () => bookings\n .filter((b) => b.orderTotal && b.paymentStatus && b.paymentStatus !== 'paid')\n .reduce((soma, b) => soma + (b.orderTotal ?? 0), 0),\n [bookings],\n )\n\n /** As baixas já registradas, do mesmo elo que enriquece a fila. */\n async function loadPaymentLines(orderId: string): Promise<PaymentDetailLine[]> {\n const detail = await config.financialBridge?.getPaymentDetail(orderId)\n return (detail?.movements ?? [])\n .filter((m) => m.paymentDate)\n .map((m) => ({\n label: [m.paymentMethodTypeName, m.cardBrand, m.cardInstallments && m.cardInstallments > 1 ? `${m.cardInstallments}x` : null]\n .filter(Boolean).join(' · ') || 'Pagamento',\n date: m.paymentDate ?? null,\n amount: m.paidAmount,\n }))\n }\n\n React.useEffect(() => {\n let vivo = true\n // Sem cancelamento por dependência instável: `provider` troca de identidade\n // a cada render e a resposta descartada já custou uma tela vazia antes.\n ;(async () => {\n try {\n const id = await provider.getCurrentPersonId?.()\n if (vivo) setMyPersonId(id ?? null)\n } catch { if (vivo) setMyPersonId(null) }\n })()\n return () => { vivo = false }\n }, [])\n\n const recarregarCheckouts = React.useCallback(async () => {\n try { setCheckouts((await provider.getPendingCheckouts?.()) ?? []) } catch { setCheckouts([]) }\n }, [])\n\n React.useEffect(() => { void recarregarCheckouts() }, [recarregarCheckouts])\n\n /**\n * O BALCÃO ACENDE SOZINHO.\n *\n * O V1 é explícito sobre o que faz disto uma notificação e não um número que\n * envelhece: um canal escutando `UPDATE` em `appointments`, mais uma **rede\n * de segurança** repetindo a consulta caso o tempo real caia. Sem isso, a\n * recepção só descobre que alguém terminou quando resolve recarregar — e\n * quem está no balcão não recarrega, atende.\n *\n * O que NÃO veio junto: o pulso de 12 segundos e o bipe. São do rodapé do V1,\n * que no celular ele decidiu não montar; aqui a faixa já está na tela que a\n * recepção olha, e som sem pedido numa recepção cheia é ruído.\n */\n React.useEffect(() => {\n const parar = provider.subscribeToBookingChanges?.(() => {\n void recarregarCheckouts()\n void fetchBookings(todayRange(), [...CHECKIN_STATUSES])\n })\n // A rede de segurança do V1: 30 segundos, e só enquanto a aba está à vista.\n const rede = setInterval(() => {\n if (document.visibilityState === 'visible') void recarregarCheckouts()\n }, 30_000)\n // E ao VOLTAR para a aba. Sem isto, quem deixou o balcão aberto atrás de\n // outra janela volta para a lista de quando saiu — o navegador congela o\n // tempo real e o relógio da aba escondida, e a espera seria de mais meio\n // minuto justamente no instante em que a pessoa olha.\n const aoVoltar = () => { if (document.visibilityState === 'visible') void recarregarCheckouts() }\n document.addEventListener('visibilitychange', aoVoltar)\n return () => { parar?.(); clearInterval(rede); document.removeEventListener('visibilitychange', aoVoltar) }\n }, [recarregarCheckouts])\n\n /** O que cada recusa do balcão quer dizer para quem está com o celular na mão. */\n const RECUSA_CHECKOUT: Record<string, string> = {\n not_found: 'Este atendimento não existe mais.',\n sem_sessao: 'Sessão expirada. Entre de novo para conferir.',\n atendimento_nao_terminou: 'O atendimento ainda não foi encerrado — não há o que conferir.',\n }\n\n // UM CARTÃO É UM CLIENTE, ENTÃO UM CLIQUE É O CLIENTE INTEIRO.\n //\n // A lista dedupa por cliente de propósito — quem passou três vezes hoje deve\n // UMA conta — mas o clique conferia só o agendamento que por acaso sobrou no\n // Map. Com três atendimentos do mesmo cliente na fila, conferir o primeiro\n // deixava o cartão no lugar, idêntico, com o nome idêntico: da cadeira da\n // recepção isso é \"o botão não faz nada\". Eram precisos três cliques às\n // cegas, e só o terceiro parecia funcionar.\n //\n // Medido: ZZQA Cliente 01 tinha 11:00, 13:00 e 15:00 pendentes; dois já\n // constavam conferidos e o cartão continuava na tela pelo terceiro.\n async function concluirCheckout(b: CalendarBooking) {\n const chave = b.clientId ?? b.id\n const doCliente = checkouts.filter((c) => (c.clientId ?? c.id) === chave)\n setConferindo(b.id)\n setCheckoutRefusal(null)\n try {\n for (const c of doCliente) {\n const r = await provider.confirmCheckout?.(c.id)\n // `confirmCheckout` devolve {ok, reason} e a recusa vinha sendo jogada\n // fora. Recusa silenciosa e sucesso silencioso são a mesma tela.\n if (r && r.ok !== true) {\n setCheckoutRefusal(RECUSA_CHECKOUT[r.reason ?? ''] ?? 'Não foi possível concluir o checkout.')\n return\n }\n }\n await recarregarCheckouts()\n } catch (e: any) {\n // Sem isto a exceção da RPC subia como rejeição não tratada: o giro\n // parava, a linha ficava, e ninguém ficava sabendo por quê.\n setCheckoutRefusal(e?.message ?? 'Não foi possível concluir o checkout.')\n } finally { setConferindo(null) }\n }\n\n /** O que cada recusa do banco quer dizer para quem está com o celular na mão. */\n const RECUSA: Record<string, string> = {\n sem_ficha_de_profissional: 'Este login não tem ficha de profissional. Só quem atende inicia o atendimento.',\n nao_e_seu_atendimento: 'Este atendimento é de outro profissional.',\n nao_e_de_hoje: 'Só dá para iniciar um atendimento de hoje.',\n ja_encerrado: 'Este atendimento já foi encerrado.',\n not_found: 'Este agendamento não existe mais.',\n }\n\n /**\n * O ATENDIMENTO NÃO MORA AQUI.\n *\n * Esta é a tela da recepção — \"receba o cliente, complete o cadastro\". A\n * régua é do profissional, e o V1 a abre na ficha do cliente. O botão\n * continua sendo daqui, porque é daqui que se começa; o trabalho acontece\n * na tela dele.\n */\n function abrirAtendimento(b: CalendarBooking) {\n if (onOpenAttendance) { onOpenAttendance(b.id); return }\n // A FICHA DO CLIENTE, ABA ATENDIMENTOS — a casa que o V1 declara: *\"não há\n // rota /atendimento: a régua é montada SÓ dentro da ficha\"*. Cheguei a\n // criar uma tela só para o atendimento; era um lugar a mais para a mesma\n // coisa, e tirava do profissional o resto da ficha bem quando ele precisa\n // dela.\n if (b.clientId) openEntity(b.clientId, 'person', 'customer', 'attendance')\n }\n\n async function startAttendance(b: CalendarBooking) {\n setBusyId(b.id)\n setStartRefusal(null)\n try {\n const r = await provider.startAttendance?.(b.id)\n if (!r?.ok) {\n setStartRefusal(RECUSA[r?.reason ?? ''] ?? 'Não foi possível iniciar o atendimento.')\n return\n }\n await fetchBookings(todayRange(), [...CHECKIN_STATUSES])\n // Começar e ficar na fila seria pedir um segundo toque para ver o que o\n // primeiro produziu. A régua abre no mesmo gesto — agora na TELA DELA.\n //\n // E abre com o atendimento JÁ EM ANDAMENTO. `b` é a linha de antes do\n // clique, com `status: 'waiting'`, e a régua decide por ela se está em\n // leitura ou escrita: passando o objeto velho, ela abria dizendo\n // \"Atendimento encerrado\" e sem deixar preencher nada — logo depois de\n // o banco ter gravado `in_progress`.\n abrirAtendimento(b)\n } finally { setBusyId(null) }\n }\n\n async function confirmCheckin(b: CalendarBooking) {\n setBusyId(b.id)\n try {\n await updateBookingStatus(b.id, 'waiting')\n await fetchBookings(todayRange(), [...CHECKIN_STATUSES])\n setReviewing(null)\n } finally { setBusyId(null) }\n }\n\n return (\n <div className=\"space-y-4\">\n {/* A frase é do V1 e não é enfeite: ela enuncia as TRÊS coisas que a\n recepção faz, nesta ordem, e o \"complete o cadastro\" no meio é o que\n lembra que esta é a hora de arrumar a ficha — com a pessoa na frente,\n antes de ela entrar na sala. */}\n <SubpageHeader\n title=\"Check-in\"\n subtitle={waiting ? `${waiting} aguardando` : undefined}\n />\n\n {/* Duas colunas, como no V1: os filtros ficam PARADOS à esquerda e a fila\n rola ao lado. Numa barra sobre a lista, cada toque num filtro empurra a\n fila para baixo e a recepção perde a linha que estava lendo.\n O DESENHO É O DA AGENDA — mesmas caixas, mesmas iniciais, mesmo ponto\n de cor no status. Duas telas vizinhas com dois vocabulários de filtro\n fazem a pessoa reaprender a mesma coisa ao trocar de aba. */}\n {/* A MESMA coluna da agenda — ver AgendaFilterRail. Antes esta tela\n desenhava a própria: 248px contra 220px, a busca com outro raio,\n \"Agendas\" onde a vizinha diz \"Profissionais\". Trocar de aba pedia um\n reaprendizado, e cada melhoria de UX precisava ser feita duas vezes. */}\n <AgendaWorkspace\n rail={\n <AgendaFilterRail\n search={{\n value: term,\n onChange: setTerm,\n placeholder: 'Buscar cliente ou profissional',\n }}\n sections={[\n {\n id: 'professionals',\n title: t('agenda.sidebar.professionals'),\n count: shownPros.length,\n content: (\n <>\n {/* Esqueleto com a FORMA da linha: sem ele a coluna nasce\n vazia e cresce, empurrando o Status para baixo depois\n que a pessoa já mirou nele. */}\n {loading && prosToday.length === 0 && [0, 1, 2].map((i) => (\n <div key={i} className=\"flex items-center gap-2.5 px-1 py-1\">\n <div className=\"h-4 w-4 animate-pulse rounded-sm bg-muted\" />\n <div className=\"h-3.5 w-24 animate-pulse rounded bg-muted/70\" />\n </div>\n ))}\n {/* A fila de hoje quase sempre tem meia dúzia de nomes e\n eles cabem em caixinhas; num dia cheio de rede vira\n campo com busca. A mesma regra da agenda. */}\n <AdaptiveFilter\n // A fila indexa por NOME, não por id — é o que o\n // agendamento traz. O retrato entra vazio: sem id não há\n // de onde buscar a foto, e a inicial já separa os nomes.\n options={prosToday.map((name) => ({ value: name, label: name, avatarUrl: null }))}\n value={shownPros}\n onChange={setShownPros}\n allLabel={t('agenda.sidebar.allProfessionals')}\n />\n </>\n ),\n },\n {\n id: 'status',\n title: t('agenda.sidebar.status'),\n count: shownStatuses.length,\n // Aberto por padrão: com a cor ao lado de cada nome, esta lista\n // é também a LEGENDA dos selos da fila. Recolhida, o selo\n // colorido do cartão fica sem dicionário.\n defaultOpen: true,\n content: (\n <StatusFilterList\n statuses={checkinStatuses}\n selected={shownStatuses}\n onToggle={(value) => setShownStatuses((cur) =>\n cur.includes(value) ? cur.filter((x) => x !== value) : [...cur, value])}\n />\n ),\n },\n ]}\n />\n }\n >\n <div className=\"min-w-0 space-y-2\">\n\n {/* O PLACAR DO DIA, no lugar de duas linhas soltas.\n Havia aqui uma frase de instrução e, no canto do cabeçalho, um \"4 de\n 4 na fila de hoje\" — dois textos que ninguém lê duas vezes e que não\n diziam o que a recepção precisa saber de relance: quem está esperando\n e quem já devia ter chegado. O placar diz, e a instrução virou a\n legenda dele. */}\n <CheckinScoreboard\n bookings={bookings}\n noShow={lost.noShow}\n openAmount={openAmount}\n formatCurrency={money}\n selectedStatuses={shownStatuses}\n onPickStatus={(st) => setShownStatuses((cur) =>\n cur.includes(st) ? cur.filter((x) => x !== st) : [...cur, st])}\n />\n <p className=\"pb-1 pt-0.5 text-xs text-muted-foreground\">\n Receba o cliente, complete o cadastro e marque como aguardando.\n </p>\n\n {/* CHECKOUTS PENDENTES — a fila do BALCÃO, que não é a fila da porta.\n A de cima é quem ainda vai entrar; esta é quem já saiu da sala e deve\n uma conta. O V1 a desenha num painel próprio, aberto pelo chip do\n rodapé; aqui ela fica acima da fila porque, no celular, rodapé com\n contador é justamente o que o V1 decidiu NÃO montar.\n\n Um cartão por CLIENTE, não por atendimento: quem passou duas vezes\n hoje deve UMA conta. */}\n {checkouts.length > 0 && (() => {\n // O CONTADOR CONTA O QUE A LISTA MOSTRA.\n //\n // A lista é por CLIENTE — quem passou duas vezes hoje deve uma conta —,\n // e o título contava os atendimentos: dizia \"2 checkouts pendentes\"\n // sobre um cartão só. Número que não bate com o que está embaixo dele\n // faz procurar o que falta.\n const porCliente = [...new Map(checkouts.map((b) => [b.clientId ?? b.id, b])).values()]\n return (\n <section className=\"space-y-2 rounded-2xl border border-warning/40 bg-warning/5 p-3\">\n <h2 className=\"flex items-center gap-2 text-xs font-semibold text-warning-foreground\">\n <CircleDollarSign className=\"h-3.5 w-3.5\" aria-hidden />\n {porCliente.length === 1 ? '1 checkout pendente' : `${porCliente.length} checkouts pendentes`}\n </h2>\n {checkoutRefusal && (\n <p className=\"rounded-lg bg-destructive/10 px-3 py-2 text-xs text-destructive\">{checkoutRefusal}</p>\n )}\n <ul className=\"space-y-2\">\n {porCliente.map((b) => (\n <li key={b.id} className=\"flex flex-wrap items-center gap-2 rounded-xl border border-border bg-card px-3 py-2\">\n {/* NO CELULAR O NOME TEM A LINHA INTEIRA.\n Com os dois botões disputando a mesma linha, \"ZZQA Cliente\n 01\" virava \"ZZQA Clie…\" e o valor sumia no meio — e é o\n valor que diz quanto cobrar. Numa tela estreita o texto pega\n a linha toda e as ações descem; da tablet para cima os três\n cabem lado a lado. */}\n <div className=\"min-w-0 basis-full sm:basis-0 sm:flex-1\">\n <p className=\"truncate text-sm font-medium\">{b.clientName ?? 'Cliente'}</p>\n <p className=\"truncate text-[11px] text-muted-foreground\">\n {b.professionalName ?? 'Sem profissional'} · {b.startsAt.slice(11, 16)}\n {b.orderTotal != null && b.orderTotal > 0 ? ` · ${money(b.orderTotal)}` : ''}\n {/* Quantos atendimentos este cartão cobre. Sem o número, um\n cartão sobre três sessões parece um sobre uma — e é o que\n fazia o clique parecer inerte. */}\n {(() => {\n const n = checkouts.filter((c) => (c.clientId ?? c.id) === (b.clientId ?? b.id)).length\n return n > 1 ? ` · ${n} atendimentos` : ''\n })()}\n </p>\n </div>\n {/* RECEBER e CONFERIR são dois gestos. O primeiro move dinheiro\n e já mora no cartão da conta; o segundo é a recepção dizendo\n \"conferi a sessão\", e é ele que tira a linha daqui. */}\n {accountMounted && b.orderTotal != null && b.orderTotal > 0 && (\n <Button variant=\"bare\" size=\"bare\"\n type=\"button\"\n onClick={() => setCharging(b)}\n className=\"shrink-0 rounded-button border border-input px-2.5 py-1.5 text-xs font-medium hover:bg-muted\"\n >\n Ver conta\n </Button>\n )}\n {(() => {\n // CONFERIDO E AINDA DEVENDO é um estado, e ele precisa\n // aparecer. A regra do V1 é que o cartão fica na fila\n // \"enquanto houver valor em aberto OU checkout por\n // confirmar\" — então conferir uma conta aberta NÃO tira a\n // linha dali. Sem dizer isso, o gesto parece não ter\n // funcionado e a pessoa clica de novo.\n const est = (b.metadata as any)?.checkout as { conferido?: boolean; emAberto?: number } | undefined\n if (est?.conferido) {\n return (\n <span className=\"shrink-0 rounded-button bg-warning/15 px-2.5 py-1.5 text-[11px] font-medium text-warning-foreground\">\n Conferido · falta receber{est.emAberto ? ` ${money(est.emAberto)}` : ''}\n </span>\n )\n }\n return (\n <Button variant=\"bare\" size=\"bare\"\n type=\"button\"\n disabled={conferindo === b.id}\n onClick={() => concluirCheckout(b)}\n className=\"shrink-0 rounded-button bg-foreground px-3 py-1.5 text-xs font-semibold text-background disabled:opacity-40\"\n >\n {conferindo === b.id ? 'Conferindo…' : 'Concluir checkout'}\n </Button>\n )\n })()}\n </li>\n ))}\n </ul>\n </section>\n )\n })()}\n\n {startRefusal && (\n <div className=\"flex items-start gap-2 rounded-lg bg-warning/10 px-3 py-2 text-xs text-warning-foreground\">\n <span className=\"flex-1\">{startRefusal}</span>\n <Button variant=\"bare\" size=\"bare\" type=\"button\" onClick={() => setStartRefusal(null)} className=\"shrink-0 text-xs font-medium hover:underline\">\n Fechar\n </Button>\n </div>\n )}\n\n {loading && cards.length === 0 ? (\n /* Esqueleto com a FORMA da linha: a fila não salta quando os dados\n chegam, e a recepção já sabe onde olhar antes de haver o que ler. */\n <ul className=\"space-y-2\" aria-busy=\"true\" aria-label=\"Carregando a fila de hoje\">\n {[0, 1, 2, 3].map((i) => (\n <li key={i} className=\"flex items-center gap-3 rounded-2xl border border-border bg-card px-4 py-3\">\n <div className=\"h-4 w-12 animate-pulse rounded bg-muted\" />\n <div className=\"min-w-0 flex-1 space-y-1.5\">\n <div className=\"h-3.5 w-40 animate-pulse rounded bg-muted\" />\n <div className=\"h-3 w-24 animate-pulse rounded bg-muted/70\" />\n </div>\n <div className=\"h-4 w-16 animate-pulse rounded bg-muted\" />\n <div className=\"h-6 w-20 animate-pulse rounded-full bg-muted\" />\n <div className=\"h-7 w-24 animate-pulse rounded-button bg-muted\" />\n </li>\n ))}\n </ul>\n ) : cards.length === 0 ? (\n <div className=\"rounded-2xl border-2 border-dashed border-muted px-6 py-12 text-center\">\n <div className=\"mx-auto mb-3 flex h-12 w-12 items-center justify-center rounded-xl bg-muted text-muted-foreground\">\n <LogIn className=\"h-6 w-6\" />\n </div>\n <p className=\"text-sm font-medium\">Ninguém na fila</p>\n <p className=\"mt-1 text-xs text-muted-foreground\">\n {term ? 'Nenhum atendimento de hoje casa com a busca.' : 'Não há atendimentos para hoje.'}\n </p>\n </div>\n ) : (\n <ul className=\"space-y-2\">\n {cards.map((b) => {\n // Já chegou: `waiting` e `in_progress` não voltam para a fila.\n const arrived = b.status === 'waiting' || b.status === 'in_progress'\n // Passou da hora e não chegou: `scheduled` e `confirmed` são os\n // dois estados de quem ainda é esperado.\n const late = !arrived && b.status !== 'completed'\n && new Date(b.startsAt).getTime() < Date.now()\n return (\n <li key={b.id}>\n <TooltipProvider delayDuration={300}>\n <BookingCard\n booking={b}\n clientKind=\"customer\"\n busy={busyId === b.id}\n selected={panel?.booking.id === b.id}\n showMoney={false}\n late={late}\n flagClass={late ? 'bg-destructive' : (STATUS_FLAG[b.status] ?? 'bg-border')}\n badge={\n <span className={`rounded-full px-2 py-0.5 text-[10px] font-medium ${STATUS_TONE[b.status] ?? ''}`}>\n {statusLabel(b.status)}\n {/* O TEMPO NA COR DO ESTADO, não numa cor própria. Âmbar\n sobre o selo verde de \"em atendimento\" lia como alerta\n dentro de uma coisa que está indo bem — e, sobre o\n fundo esverdeado, mal se lia. Quem espera continua em\n âmbar porque ali o tempo É o alerta; quem já entrou\n herda a cor do próprio selo. */}\n {arrived && b.checkedInAt && (\n <span className=\"ml-1 opacity-70\">· {waitedFor(b.checkedInAt)}</span>\n )}\n </span>\n }\n aside={\n /* Quanto é, e em que pé está. A recepção cobra na saída e\n precisa do valor na linha; procurá-lo abrindo o\n agendamento é um clique por cliente numa tela feita para\n não ter nenhum. Sem fatura levantada mostra só o número:\n um silêncio é mais honesto que \"em aberto\". */\n b.orderTotal != null && b.orderTotal > 0 ? (\n <PaymentStatusBadge\n status={b.paymentStatus ?? 'none'}\n totalAmount={b.orderTotal}\n formatCurrency={money}\n compact\n onLoadDetails={b.orderId ? () => loadPaymentLines(b.orderId!) : undefined}\n onReceive={canReceive ? () => setCharging(b) : undefined}\n onOpenDetails={() => setCharging(b)}\n />\n ) : undefined\n }\n actions={<>\n {/* `Tooltip` do design system e não o `title` do navegador:\n o `title` demora um segundo, desenha uma caixinha do\n sistema que ignora o tema e some ao menor movimento. */}\n <Tooltip>\n <TooltipTrigger asChild>\n <Button variant=\"bare\" size=\"bare\"\n type=\"button\"\n onClick={() => (onOpenBooking ? onOpenBooking(b) : setEditing(b))}\n aria-label=\"Abrir agendamento\"\n className=\"inline-flex items-center justify-center rounded-button border border-input p-1.5 hover:bg-muted\"\n >\n <CalendarDays className=\"h-3.5 w-3.5\" aria-hidden />\n </Button>\n </TooltipTrigger>\n <TooltipContent>Abrir agendamento</TooltipContent>\n </Tooltip>\n\n {b.clientPhone && <ClientContactButton phone={b.clientPhone} compact />}\n\n {/* ESTADO QUE IMPEDE NÃO OFERECE A AÇÃO — TROCA A AÇÃO.\n Quem já chegou via um botão \"Chegou\" desabilitado: um\n controle morto que ocupa o lugar do único gesto que\n ainda faz sentido ali. O V1 troca por **Detalhes**, que\n abre a MESMA conferência sem nada a concluir — e é onde\n se confere o que falta no cadastro e se recebe.\n Atendimento concluído não mostra nem um nem outro: não\n há o que receber nem o que conferir. */}\n {/* INICIAR ATENDIMENTO — o gesto do profissional, aqui e\n não na ficha do cliente.\n\n O V1 põe este botão em Clientes → Listar → a ficha. No\n celular isso é o caminho errado: quem atende chega pela\n FILA DO DIA, já com a pessoa na frente, e procurar o\n nome numa lista de 120 mil é um desvio que só existe\n porque a tela não ofereceu o botão onde a mão está.\n\n Só para quem chegou (`waiting`/`in_progress`) e só para\n o DONO do agendamento — a mesma regra do V1. Login sem\n ficha vê o botão desabilitado com o motivo, em vez do\n silêncio que lá é a pergunta de suporte mais provável. */}\n {arrived && b.status !== 'completed' && myPersonId !== undefined\n && (myPersonId === null || b.professionalId === myPersonId) && (\n <Button variant=\"bare\" size=\"bare\"\n type=\"button\"\n disabled={myPersonId === null || busyId === b.id}\n onClick={() => (b.status === 'in_progress' ? abrirAtendimento(b) : startAttendance(b))}\n title={myPersonId === null ? RECUSA.sem_ficha_de_profissional : undefined}\n // A AÇÃO PRINCIPAL USA A COR DE AÇÃO, e o verde fica para\n // o que é verde por significado — o WhatsApp do cliente,\n // ao lado. Dois verdes competindo na mesma linha fazem os\n // dois perderem: o olho não sabe qual é o gesto e qual é\n // o atalho.\n 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\"\n >\n <Play className=\"h-3.5 w-3.5\" aria-hidden />\n {b.status === 'in_progress' ? 'Continuar' : 'Iniciar atendimento'}\n </Button>\n )}\n\n {b.status === 'completed' ? null : arrived ? (\n <Button variant=\"bare\" size=\"bare\"\n type=\"button\"\n onClick={() => setReviewing(b)}\n 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\"\n >\n <CheckCircle2 className=\"h-3.5 w-3.5\" aria-hidden />\n Detalhes\n </Button>\n ) : (\n <Button variant=\"bare\" size=\"bare\"\n type=\"button\"\n disabled={busyId === b.id}\n onClick={() => setReviewing(b)}\n 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\"\n >\n <LogIn className=\"h-3.5 w-3.5\" aria-hidden />\n Check-in\n </Button>\n )}\n </>}\n />\n </TooltipProvider>\n </li>\n )\n })}\n </ul>\n )}\n </div>\n </AgendaWorkspace>\n\n {reviewing && (\n <CheckinDialog\n booking={reviewing}\n statusLabel={statusLabel(reviewing.status)}\n requirements={requirementsFor(reviewing)}\n onReceive={canReceive ? (b) => { setReviewing(null); setCharging(b) } : undefined}\n onConfirm={reviewing.status === 'waiting' || reviewing.status === 'in_progress' ? undefined : confirmCheckin}\n clientRecord={clientRecord}\n onClose={() => setReviewing(null)}\n />\n )}\n\n {editing && (\n <AppointmentModal\n open\n mode=\"edit\"\n bookingId={editing.id}\n onClose={() => {\n setEditing(null)\n void fetchBookings(todayRange(), [...CHECKIN_STATUSES])\n }}\n />\n )}\n\n {charging?.clientId && (\n <WidgetSlot\n zone={FINANCIAL_ZONES.clientAccount}\n personId={charging.clientId}\n orderId={charging.orderId}\n clientName={charging.clientName}\n onGenerate={async () => {\n // O host sabe COMO levantar a cobrança quando ELE tem uma ponte —\n // e quando não tem, o botão ficava morto: clicar em \"Gerar\n // recebimento\" não produzia movimento nenhum, nem mensagem. Quem\n // clica confia no botão.\n //\n // A queda é para a porta única do SDK: fechar o pedido do\n // atendimento, que é o que levanta o recebível no banco.\n if (onReceive) await onReceive(charging)\n else {\n // A recusa FALA — {ok:false} engolido era um botão que \"não fazia\n // nada\" (mesma correção da aba Financeiro do agendamento).\n const r = await provider.chargeCheckout?.(charging.id)\n if (r && !r.ok) {\n setCheckoutRefusal(RECUSA_CHECKOUT[r.reason ?? ''] ?? 'Não foi possível gerar o recebimento.')\n return\n }\n }\n await fetchBookings(todayRange(), [...CHECKIN_STATUSES])\n await recarregarCheckouts()\n }}\n onClose={() => {\n setCharging(null)\n // Pagar acontece DENTRO deste painel, e a linha da fila mostra o\n // estado do pagamento: sem recarregar, a cliente que acabou de\n // pagar continua \"em aberto\" na tela de quem a atendeu.\n void fetchBookings(todayRange(), [...CHECKIN_STATUSES])\n }}\n />\n )}\n </div>\n )\n}\n","import React from 'react'\nimport { usePluginRuntimeOptional } from '@fayz-ai/core'\nimport type { StoreApi } from 'zustand/vanilla'\nimport { AgendaContextProvider } from '../context'\nimport type { ResolvedAgendaConfig } from '../config'\nimport type { AgendaDataProvider } from '../data/types'\nimport type { AgendaUIState } from '../store'\nimport { CheckinView, type CheckinViewProps } from './CheckinView'\nimport { setAgendaTenantId } from '../lib/tenant'\n\n// The same shell AgendaPage puts around the calendar: config, provider and\n// store come from the factory, not from a second wiring that could drift.\nexport function CheckinPage({ config, provider, store, onReceive, onOpenBooking }: {\n config: ResolvedAgendaConfig\n provider: AgendaDataProvider\n store: StoreApi<AgendaUIState>\n} & CheckinViewProps) {\n const runtime = usePluginRuntimeOptional()\n const tenantId = runtime?.context.tenant?.id\n\n React.useEffect(() => {\n setAgendaTenantId(tenantId)\n return () => setAgendaTenantId(undefined)\n }, [tenantId])\n\n return (\n <AgendaContextProvider config={config} provider={provider} store={store}>\n <CheckinView onReceive={onReceive} onOpenBooking={onOpenBooking} />\n </AgendaContextProvider>\n )\n}\n"]}
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import { AgendaFilterRail, AdaptiveFilter, AgendaWorkspace, BookingCard, ClientContactButton } from './chunk-HGJI2JR6.js';
|
|
2
|
+
import { useAgendaConfig, useAgendaStore } from './chunk-XTNSK4RJ.js';
|
|
3
|
+
import { useEffect, useState, useRef, useMemo } from 'react';
|
|
4
|
+
import { CheckCircle, Check, X, Loader2, ChevronDown } from 'lucide-react';
|
|
5
|
+
import { Button, FilterCheckRow, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent } from '@fayz-ai/ui';
|
|
6
|
+
import { useTranslation, formatCurrency } from '@fayz-ai/core';
|
|
7
|
+
import { usePluginPrefs } from '@fayz-ai/admin';
|
|
8
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
9
|
+
|
|
10
|
+
var HORIZONS = [
|
|
11
|
+
{ days: 1, label: "S\xF3 hoje", short: "hoje" },
|
|
12
|
+
{ days: 2, label: "Hoje e amanh\xE3", short: "2 dias" },
|
|
13
|
+
{ days: 3, label: "Pr\xF3ximos 3 dias", short: "3 dias" },
|
|
14
|
+
{ days: 7, label: "Pr\xF3ximos 7 dias", short: "7 dias" }
|
|
15
|
+
];
|
|
16
|
+
var STATE_TONE = {
|
|
17
|
+
pending: {
|
|
18
|
+
flag: "bg-warning",
|
|
19
|
+
border: "border-warning/40",
|
|
20
|
+
badge: "bg-warning/15 text-warning-foreground"
|
|
21
|
+
},
|
|
22
|
+
sent: {
|
|
23
|
+
flag: "bg-info",
|
|
24
|
+
border: "border-info/40",
|
|
25
|
+
badge: "bg-info/15 text-info"
|
|
26
|
+
},
|
|
27
|
+
confirmed: {
|
|
28
|
+
flag: "bg-success",
|
|
29
|
+
border: "border-success/40",
|
|
30
|
+
badge: "bg-success/15 text-success"
|
|
31
|
+
},
|
|
32
|
+
declined: {
|
|
33
|
+
flag: "bg-destructive",
|
|
34
|
+
border: "border-destructive/40",
|
|
35
|
+
badge: "bg-destructive/15 text-destructive"
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
var STATE_LABEL = (t) => ({
|
|
39
|
+
pending: t("agenda.confirmations.statePending"),
|
|
40
|
+
sent: t("agenda.confirmations.stateSent"),
|
|
41
|
+
confirmed: t("agenda.confirmations.stateConfirmed"),
|
|
42
|
+
declined: t("agenda.confirmations.stateDeclined")
|
|
43
|
+
});
|
|
44
|
+
function getConfirmationStatus(booking) {
|
|
45
|
+
if (booking.status === "confirmed") return "confirmed";
|
|
46
|
+
if (booking.status === "cancelled" || booking.status === "no_show") return "declined";
|
|
47
|
+
const confirmations = booking.metadata?.confirmations;
|
|
48
|
+
if (!confirmations?.length) return "pending";
|
|
49
|
+
const last = confirmations[confirmations.length - 1];
|
|
50
|
+
return last.status;
|
|
51
|
+
}
|
|
52
|
+
function Group({
|
|
53
|
+
title,
|
|
54
|
+
count,
|
|
55
|
+
defaultOpen,
|
|
56
|
+
empty,
|
|
57
|
+
pulse,
|
|
58
|
+
children
|
|
59
|
+
}) {
|
|
60
|
+
const [open, setOpen] = useState(defaultOpen ?? true);
|
|
61
|
+
return /* @__PURE__ */ jsxs("section", { children: [
|
|
62
|
+
/* @__PURE__ */ jsxs(
|
|
63
|
+
Button,
|
|
64
|
+
{
|
|
65
|
+
variant: "bare",
|
|
66
|
+
size: "bare",
|
|
67
|
+
type: "button",
|
|
68
|
+
onClick: () => setOpen((p) => !p),
|
|
69
|
+
"aria-expanded": open,
|
|
70
|
+
className: "justify-start whitespace-normal mb-2 flex w-full items-center gap-2 text-left text-xs font-medium text-muted-foreground transition-colors hover:text-foreground",
|
|
71
|
+
children: [
|
|
72
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: `h-3.5 w-3.5 shrink-0 transition-transform ${open ? "" : "-rotate-90"}` }),
|
|
73
|
+
/* @__PURE__ */ jsx("span", { children: title }),
|
|
74
|
+
/* @__PURE__ */ jsx("span", { className: `rounded-full px-1.5 py-px text-[10px] font-semibold tabular-nums transition-all duration-300 ${pulse ? "scale-125 bg-success text-white" : "scale-100 bg-muted text-muted-foreground"}`, children: count })
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
),
|
|
78
|
+
open && (count > 0 ? /* @__PURE__ */ jsx("ul", { className: "space-y-2", children }) : empty ? /* @__PURE__ */ jsx("p", { className: "px-1 pb-1 text-xs text-muted-foreground", children: empty }) : null)
|
|
79
|
+
] });
|
|
80
|
+
}
|
|
81
|
+
function ConfirmationsView() {
|
|
82
|
+
const t = useTranslation();
|
|
83
|
+
const config = useAgendaConfig();
|
|
84
|
+
const confirmations = useAgendaStore((s) => s.pendingConfirmations);
|
|
85
|
+
const loading = useAgendaStore((s) => s.confirmationsLoading);
|
|
86
|
+
const fetchConfirmations = useAgendaStore((s) => s.fetchConfirmations);
|
|
87
|
+
const updateStatus = useAgendaStore((s) => s.updateBookingStatus);
|
|
88
|
+
const prefs = usePluginPrefs("agenda", { confirmationDays: 2 });
|
|
89
|
+
const days = Number(prefs.get("confirmationDays") ?? 2) || 2;
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
void fetchConfirmations(days);
|
|
92
|
+
}, [days, fetchConfirmations]);
|
|
93
|
+
const [term, setTerm] = useState("");
|
|
94
|
+
const [shownPros, setShownPros] = useState([]);
|
|
95
|
+
const [shownStates, setShownStates] = useState([]);
|
|
96
|
+
const [busyId, setBusyId] = useState(null);
|
|
97
|
+
const [held, setHeld] = useState([]);
|
|
98
|
+
const [leavingId, setLeavingId] = useState(null);
|
|
99
|
+
const [pulse, setPulse] = useState(false);
|
|
100
|
+
const timers = useRef([]);
|
|
101
|
+
useEffect(() => () => {
|
|
102
|
+
timers.current.forEach(clearTimeout);
|
|
103
|
+
}, []);
|
|
104
|
+
const HOLD_MS = 650;
|
|
105
|
+
const SLIDE_MS = 480;
|
|
106
|
+
const decide = async (id, next) => {
|
|
107
|
+
setBusyId(id);
|
|
108
|
+
try {
|
|
109
|
+
await updateStatus(id, next);
|
|
110
|
+
setHeld((cur) => [...cur, id]);
|
|
111
|
+
timers.current.push(window.setTimeout(() => setLeavingId(id), HOLD_MS));
|
|
112
|
+
timers.current.push(window.setTimeout(() => {
|
|
113
|
+
setLeavingId(null);
|
|
114
|
+
setHeld((cur) => cur.filter((x) => x !== id));
|
|
115
|
+
setPulse(true);
|
|
116
|
+
}, HOLD_MS + SLIDE_MS));
|
|
117
|
+
timers.current.push(window.setTimeout(() => setPulse(false), HOLD_MS + SLIDE_MS + 420));
|
|
118
|
+
} catch {
|
|
119
|
+
} finally {
|
|
120
|
+
setBusyId(null);
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
const CONFIRMATION_STATES = useMemo(() => [
|
|
124
|
+
{ value: "pending", label: t("agenda.confirmations.statePending"), color: "hsl(var(--warning))" },
|
|
125
|
+
{ value: "sent", label: t("agenda.confirmations.stateSent"), color: "hsl(var(--info))" },
|
|
126
|
+
{ value: "confirmed", label: t("agenda.confirmations.stateConfirmed"), color: "hsl(var(--success))" },
|
|
127
|
+
{ value: "declined", label: t("agenda.confirmations.stateDeclined"), color: "hsl(var(--destructive))" }
|
|
128
|
+
], [t]);
|
|
129
|
+
const pros = useMemo(() => {
|
|
130
|
+
const names = /* @__PURE__ */ new Set();
|
|
131
|
+
for (const b of confirmations) if (b.professionalName) names.add(b.professionalName);
|
|
132
|
+
return [...names].sort((a, b) => a.localeCompare(b));
|
|
133
|
+
}, [confirmations]);
|
|
134
|
+
const shown = useMemo(() => {
|
|
135
|
+
const needle = term.trim().toLowerCase();
|
|
136
|
+
return confirmations.filter((b) => {
|
|
137
|
+
if (shownPros.length && !(b.professionalName && shownPros.includes(b.professionalName))) return false;
|
|
138
|
+
if (shownStates.length && !shownStates.includes(getConfirmationStatus(b))) return false;
|
|
139
|
+
if (needle) {
|
|
140
|
+
const hay = `${b.clientName ?? ""} ${b.professionalName ?? ""} ${b.clientPhone ?? ""}`.toLowerCase();
|
|
141
|
+
if (!hay.includes(needle)) return false;
|
|
142
|
+
}
|
|
143
|
+
return true;
|
|
144
|
+
});
|
|
145
|
+
}, [confirmations, shownPros, shownStates, term]);
|
|
146
|
+
const isSettled = (b) => {
|
|
147
|
+
const st = getConfirmationStatus(b);
|
|
148
|
+
return st === "confirmed" || st === "declined";
|
|
149
|
+
};
|
|
150
|
+
const pending = shown.filter((b) => !isSettled(b) || held.includes(b.id));
|
|
151
|
+
const settled = shown.filter((b) => isSettled(b) && !held.includes(b.id));
|
|
152
|
+
const confirmedCount = confirmations.filter((b) => b.status === "confirmed").length;
|
|
153
|
+
const pendingCount = confirmations.length - confirmedCount;
|
|
154
|
+
const totalCount = confirmations.length;
|
|
155
|
+
const confirmRate = totalCount > 0 ? Math.round(confirmedCount / totalCount * 100) : 0;
|
|
156
|
+
const allDone = totalCount > 0 && pendingCount === 0;
|
|
157
|
+
const rail = /* @__PURE__ */ jsx(
|
|
158
|
+
AgendaFilterRail,
|
|
159
|
+
{
|
|
160
|
+
search: { value: term, onChange: setTerm, placeholder: t("agenda.confirmations.searchPlaceholder") },
|
|
161
|
+
sections: [
|
|
162
|
+
{
|
|
163
|
+
id: "professionals",
|
|
164
|
+
title: t("agenda.sidebar.professionals"),
|
|
165
|
+
count: shownPros.length,
|
|
166
|
+
content: /* @__PURE__ */ jsx(
|
|
167
|
+
AdaptiveFilter,
|
|
168
|
+
{
|
|
169
|
+
options: pros.map((name) => ({ value: name, label: name, avatarUrl: null })),
|
|
170
|
+
value: shownPros,
|
|
171
|
+
onChange: setShownPros,
|
|
172
|
+
allLabel: t("agenda.sidebar.allProfessionals")
|
|
173
|
+
}
|
|
174
|
+
)
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
id: "horizon",
|
|
178
|
+
// O VALOR VAI NO TÍTULO. Dobrar a seção guardaria o recorte junto
|
|
179
|
+
// com a régua, e é justamente a seção fechada que faria a lista
|
|
180
|
+
// vazia mentir — "não há ninguém para confirmar" quando o que há é
|
|
181
|
+
// um alcance curto. No título ele sobrevive ao fechamento.
|
|
182
|
+
title: `Per\xEDodo \xB7 ${HORIZONS.find((h) => h.days === days)?.short ?? ""}`,
|
|
183
|
+
defaultOpen: true,
|
|
184
|
+
content: /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: HORIZONS.map((h) => /* @__PURE__ */ jsx(
|
|
185
|
+
Button,
|
|
186
|
+
{
|
|
187
|
+
variant: "bare",
|
|
188
|
+
size: "bare",
|
|
189
|
+
type: "button",
|
|
190
|
+
onClick: () => prefs.set("confirmationDays", h.days),
|
|
191
|
+
"aria-pressed": h.days === days,
|
|
192
|
+
className: "flex w-full items-center justify-start gap-2 rounded-md px-1.5 py-1.5 text-left text-xs transition-colors " + (h.days === days ? "bg-muted font-medium text-foreground" : "font-normal text-muted-foreground hover:bg-muted/50"),
|
|
193
|
+
children: h.label
|
|
194
|
+
},
|
|
195
|
+
h.days
|
|
196
|
+
)) })
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: "state",
|
|
200
|
+
title: t("agenda.sidebar.status"),
|
|
201
|
+
count: shownStates.length,
|
|
202
|
+
content: /* @__PURE__ */ jsx(Fragment, { children: CONFIRMATION_STATES.map((st) => /* @__PURE__ */ jsx(
|
|
203
|
+
FilterCheckRow,
|
|
204
|
+
{
|
|
205
|
+
option: { value: st.value, label: st.label, color: st.color },
|
|
206
|
+
checked: shownStates.length === 0 || shownStates.includes(st.value),
|
|
207
|
+
onToggle: () => setShownStates((cur) => cur.includes(st.value) ? cur.filter((x) => x !== st.value) : [...cur, st.value])
|
|
208
|
+
},
|
|
209
|
+
st.value
|
|
210
|
+
)) })
|
|
211
|
+
}
|
|
212
|
+
],
|
|
213
|
+
footer: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
214
|
+
shown.length,
|
|
215
|
+
" de ",
|
|
216
|
+
confirmations.length
|
|
217
|
+
] })
|
|
218
|
+
}
|
|
219
|
+
);
|
|
220
|
+
const renderCard = (booking) => {
|
|
221
|
+
const confStatus = getConfirmationStatus(booking);
|
|
222
|
+
const tone = STATE_TONE[confStatus] ?? STATE_TONE.pending;
|
|
223
|
+
const busy = busyId === booking.id;
|
|
224
|
+
const leaving = leavingId === booking.id;
|
|
225
|
+
booking.services?.map((sv) => sv.name).join(", ");
|
|
226
|
+
booking.orderTotal != null ? formatCurrency(booking.orderTotal, config.currency.code) : null;
|
|
227
|
+
return /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
228
|
+
BookingCard,
|
|
229
|
+
{
|
|
230
|
+
booking,
|
|
231
|
+
clientKind: Array.isArray(config.clientKind) ? config.clientKind[0] : config.clientKind,
|
|
232
|
+
showDate: true,
|
|
233
|
+
busy,
|
|
234
|
+
flagClass: tone.flag,
|
|
235
|
+
className: `shadow-button hover:bg-muted/40 ${tone.border} ${busy ? "pointer-events-none opacity-60" : ""} ${leaving ? "pointer-events-none translate-y-8 scale-[0.98] opacity-0 duration-[480ms] ease-in" : "translate-y-0 scale-100 duration-300"}`,
|
|
236
|
+
badge: /* @__PURE__ */ jsxs("span", { className: `inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-[11px] font-medium ${tone.badge}`, children: [
|
|
237
|
+
busy && /* @__PURE__ */ jsx(Loader2, { className: "h-3 w-3 animate-spin", "aria-hidden": true }),
|
|
238
|
+
STATE_LABEL(t)[confStatus] ?? confStatus
|
|
239
|
+
] }),
|
|
240
|
+
actions: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
241
|
+
booking.clientPhone && /* @__PURE__ */ jsx(ClientContactButton, { phone: booking.clientPhone }),
|
|
242
|
+
/* @__PURE__ */ jsx("span", { className: "mx-0.5 h-5 w-px bg-border", "aria-hidden": true }),
|
|
243
|
+
booking.status !== "confirmed" && /* @__PURE__ */ jsxs(
|
|
244
|
+
Button,
|
|
245
|
+
{
|
|
246
|
+
variant: "bare",
|
|
247
|
+
size: "bare",
|
|
248
|
+
type: "button",
|
|
249
|
+
onClick: () => decide(booking.id, "confirmed"),
|
|
250
|
+
disabled: busy,
|
|
251
|
+
className: "flex h-8 items-center gap-1.5 rounded-button bg-primary px-3 text-xs font-medium text-primary-foreground shadow-button transition-all hover:brightness-110 active:scale-95 disabled:opacity-60",
|
|
252
|
+
children: [
|
|
253
|
+
/* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5" }),
|
|
254
|
+
/* @__PURE__ */ jsx("span", { children: t("agenda.confirmations.confirmShort") })
|
|
255
|
+
]
|
|
256
|
+
}
|
|
257
|
+
),
|
|
258
|
+
/* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
259
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
260
|
+
Button,
|
|
261
|
+
{
|
|
262
|
+
variant: "bare",
|
|
263
|
+
size: "bare",
|
|
264
|
+
type: "button",
|
|
265
|
+
onClick: () => decide(booking.id, "cancelled"),
|
|
266
|
+
disabled: busy,
|
|
267
|
+
"aria-label": t("agenda.confirmations.markCancelled"),
|
|
268
|
+
className: "flex h-8 w-8 items-center justify-center rounded-button border border-transparent text-muted-foreground transition-all hover:border-destructive/30 hover:bg-destructive/10 hover:text-destructive active:scale-95 disabled:opacity-60",
|
|
269
|
+
children: /* @__PURE__ */ jsx(X, { className: "h-3.5 w-3.5" })
|
|
270
|
+
}
|
|
271
|
+
) }),
|
|
272
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: t("agenda.confirmations.markCancelled") })
|
|
273
|
+
] })
|
|
274
|
+
] })
|
|
275
|
+
}
|
|
276
|
+
) }, booking.id);
|
|
277
|
+
};
|
|
278
|
+
return /* @__PURE__ */ jsx(AgendaWorkspace, { rail, children: /* @__PURE__ */ jsx(TooltipProvider, { delayDuration: 300, children: /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
279
|
+
/* @__PURE__ */ jsxs("div", { className: `rounded-lg border p-3 transition-colors ${allDone ? "border-success/40 bg-success/5" : "border-border bg-card"}`, children: [
|
|
280
|
+
/* @__PURE__ */ jsxs("div", { className: "mb-2 flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1", children: [
|
|
281
|
+
/* @__PURE__ */ jsxs("span", { className: "flex items-baseline gap-2", children: [
|
|
282
|
+
/* @__PURE__ */ jsxs("span", { className: `text-2xl font-semibold tabular-nums transition-colors ${allDone ? "text-success" : "text-foreground"}`, children: [
|
|
283
|
+
confirmRate,
|
|
284
|
+
"%"
|
|
285
|
+
] }),
|
|
286
|
+
/* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
287
|
+
confirmedCount,
|
|
288
|
+
" ",
|
|
289
|
+
t("agenda.confirmations.ofConfirmed", { total: String(totalCount) })
|
|
290
|
+
] })
|
|
291
|
+
] }),
|
|
292
|
+
allDone ? /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1.5 text-sm font-medium text-success", children: [
|
|
293
|
+
/* @__PURE__ */ jsx(CheckCircle, { className: "h-4 w-4", "aria-hidden": true }),
|
|
294
|
+
t("agenda.confirmations.allDone")
|
|
295
|
+
] }) : /* @__PURE__ */ jsxs("span", { className: "text-sm text-muted-foreground", children: [
|
|
296
|
+
t("agenda.confirmations.toConfirm"),
|
|
297
|
+
" ",
|
|
298
|
+
/* @__PURE__ */ jsx("strong", { className: "text-base font-semibold text-foreground tabular-nums", children: pendingCount })
|
|
299
|
+
] })
|
|
300
|
+
] }),
|
|
301
|
+
/* @__PURE__ */ jsx(
|
|
302
|
+
"div",
|
|
303
|
+
{
|
|
304
|
+
className: "h-2 overflow-hidden rounded-full bg-muted",
|
|
305
|
+
role: "progressbar",
|
|
306
|
+
"aria-valuenow": confirmRate,
|
|
307
|
+
"aria-valuemin": 0,
|
|
308
|
+
"aria-valuemax": 100,
|
|
309
|
+
"aria-label": t("agenda.confirmations.confirmed"),
|
|
310
|
+
children: /* @__PURE__ */ jsx(
|
|
311
|
+
"div",
|
|
312
|
+
{
|
|
313
|
+
className: `h-full rounded-full transition-[width,background-color] duration-700 ease-out ${allDone ? "bg-success" : "bg-warning"}`,
|
|
314
|
+
style: { width: `${confirmRate}%` }
|
|
315
|
+
}
|
|
316
|
+
)
|
|
317
|
+
}
|
|
318
|
+
)
|
|
319
|
+
] }),
|
|
320
|
+
loading && confirmations.length === 0 ? /* @__PURE__ */ jsx("ul", { className: "space-y-2", "aria-busy": "true", "aria-label": t("agenda.confirmations.loading"), children: [0, 1, 2, 3].map((i) => /* @__PURE__ */ jsxs("li", { className: "flex gap-3 rounded-lg border border-border bg-card p-3", children: [
|
|
321
|
+
/* @__PURE__ */ jsx("div", { className: "w-1 shrink-0 rounded-full bg-muted" }),
|
|
322
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-2", children: [
|
|
323
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
324
|
+
/* @__PURE__ */ jsx("div", { className: "h-3.5 w-16 animate-pulse rounded bg-muted" }),
|
|
325
|
+
/* @__PURE__ */ jsx("div", { className: "h-4 w-44 animate-pulse rounded bg-muted/80" })
|
|
326
|
+
] }),
|
|
327
|
+
/* @__PURE__ */ jsx("div", { className: "h-3 w-72 animate-pulse rounded bg-muted/60" })
|
|
328
|
+
] }),
|
|
329
|
+
/* @__PURE__ */ jsx("div", { className: "h-8 w-40 shrink-0 animate-pulse rounded-button bg-muted/60" })
|
|
330
|
+
] }, i)) }) : shown.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [
|
|
331
|
+
/* @__PURE__ */ jsx(CheckCircle, { className: "mb-3 h-12 w-12 text-muted-foreground/30" }),
|
|
332
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: confirmations.length === 0 ? t("agenda.confirmations.empty") : t("agenda.confirmations.noMatch") }),
|
|
333
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground/70", children: confirmations.length === 0 ? t("agenda.confirmations.emptySubtitle") : t("agenda.confirmations.noMatchSubtitle") })
|
|
334
|
+
] }) : /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
|
|
335
|
+
/* @__PURE__ */ jsx(
|
|
336
|
+
Group,
|
|
337
|
+
{
|
|
338
|
+
title: t("agenda.confirmations.groupPending"),
|
|
339
|
+
count: pending.length,
|
|
340
|
+
defaultOpen: true,
|
|
341
|
+
empty: t("agenda.confirmations.allDone"),
|
|
342
|
+
children: pending.map(renderCard)
|
|
343
|
+
}
|
|
344
|
+
),
|
|
345
|
+
(settled.length > 0 || held.length > 0) && /* @__PURE__ */ jsx(
|
|
346
|
+
Group,
|
|
347
|
+
{
|
|
348
|
+
title: t("agenda.confirmations.groupSettled"),
|
|
349
|
+
count: settled.length,
|
|
350
|
+
defaultOpen: false,
|
|
351
|
+
pulse,
|
|
352
|
+
children: settled.map(renderCard)
|
|
353
|
+
}
|
|
354
|
+
)
|
|
355
|
+
] })
|
|
356
|
+
] }) }) });
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export { ConfirmationsView };
|
|
360
|
+
//# sourceMappingURL=ConfirmationsView-DZJSYBXA.js.map
|
|
361
|
+
//# sourceMappingURL=ConfirmationsView-DZJSYBXA.js.map
|