- HorariosConfig: nova aba "Procedimentos" — lista marcável (atende/não atende); ao
desmarcar, campos motivo + como falar. GET/PUT /clinica/:id/procedimentos-atende.
- Menu: "SECRETÁRIA IA" sai do menu lateral; agora é acessível por um botão (ícone Bot)
no header do WhatsApp/Inbox, e o "voltar" da Secretária retorna ao inbox.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Contexto interno do paciente p/ a Secretária dar continuidade: dentista responsável
(do orçamento aberto; fallback = agendamento mais recente) + em_tratamento. Por
paciente_id ou telefone.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
start_time é naive; o node-pg o serializa no fuso do container (America/Sao_Paulo).
Exibir no fuso do NAVEGADOR (ex.: UTC-4) mostrava -1h (09:30 -> 08:30). Agora o
frontend formata SEMPRE em America/Sao_Paulo (o mesmo frame do armazenamento),
recuperando o wall-clock literal para qualquer navegador:
- utils/appTime.ts: STORAGE_TZ + fmtHora/fmtDataCurta/fmtDataHora.
- FullCalendar timeZone="America/Sao_Paulo".
- AgendaDetailModal (card "Data e hora" + histórico) e AgendaView (interesses) usam os helpers.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Lista os dígitos dos números de pessoa_comunicacao da clínica (dentistas/equipe) p/
o motor não tratar um número interno como paciente.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
O avatar/foto do número em /sessions estava rounded-2xl (quadrado arredondado);
passa a rounded-full (círculo), com a foto (object-cover) preenchendo.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
/status calcula "hoje"/semana no fuso da clínica (getClinicaTZ) e passa a devolver
`timezone` — usado pelo motor p/ injetar a data/hora no fuso certo.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Helpers de TZ (partsNow/tzOffsetMin/zonedToUtc/getClinicaTZ; Brasil sem DST).
- Janelas de contato (contatoPermitidoAgora/proximaJanela) passam a receber o
"agora" no fuso da clínica; proxima_janela_ts vira o instante REAL no fuso.
Usado em /dentista-contato, /responsavel-contato, /contatos.
- /slots: "hoje"/"agora" (filtro de passado) no fuso da clínica, não do container.
Verificado E2E: clínica=Campo_Grande(UTC-4), container UTC-3 21h → janela às 20h
(hora da clínica) libera; às 21h (hora do container) bloqueia.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fundação do multi-fuso: cada clínica define seu timezone (Configurações → Perfil da
Unidade → Fuso horário). Migration clinicas.timezone (default America/Sao_Paulo);
GET/PUT /api/clinicas/:id/timezone (whitelist de fusos do Brasil; PUT só dono/admin).
Base para a Fase 2 (usar o fuso no cálculo de slots, no "agora" da Secretária e na
exibição da agenda) — que corrige o -1h observado (IA confirmou 09:30, agenda mostrou
08:30) causado por satélite=UTC-3, motor=UTC e navegador=UTC-4 com horário naive.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1) 403 ao "desligar a IA no chat": guardAreaAccess tratava TODO /sec/* como config da
Secretária (exige can_secretaria). Mas /sec/handoff é assumir/soltar o CHAT = INBOX.
Reclassificado p/ can_inbox — a recep (can_inbox, sem can_secretaria) volta a assumir
o chat. /sec/agent|nodes|slots e /secretaria/* seguem exigindo can_secretaria.
(session-power global segue can_secretaria — o dono delega se quiser.)
2) Duplicação de horário (parte 2): o DONO agora tem, no modal, o card + botão p/
configurar o "Horário da clínica" (clinicas_horarios) em Horários de Funcionamento
(aba clínica). Completude do dono passa a exigir esse horário. Backend:
comunicacaoCompleto(p, {ehDentista,jornadaOk,ehDono,horarioClinicaOk}); GET devolve
horario_clinica_configurado. Verificado: dono da Consultt sem horário → gate exige.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A seção "Agenda/Horários de atendimento" do modal era uma 3ª fonte de horário que a
Secretária NÃO lia (ela agenda por clinicas_horarios + dentistas_horarios). Removida:
- Modal cuida só de contato (endereço + números + janelas de envio). Para dentista,
mostra o STATUS da jornada real e um botão que abre "Horários de Funcionamento"
(HorariosConfig, aba dentistas) por cima; ao fechar, recarrega.
- Completude do DENTISTA passa a exigir a jornada REAL configurada (dentistas_horarios),
não mais a agenda do modal. GET recomputa `completo` ao vivo + devolve jornada_configurada.
- Backend: comunicacaoCompleto(p, ehDentista, jornadaOk) + helper jornadaConfigurada.
Verificado: dentista com jornada → completo; query bate com dentistas_horarios real.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolve o contato do dono/responsável p/ decisões da clínica: por nome (pessoa da
lista de contatos — cobre o dono cujo número está na conta de dentista) ou por papel
(dono/responsável). Mesma forma do /dentista-contato (contato_agora, bloqueado_por_janela,
proxima_janela[_ts]). Verificado: por nome resolve o número compartilhado.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Opção (b) — mesma pessoa/número servindo a papéis diferentes, roteando por situação:
- GET /api/comunicacao/numero-lookup: ao digitar um número, avisa se ele já é de outra
conta/papel da clínica (ex.: número na conta dentista aparece ao editar a de dono).
Modal mostra banner "pode compartilhar — descreva as situações; a Secretária roteia".
- Bridge GET /contatos: agenda de contatos internos (nome, papel, situações de cada
número, janela atual) p/ a Secretária rotear por assunto. Não expõe o número cru.
Verificado E2E: lookup como dono acha o nº na conta dentista; /contatos lista papel+situações.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
O gate de comunicação interna usava useRef sem importá-lo, quebrando o render
inteiro do App (ReferenceError → tela branca).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Gate progressivo: modal ganha "Lembrar mais tarde" e reaparece a cada login;
a partir da 4ª aparição vira bloqueio até preencher (contador em localStorage
por clínica; ref evita contagem dupla no StrictMode).
- Agenda passa a ser exigida/exibida só para DENTISTA (quem atende). Backend:
comunicacaoCompleto(p, ehDentista) via vinculos.role; GET devolve `role`.
Não-dentista (dono/funcionário) completa só com endereço + números.
Verificado: dono completa sem agenda; dentista exige agenda; role no GET ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Perfil sem endereço próprio → GET /api/comunicacao sugere o endereço já cadastrado
da clínica (só confirmar, não redigitar) + flag endereco_da_clinica. Modal mostra
aviso "puxamos o endereço da clínica — é só conferir". Resolve a fricção do dono,
cuja clínica já tem endereço.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
/dentista-contato passa a devolver proxima_janela_ts (ISO do início da próxima janela)
além do label — o motor usa como fire_at do envio diferido.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
/dentista-contato passa a ler pessoa_comunicacao (via dentistas.usuario_id):
- Com perfil: só devolve número se a hora atual cair numa janela permitida
(contato_agora); senão bloqueado_por_janela=true + proxima_janela ("sábado às 08:00").
- Sem perfil: back-compat — usa telefone legado e não bloqueia.
Helpers contatoPermitidoAgora/proximaJanela (TZ do container = America/Sao_Paulo).
Verificado E2E: fora da janela -> bloqueado; dentro -> libera nº; sem perfil -> ok.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Botão "Comunicação interna" em cada membro (gestores) abre o mesmo modal full-screen
para preencher/editar o perfil de contato daquele usuário (usuarioId = m.id).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Base da "comunicação interna": cada não-paciente (dentista/funcionário) cadastra
endereço, MÚLTIPLOS números de WhatsApp e a disponibilidade de agenda.
Backend:
- Tabela pessoa_comunicacao (usuario_id+clinica_id): endereco/numeros/agenda JSONB + completo.
- GET/PUT /api/comunicacao/:usuarioId ('me' = usuário atual). Autoriza self OU gestor
(dono/admin). Recalcula `completo` no PUT (regra: endereço + ≥1 número com descrição
e ≥1 janela de horário + agenda decidida; se aberta, exige ≥1 janela).
Frontend:
- ComunicacaoInternaModal (full-screen): Endereço (com ViaCEP), Números (cada um com
descrição + situações + janelas de horário permitido p/ a Secretária enviar msg),
Agenda (abrir/fechar + horários de atendimento). Editor de janelas reutilizável.
- Gate no App.tsx: todo não-paciente com clínica ativa cujo perfil está incompleto vê
o modal FORÇADO em todas as telas (espelha o gate de troca de senha) até completar.
Verificado E2E: PUT incompleto->completo=false; PUT completo->true; GET reflete.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Novos endpoints server-to-server (auth x-nw-agenda-secret), presos ao clinica_id
e ao telefone do contato (posse verificada — impede mexer na consulta de terceiros):
- GET /consultas — consultas do telefone/família (futuras por padrão; incluir_passadas)
- POST /confirmar — marca presença (status=confirmado)
- POST /cancelar — cancela e libera o horário (status=cancelado + motivo em observacoes)
- POST /remarcar — move para novo slot com anti-overbooking (findConflict exclui a própria)
Verificado E2E: GET /consultas -> 200 com dentista/procedimento/status.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backend:
- Nova coluna salas.contrato (TEXT, migration ADD COLUMN IF NOT EXISTS).
- INSERT/UPDATE de sala aceitam `contrato`; marketplace SELECT devolve s.contrato
(minhas salas já via s.*).
Frontend (SalasPlugin):
- CONTRATO_MODELO: modelo padrão cuja cláusula 2 deixa TODOS os procedimentos sob
inteira responsabilidade do locatário; o dono edita por sala.
- SalaForm ganha textarea "Contrato de locação" pré-preenchida com o modelo +
botão "Restaurar modelo".
- Passo Pagamento: botão "Ler contrato de locação" abre modal com o texto
(contrato da sala, ou o modelo) + checkbox de aceite. "Solicitar Reserva" só
habilita após aceitar; o aceite é registrado nas observações da reserva.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Antes o modelo Dia só permitia um intervalo consecutivo (início + N dias).
Agora permite escolher dias avulsos (ex.: 1 e 5, 3 e 20):
- Clique no calendário alterna (marca/desmarca) cada dia — estado diasMulti[].
- Card mostra chips dos dias selecionados (removíveis; dia ocupado fica âmbar) +
dica "cada dia vira uma reserva (08h–18h)".
- Total = valor_diaria × nº de dias; resumo lista os dias.
- handleReserve envia uma reserva por dia; reporta quantas foram criadas e
quantas falharam (ex.: já ocupado).
- Travas de navegação/botão passam a usar temSelecao (Dia não usa f.inicio).
- Semana/Mês seguem com stepper de quantidade; Hora/Período inalterados.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Calendário (passo 2):
- qtd vira "quantidade" genérica por modelo: Hora=horas (1–12), Dia=dias (1–30),
Semana=semanas (1–8), Mês=meses (1–12). computeRange estende o fim conforme a
quantidade; total = valor × quantidade (Turno segue unitário).
- O intervalo escolhido (diária/semanal/mensal) é realçado no calendário e some
ao trocar de modelo (qtd volta a 1).
- Controles (Início/Duração/Período/Quantidade) que ficavam escondidos abaixo do
calendário agora vivem num card destacado, com cabeçalho do dia/intervalo
selecionado e auto-scroll (scrollIntoView) ao escolher o dia.
- Resumo ganha linha "Quantidade" e a nota do total é genérica (N un × valor).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Seletor de modelo (Hora/Período/Dia/Semana/Mês) sai do passo Calendário e
passa a ficar no passo Dados — o usuário escolhe o modelo primeiro, depois
o calendário/horário reagem à escolha.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Rótulos dos modelos: Turno→Período, Diária→Dia, Semanal→Semana, Mensal→Mês
(ids inalterados). Label do controle "Turno" → "Período".
- Calendário cortado no wizard: as células aspect-square deixavam a grade alta
demais p/ a área do slide; troca p/ altura fixa h-9 — a grade fica compacta e
aparece completa.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Modal de reservar sala reorganizado num wizard (formato slide, translateX) com
stepper clicável p/ voltar:
1) Dados — reservar como (pessoal/unidade) + profissional + observações.
2) Calendário — modelo (cartões c/ preço) + calendário mensal + horário/turno.
3) Resumo — resumo detalhado (sala, modelo, período, total) + histórico de
reservas da sala.
4) Pagamento — total, forma de pagamento (PIX/Dinheiro/Transferência/Cartão) e
como funciona; a forma escolhida vai nas observações do pedido.
Navegação Voltar/Próximo (Próximo bloqueado no calendário até escolher o dia).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Substitui os dois datetime-local crus por um calendário mensal profissional
(navegação de mês, dia selecionável, dias passados desabilitados, dias ocupados
com marcador, destaque de intervalo p/ semanal/mensal). Modelo de locação vira
grade de cartões com o preço de cada (Hora/Turno/Diária/Semanal/Mensal, só os que
a sala oferece). Controles conforme o modelo: hora (início + duração), turno
(manhã/tarde/noite); diária/semanal/mensal calculam o período. Resumo do período
+ ocupados do dia selecionado. início/fim derivados do calendário (backend igual).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
As duas rotas renderizam o mesmo SalasPlugin com prop `view` diferente; o `tab`
vinha do useState (só no mount) e não reagia à troca de `view`. Agora um efeito
reseta a aba ativa quando `view` muda.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fonte única para os campos de filtro (inputNew/labelNew) e um componente
SearchInput padrão (ícone + input arredondado, encaminha ref/props). Profissionais
e Salas passam a importar do módulo (dedupe). Aplica SearchInput na busca do
Orçamentos como 1ª adoção. Buscas que são autocomplete com dropdown (Pacientes,
Relatórios) ficam para conversão cuidadosa caso a caso (não quebrar o dropdown).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Área de filtros de /alugar-salas restilizada igual ao marketplace de
profissionais: card com busca no topo, filtros recolhíveis (Ocultar/Mostrar),
slider de raio com badge KM, barra de ações (Limpar + Buscar) e busca por
texto (nome/cidade/bairro) sobre o resultado.
- Fix: o marketplace listava as PRÓPRIAS salas (e as da unidade ativa) — agora
exclui owner_usuario_id do usuário e clinica_id da unidade ativa. Não se aluga
a própria sala.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cria components/SegmentedTabs.tsx (controle segmentado padrão, responsivo, com
cor de destaque + ícone + badge) e converte as barras de aba que usavam sublinhado
ou pills soltos: Comissões, Glosas, Lab Clientes, Prótese (OS + cotações),
Contratos, Profissionais (marketplace) e HorariosConfig. Telas que já usavam o
visual (Gestão de Equipe, Notificações, RX, Salas) ficam como referência.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Abas viram segmented control: no mobile ocupam a largura toda e distribuem igual;
no desktop encolhem ao conteúdo (some quando há só 1 aba).
- "Cadastrar Sala" ganha toolbar (contagem à esquerda, botão à direita no desktop,
largura cheia no mobile) em vez de botão solto.
- Filtros do marketplace: grid 2/3/6 colunas (distribui uniforme) e BUSCAR full-width
no mobile.
- Cards de sala: 3 colunas em telas largas (xl).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
O menuItems do Sidebar filtra por um allowlist de ids p/ o superadmin, e
'superadmin-secretaria' não estava incluído — por isso a página de motivos não
aparecia no menu. Adicionado ao allowlist.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Botão global no /wa-secretaria agora abre modal com textarea de motivo (obrigatório
p/ desativar) + histórico, em vez de um confirm. Página super admin "Secretária IA"
passa a listar os motivos por ESCOPO: "Todos os chats" (global) e "Uma sessão",
mesclando os dois logs do motor (/secretaria/power/log + /session-power/log).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Complementa o fix anterior: no mount, loadChats podia rodar com o activeInstanceId
persistido (stale) antes de loadInstances validar, deixando os chats na tela mesmo
após o reset. Agora o efeito faz setChats([]) quando não há instância ativa.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
O /wa-inbox persistia activeInstanceId + inboxCache:v1:* no localStorage e o logout
não limpava — então o próximo login (ex.: superadmin) herdava a instância e via os
chats antigos de outra sessão. Agora: clearInboxLocalState() no logout, na expiração
de sessão e na troca de workspace; e loadInstances reseta activeInstanceId se ele não
estiver na lista de instâncias acessíveis (guarda defensiva).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Remove colunas Número e Quem (telefone/pessoa da clínica) da tabela — a página é
contexto GERAL para melhorar a IA, não para ver dados de uma clínica. Ajusta o texto.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Nova aba no super admin (/superadmin/secretaria): tabela com quem ligou/desligou a
Secretária de cada número, quando e o MOTIVO do desligamento. Endpoint
/api/superadmin/secretaria-desligamentos (superadminGuard) busca o log global no
motor (resolve um e-mail de dono pareado; log é global). Item no Sidebar + rotas.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Botão Power na thin sidebar do /wa-inbox age SÓ na sessão/número aberto (verde=on,
vermelho pulsante=off). Abre modal com textarea de motivo (obrigatório p/ desligar),
histórico de quem ligou/desligou e aviso de rate-limit (1x/hora). secSessionPowerApi
+ SessaoSecretariaPower.tsx.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Grade de 30min com rótulo só na hora (slotDuration 30min + slotLabelInterval 1h);
a linha de :30 fica tracejada/fraca (fc-timegrid-slot-minor), distinta da linha
cheia da hora — como na agenda do Google.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
As ações da coluna de controles no desktop eram só uma fileira de ícones sem
rótulo (difícil saber o que era cada um). Agora o desktop herda a estrutura do
mobile: botões empilhados com ícone + nome (Atividade da equipe, Pedidos pendentes,
Configurações, Meus Horários, etc.), badges reposicionados junto ao ícone.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
/api/dashboard/stats devolvia a agenda inteira da clínica (nomes de pacientes de
todos os dentistas) + faturamento/despesas/growth no payload para QUALQUER membro,
inclusive dentista. Pior: cache por clínica (dashboard:stats:<clinica>) servia o
mesmo JSON a todos os papéis. Agora: dentista vê só a própria agenda; financeiro
só p/ admin/donoclinica/donoconsultorio/funcionario; cache por escopo (papel/dentista).
Verificado: dentista recém-cadastrado (murilo) → agenda vazia + financeiro 0; dono → tudo.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Os papéis (sec_numbers) são resolvidos no motor pela conta (x-nw-email). Um
usuário que só existe no scoreodonto (ex.: recepcionista) não tem conta no motor,
então buscar os papéis com o e-mail DELE retorna "conta não encontrada" → todos os
números liberados apareciam como "Sem papel definido". Agora o papel de cada sessão
liberada é buscado com o e-mail do DONO da sessão (cache por e-mail). Verificado:
recep passa a ver o número como role=clinic ("Clínica / Recepção · Rui Cesar").
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- GET /encaixe/analisar: ocupação do horário desejado (quem está nele) + horário
livre mais cedo (flex-inclusivo) p/ oferecer/encaixar.
- POST /encaixe/mover: remarca um agendamento (o A aceitou adiantar), com checagem
de conflito no destino (findConflict ganha excludeId p/ ignorar o próprio).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
O index.js (já no main) importa createAccountsRoute/provisionNewwhatsAccount do
proxy.js, mas esses exports nunca foram commitados — só existiam no working tree.
Isso quebrava o boot do backend na imagem de produção (SyntaxError: proxy.js does
not provide an export named 'createAccountsRoute') → 502. Commita proxy.js (exports
createAccountsRoute/provisionNewwhatsAccount/operatorSignature/createWsTunnel) e
server.js (usa operatorSignature + parser 12mb em /api/nw/v1), deixando o main
consistente com o backend que já roda em dev.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Fecha furos de identificação relatados:
- Telefone compartilhado na família: nova pergunta telefone_compartilhado +
cautelas IDENTIDADE/TELEFONE COMPARTILHADO — a IA nunca assume quem fala pelo
número (ex.: cadastrado no filho, mas quem fala é a mãe; ou pergunta do marido)
e confirma nome + para quem é antes de agendar; se não bater, encaminha ao humano.
- Profissional não cadastrado: cautela para citações de dentista fora da lista
(ex.: prótese com Dr. de fora) — encaminha ao humano e pede no resumo que o
dono cadastre o profissional.
- Pendência de config quando telefone_compartilhado=Sim (completar cadastro dos familiares).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Satélite da Secretária IA:
- /contexto (dentistas+especialidades+situações+cautelas) e /duvida (encaminhar
ao humano sem agendar); agenda_pedidos ganha tipo=duvida + resumo.
- Situações da clínica: tabela agenda_situacoes + config (dono) + aba na UI.
- Checklist do dono (checklist-defs.js + agenda_checklist): perguntas Sim/Não
conferidas contra o banco (dentistas/pacientes) → pendências de config +
cautelas p/ a IA não chutar.
- Área de PENDÊNCIAS DO DONO (PendenciasDono.tsx): engloba config + fila
operacional (horários/dúvidas); botão+badge no /wa-secretaria (só o dono).
- Fila humana (PedidosPendentes): cards de dúvida + resolver; /resolver.
- Botão DESLIGAR global da Secretária no ThinNav (secPowerApi, visível a todos).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Espaçamento/duração padrão do slot passa de 30 para 60 min (1h entre atendimentos).
- /slots aceita ?periodo=manha|tarde (a Secretária pergunta o período antes de oferecer
e busca só aquele período).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>