Commit Graph

265 Commits

Author SHA1 Message Date
VPS 4 Builder df5c4bacb8 fix(modais): corrigir crash null[0] em dentistas ao abrir ReceitaModal e PedidoExameModal
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>
2026-05-14 17:02:44 +02:00
VPS 4 Builder a7d2172da8 feat: receitas, pedido de exame, edição multi-página de paciente 2026-05-14 16:53:33 +02:00
VPS 4 Builder 15a8527f1c feat(pacientes): modais de receita, pedido de exame e edição em 4 páginas
- 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>
2026-05-14 16:53:28 +02:00
VPS 4 Builder 3cfc247b6e Merge claude/goofy-sinoussi-94238f: date fix, convenio dropdown, grupo familiar redesign 2026-05-14 16:11:47 +02:00
VPS 4 Builder 3d287a4579 chore: bump APP_VERSION V1.0.3 → V1.0.4
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 15:59:24 +02:00
VPS 4 Builder ee3f6c3bb2 feat(patients): fix date bug, convenio dropdown, redesign grupo familiar
- 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>
2026-05-14 15:54:54 +02:00
VPS 4 Builder 1e873cb653 fix(login): resolve merge conflict — preloader now uses APP_VERSION
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 08:01:55 +02:00
VPS 4 Builder 15ba7ffe95 fix(agenda): fix $.map crash and getPacientes type mismatch; restore preloader
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>
2026-05-14 08:01:18 +02:00
VPS 4 Builder 3229572d3b Merge branch 'claude/youthful-mendel-0c569f' 2026-05-14 07:50:25 +02:00
VPS 4 Builder e0be095055 fix(patients): prevent $.map crash in AgendamentoModal and AutocompleteField
- 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>
2026-05-14 07:50:22 +02:00
VPS 4 Builder cabd2b9118 Merge claude/youthful-mendel-0c569f: grupo familiar + indicado por
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 07:32:30 +02:00
VPS 4 Builder 4c39a04654 feat(pacientes): grupo familiar + indicado por — DB migration, routes, autocomplete UI, family modal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 07:32:27 +02:00
VPS 4 Builder 11b0c2d2bf Merge claude/youthful-mendel-0c569f: patient UI overhaul
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 07:19:14 +02:00
VPS 4 Builder b411ab3c01 feat(pacientes): compact cards (6/screen), sort pills, clean search bar, 20-default limit
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 07:19:11 +02:00
VPS 4 Builder 58d875b4fc Merge claude/youthful-mendel-0c569f: patient card redesign + dataNascimento fix
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 07:12:01 +02:00
VPS 4 Builder 51cbd07125 fix(pacientes): alias dataNascimento in query; show full name; add CopyField component per data field
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 07:11:58 +02:00
VPS 4 Builder 6b67fbc26e fix(sync): truncate fields in import-legacy to match column VARCHAR limits
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 07:05:31 +02:00
VPS 4 Builder ca584dca72 Merge claude/youthful-mendel-0c569f: import-legacy + server-side pacientes search
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 07:03:58 +02:00
VPS 4 Builder 162404f4e8 feat(sync): add import-legacy route for Apps Script patients; fix pacientes API to use server-side search
- POST /api/sync/import-legacy: reads PACIENTES-CONSULTT-CLINIC tab (1755 patients),
  maps Nome+SobreNome→nome, CPF, Credenciada→convenio, DataNasci, TefefonePrincipal→telefone
- GET /api/pacientes: now supports ?q= server-side search, returns {data, total} — avoids loading all 1755 rows in client
- PatientsView: uses {data, total} response, shows count of all patients in header
- SyncView: adds "Importar Planilha Legada" button (amber)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 07:03:51 +02:00
VPS 4 Builder 1d2d164a73 fix(docker): expand frontend build context to root so update-version.js is accessible
WORKDIR changed to /app/frontend so ../update-version.js resolves correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 06:52:58 +02:00
VPS 4 Builder 1b56d729bf Merge claude/youthful-mendel-0c569f: add update-version.js build bump
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 06:52:07 +02:00
VPS 4 Builder 084a8f46c5 feat(build): add update-version.js to auto-increment patch on every build
Script runs before vite build via package.json "build" script.
Reads/writes APP_VERSION in frontend/constants.ts (now at V1.0.3).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 06:52:03 +02:00
VPS 4 Builder c718e888d7 Merge claude/youthful-mendel-0c569f: add APP_VERSION to ConfiguracoesView
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 06:48:58 +02:00
VPS 4 Builder 8331e87d26 feat(frontend): centralize APP_VERSION in constants.ts, show in ConfiguracoesView header
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 06:48:55 +02:00
VPS 4 Builder 424c2e4728 Merge claude/youthful-mendel-0c569f: fix realTab propagation in Sheets sync
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 06:46:33 +02:00
VPS 4 Builder 6cf18c049f fix(sync): propagate real tab name in pushTableToSheet and pullTableFromSheet
Ensures values.update and values.get use the actual tab title returned by
ensureSheetTab (case-insensitive match) so writes don't fail when existing
Apps Script tabs have different casing than our expected names.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 06:46:30 +02:00
VPS 4 Builder 06ae8ce98b Merge branch 'claude/youthful-mendel-0c569f' 2026-05-14 06:36:26 +02:00
VPS 4 Builder 319c0dcedd fix(oauth): check googleCreds not process.env in URL guard
process.env is only set if env var exists; credentials saved via UI
live in googleCreds. Guard was blocking users who saved via DB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 06:36:22 +02:00
VPS 4 Builder 7d61550ae6 Merge branch 'claude/youthful-mendel-0c569f' 2026-05-14 05:56:03 +02:00
VPS 4 Builder 263c98d74f feat(settings): Google OAuth credentials UI in Configurações
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>
2026-05-14 05:56:00 +02:00
VPS 4 Builder 96b0cd0172 Merge branch 'claude/youthful-mendel-0c569f' 2026-05-14 05:52:36 +02:00
VPS 4 Builder fc891ab878 fix(oauth): set GOOGLE_REDIRECT_URI in docker-compose; guard missing credentials
- Add APP_URL and GOOGLE_REDIRECT_URI=https://scoreodonto.com/api/oauth2callback
  to docker-compose backend environment
- /api/auth/google/url returns 503 with clear message when CLIENT_ID/SECRET unset
- GoogleConnectButton shows alert instead of silently breaking on API error

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 05:52:33 +02:00
VPS 4 Builder 3b6c7f43d6 Merge branch 'claude/youthful-mendel-0c569f' 2026-05-14 05:44:59 +02:00
VPS 4 Builder 63962d51c1 feat(sync): Google Sheets bidirectional backup (PG ↔ Sheets)
Backend:
- Add SYNC_TABLES config (pacientes, agendamentos, financeiro, leads, guias)
- Add getSheetsClient(), ensureSheetTab(), pushTableToSheet(), pullTableFromSheet()
- Add schedulePush() debounced 5s auto-push after every write to main tables
- Add GET /api/sync/status, POST /api/sync/push, POST /api/sync/pull
- Update OAuth scope to include spreadsheets

Frontend:
- Redesign SyncView: per-table status grid, two-button operation (Backup/Restore),
  terminal log with color-coded lines, auto-backup info notice

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 05:44:52 +02:00
VPS 4 Builder 8922354772 Merge branch 'claude/youthful-mendel-0c569f' 2026-05-14 05:02:53 +02:00
VPS 4 Builder c70b2cde66 fix(ui): replace all MySQL references with PostgreSQL
Update SyncView description, security note, mock progress logs,
dbConfig, loading text in PatientsView, UpdateDbView warning, and
metadata.json description. Rename syncGoogleSheetsToMysql →
syncGoogleSheetsToPg.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 05:02:49 +02:00
VPS 4 Builder 7abda1dc58 Merge branch 'claude/youthful-mendel-0c569f' 2026-05-14 04:54:25 +02:00
VPS 4 Builder 2a284f9e6e feat(ui): add Configurações page with profile/integration cards
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>
2026-05-14 04:54:22 +02:00
VPS 4 Builder 829489d216 Merge branch 'claude/youthful-mendel-0c569f' 2026-05-14 04:44:55 +02:00
VPS 4 Builder 2f3c0420e3 feat(routing): switch from hash routing to HTML5 History API
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>
2026-05-14 04:44:53 +02:00
VPS 4 Builder edf04c9735 Merge branch 'claude/youthful-mendel-0c569f' 2026-05-14 04:26:07 +02:00
VPS 4 Builder 435176c0c3 fix(backend): add camelCase response mapping for all GET endpoints
PG stores columns lowercase but frontend expects camelCase. Each GET endpoint
now maps the response to include camelCase aliases alongside the lowercase fields.

- agendamentos: dentistaId, pacienteNome, pacienteCelular
- dentistas: corAgenda
- procedimentos: especialidadeId, especialidadeNome, valorParticular, codigoInterno;
  fix valoresPlanos filter (v.procedimentoid) and map planoId, planoNome, codigoPlano
- guias: full camelCase map (dataSolicitacao, beneficiarioNome, tipoTratamento, etc.)
- gto-builder: map items (builderId, procedimentoId, codigoTUSS, valorTotal, etc.)
  and builder fields (pacienteId, dentistaId, credenciadaId)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 04:26:03 +02:00
VPS 4 Builder 18b846d78d Merge branch 'claude/youthful-mendel-0c569f' 2026-05-14 04:18:31 +02:00
VPS 4 Builder 38bbf36735 fix(backend): lowercase all column names in buildInsert/buildUpdate and explicit queries
All PostgreSQL columns are stored lowercase (PG folds unquoted identifiers).
The previous code quoted camelCase keys ("corAgenda", "builderId", etc.) which
caused column-not-found errors.

- buildInsert: k.toLowerCase() for all column names, no more double-quotes
- buildUpdate: k.toLowerCase() for SET clause and WHERE column
- dentistas/register: "corAgenda" → coragenda
- procedimentos PUT: "procedimentoId" → procedimentoid in DELETE
- gto_builders INSERT: "pacienteId"/"dentistaId"/"credenciadaId" → lowercase
- gto_items queries: "builderId" → builderid, "valorTotal" → valortotal
- guias: "dataSolicitacao"/"beneficiarioNome"/etc → lowercase with normalizedSort

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 04:18:29 +02:00
VPS 4 Builder 7c41fa09ed chore(frontend): adiciona @types/react e @types/react-dom 2026-05-14 04:09:41 +02:00
VPS 4 Builder f0f24e8a68 chore: atualiza .gitignore e adiciona package-lock.json do frontend 2026-05-14 04:08:47 +02:00
VPS 4 Builder 3456d26cbc feat(frontend): restore 3s preloader initialization view in LoginView 2026-05-13 23:30:47 +02:00
VPS 4 Builder e61ef6c0ca fix(backend): move procedimentos/reorder before /:id to prevent route shadowing
Express matches routes in registration order. PUT /procedimentos/:id was
registered before /procedimentos/reorder, causing 'reorder' to be captured
as an id param and the reorder endpoint to be unreachable.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 23:20:39 +02:00
VPS 4 Builder a0ab095923 refactor(backend): migrate all queries to native PostgreSQL, remove MySQL shim
- Remove translateQuery function, pool.query override, and pool.getConnection mock entirely
- Replace all ? placeholders with $N positional parameters throughout
- Replace INSERT INTO t SET ? with buildInsert() helper across pacientes, dentistas, especialidades, planos, procedimentos, financeiro, leads, gto_items
- Replace UPDATE t SET ? WHERE id = ? with buildUpdate() helper across the same tables
- Replace all pool.getConnection()/beginTransaction/commit/rollback blocks with withTransaction()
- Convert ON DUPLICATE KEY UPDATE to ON CONFLICT DO UPDATE SET in dentistas/register (usuarios, vinculos, dentistas tables)
- Fix dentistas/register to use withTransaction instead of SET FOREIGN_KEY_CHECKS
- Fix all [rows] array destructuring to { rows } object destructuring (PG native result shape)
- Fix guias dynamic WHERE builder to use incrementing $N parameter indices
- Fix double-quoted string literal bug in procedimentos categoria query ("Particular" → 'Particular')
- Quote camelCase column names in gto_items/gto_builders queries ("builderId", "valorTotal", etc.)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-13 23:13:35 +02:00
VPS 4 Builder 26a10ac4f7 fix(nav): corrige sistema de navegação pós-login e sincronização de URL
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>
2026-05-13 22:56:30 +02:00