- Clicar dente adiciona GTOItem diretamente ao rascunho (sem botão "adicionar")
- Clicar dente já adicionado remove o item do rascunho
- Cada item no rascunho tem checkboxes O,M,D,V,L,P com comportamento radio (seleciona 1, desmarca outros)
- Observação editável inline abaixo de cada item
- Dentista avaliador (selecionado) aparece em cada linha do rascunho
- ADICIONAR ITEM mantido apenas para procedimentos ARCO/GERAL
- Validação de face obrigatória movida para FINALIZAR GTO
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Cada dente selecionado tem seu próprio input de face e observação (não mais unificado)
- ADICIONAR ITEM cria um GTOItem por dente com face/obs individual
- Layout mobile em 3 páginas: pág 1 paciente/especialidade/dentista, pág 2 odontograma, pág 3 faces/obs + rascunho
- Navegação mobile com footer fixo: VOLTAR | indicador de página | PRÓXIMO / FINALIZAR
- Campo descricao_mobile no cadastro de procedimentos (versão curta para celular)
- ALTER TABLE procedimentos ADD COLUMN descricao_mobile TEXT (já executado no DB)
- Desktop mantém layout coluna dupla original sem mudança
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Substitui o match de nome no frontend por uma busca real no backend.
handleAtender agora chama getPacientes(pacienteNome), seta o paciente
diretamente no store e navega — LancarGTO abre com o card já preenchido.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Adiciona normalize() para remover acentos antes de comparar nomes,
resolvendo falhas quando o agendamento usa "Joao" e o cadastro "João"
- Troca correspondência exata por filtro com includes() em ambas as direções
- Seleciona diretamente se há exatamente 1 match (sem abrir modal)
- Se múltiplos matches: abre modal com busca pré-preenchida
- Se nenhum match: abre modal vazio para busca manual
- Aplica normalize() também no filteredPatients para consistência
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- LancarGTO: substitui <select> de beneficiário por botão que abre modal de busca
com filtro em tempo real por nome/CPF; avatar com inicial do paciente
- LancarGTO store: adiciona pendingSearch para receber paciente pré-selecionado
de outra view; auto-seleciona por correspondência exata ou abre modal com
o nome pré-preenchido
- AgendaView: adiciona botão "ATENDER PACIENTE" no painel de detalhes do
agendamento (visível para todos os roles exceto paciente); ao clicar define
o pendingSearch e navega para /lancar-gto com o paciente do horário
- App.tsx: passa onNavigate para AgendaView; adiciona lancar-gto às permissões
do role dentista
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Tabelas contratos e contrato_itens com migrações automáticas
- Endpoints CRUD com itens em transação
- ContratosView: listagem, filtro por status, cards, stats
- ContratoModal: wizard 3 abas (Dados/Itens/Cláusulas), catálogo de
procedimentos, DnD para reordenar, financeiro completo, print A4
- Sidebar: novo item CONTRATOS
- App.tsx: nova rota /contratos com permissões dentista/funcionario
- PatientsView: botão CONTRATOS no menu financeiro
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Catálogo de procedimentos por especialidade com busca e filtros
- Preço automático pelo convênio/plano do paciente (valoresPlanos)
- Campos de região: dente (texto livre), arcada (Superior/Inferior/Ambas),
face (V/L/M/D/O multi-select) conforme tipo_regiao e exige_face
- Quantidade por item com +/-
- Painel financeiro: desconto (R$ ou %), entrada (auto-marcada Pago),
parcelamento até 48x com vencimentos mensais automáticos
- Ao salvar: gera uma entrada de financeiro por parcela + entrada separada
- Integrado no botão LANÇAR do menu financeiro dos pacientes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Menu clínico expandido para 8 botões em grade 4 colunas (2 no mobile)
- AGENDAMENTOS: reutiliza HistoricoModal existente
- GTOs: exporta useGTOStore do LancarGTO, pré-preenche paciente e navega
para /lancargto via hash
- TRATAMENTOS: novo modal com stats (total/concluídos/faltas), resumo de
procedimentos realizados e histórico cronológico
- RELATÓRIOS: novo modal com visão completa imprimível — dados pessoais,
endereço, vínculos, resumo de atendimentos e observações clínicas
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
useHybridBackend inicializa data como null, não undefined — o default = []
na desestruturação não se aplica a null, causando TypeError ao fazer
dentistas[0]. Substituído por operador ?? após a desestruturação.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- ReceitaModal: gerenciamento de modelos de receita por especialidade com
drag-and-drop para reordenar medicamentos e preview de impressão
- PedidoExameModal: pedido de exames por catálogo (Raio-X / Tomografia /
Outros) com DnD para ordenar, campos de região/observação por item e
preview de impressão
- EditarPacienteModal: reformulado em 4 páginas (Dados / Família /
Endereço / Histórico) com navegação anterior/próximo e modal 2xl
- Backend: tabelas modelos_receita, items_receita, pedidos_exame,
items_pedido_exame criadas na migração; colunas de endereço adicionadas
em pacientes; endpoints CRUD para receitas e pedidos de exame
- Integração: botões Receita e Raio-X no menu clínico agora abrem os
novos modais reais em vez do placeholder
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix dataNascimento date format error: convert dd/MM/yyyy → yyyy-MM-dd when
loading EditarPacienteModal so <input type="date"> validates correctly
- Change convênio field from free text to dropdown populated from planos table
- Redesign grupo familiar: search a patient as family member instead of named
group, with parentesco field (datalist with common options, accepts custom)
- GET /api/pacientes: proper camelCase aliases, LEFT JOINs for grupoFamiliarNome
and indicadoPorNome, sort parameter support (az/za/convenio/recentes)
- Add startup migrations: ADD COLUMN IF NOT EXISTS for grupofamiliarid,
grupofamiliarrelacao, indicadorporid on pacientes table
- savePaciente: preserve caller-supplied id instead of overwriting with Math.random
- Add grupoFamiliarRelacao to Paciente type
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AgendaView:
- Fix agendamentos?.filter(...).map(...) crash: optional chain didn't guard .map when agendamentos is null on initial render
- Fix getPacientes fetcher to extract .data array — API now returns {data, total} not Paciente[], causing searchResults.map crash
LoginView:
- Restore 3s preloader splash screen with {APP_VERSION} instead of hardcoded version string
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix agendamentos?.filter(...).map(...) — optional chain didn't protect .map, crashing when agendamentos is null on first render
- Add Array.isArray guard in AutocompleteField before setResults to prevent non-array from backend error responses breaking subsequent .map
- Add array guard in getGruposFamiliares to return [] on backend error instead of propagating error object
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- Load GOOGLE_CLIENT_ID/SECRET from DB at startup (env vars take priority)
- GET /api/settings/google-credentials → masked status (secret never returned)
- POST /api/settings/google-credentials → save to DB, hot-reload in memory
- /api/auth/google/url returns 503 with clear message when unconfigured
Frontend (ConfiguracoesView):
- OAuth credential inputs (Client ID + Client Secret with show/hide toggle)
- Shows configured status badge (ENV vs DB) and masked clientIdHint
- Instructions with redirect URI shown inline
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Move user profile card and Google connect button from sidebar footer
to a dedicated Configurações view. Sidebar footer now shows a compact
user button that navigates to settings. All roles have access.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Remove all window.location.hash assignments and hashchange events.
Navigate with history.pushState; listen on popstate. Fix base href to
'/' so assets resolve correctly on deep paths. Update magic link URL.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Causa raiz do login não navegar: getCurrentRole() retornava null quando
vinculos.role era NULL no banco, fazendo isViewAllowed() retornar false
e handleNavigate() falhar silenciosamente sem nenhum feedback.
Fixes:
- backend.ts: getCurrentRole usa workspace?.role || 'paciente' para
garantir que nunca retorne null/undefined
- App.tsx: onLoginSuccess usa setCurrentView+navigate diretamente,
sem passar pela guarda de permissão (desnecessária pós-autenticação)
- App.tsx: mount effect sempre chama navigate(currentView) para sincronizar
a URL com a view real — antes só executava com hash vazio, deixando
/#/login na URL ao recarregar com usuário autenticado
- LoginView.tsx: remove spinner isInitializing de 3s que bloqueava o
formulário em toda montagem do componente
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Backend: substitui string literals com aspas duplas por aspas simples
nas queries PostgreSQL (settings, GTO builder, google_tokens delete)
- Backend: corrige alias SQL 'dentistanome' para 'dentistaNome' (quoted)
e ajusta frontend para usar 'pacientenome' (lowercase do PG)
- Backend: move app.listen() para após todas as rotas registradas
- Backend: ativa generateIntelligentNotifications no startup + intervalo 5min
- Backend: magic link usa process.env.APP_URL em vez de localhost:3000 hardcoded
- Frontend: adiciona 'relatorios' às permissões do role 'funcionario'
- Frontend: adiciona onClick ao botão Relatórios do Dashboard
- Frontend: adiciona Content-Type e JWT token em salvarAgendamento/atualizarAgendamento
- Frontend: MeusTratamentos passa JWT token nas chamadas a /api/guias
- Frontend: ClinicasView passa JWT token no endpoint de atualização de cor
- Frontend: corrige subtítulo 'MYSQL + GOOGLE' para 'POSTGRESQL + GOOGLE'
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>