- .gitea/workflows/build.yml: push->build rx-scoreodonto:<sha> no registry; tag->promote+deploy
- docker-compose.prod.yml: image do registry, APP_VERSION/GIT_COMMIT em runtime
- deploy-prod.sh: VPS1 pull + up --no-build (antigo vira deploy-prod-legado-webhook.sh)
- /api/health usa APP_VERSION (tag) com fallback version.txt; badge no canto
- webhook 9003 aposentado (gatilho agora e CI/tag)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- clinica_id (tenant do scoreodonto, clinicas.id) como eixo de isolamento em
clinics_devices e images (+índices); clinic_name vira rótulo legível.
- Cadastro de device, socket (machine_token) e uploads (direct + legado) gravam clinica_id.
- Listagens (/images, /patients, /by-patient, /unique-clinics) filtram por clinica_id.
- /clients: lista clientes Windows ativos (inclui autenticados sem client-identify),
tempo real + polling, exibe a clínica.
- send-image marcado como legado (client real usa Direct Upload).
Inclui trabalho de dev acumulado no working tree (não havia commits desde origin/main).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Todo o tratamento mobile é aditivo e isolado atrás de @media (max-width:1023px/
480px/360px) — o visual desktop (≥1024px) permanece pixel-idêntico.
Navegação:
- Sidebar.jsx: vira drawer deslizante no mobile (top-bar fixo + hambúrguer +
backdrop + botão fechar). Trava scroll do body, fecha ao navegar/tocar item.
No desktop continua sidebar fixa idêntica.
Componente novo:
- DropdownMenu.jsx: menu flutuante chevron-down reutilizável (fecha em
outside-click/ESC). Usado para colapsar ações secundárias no mobile.
Headers (GtosPage, PatientsPage):
- Busca full-width; botões "+ Novo/Atualizar" em linha no desktop (.hide-mobile)
e colapsados em DropdownMenu no mobile (.show-mobile).
Tabelas → cards empilhados (PatientsPage, GtosPage, AdminClinicsPage):
- classe .responsive-table + data-label em cada td. No mobile thead some e
cada linha vira card com rótulo:valor. Desktop mantém tabela normal.
CSS (index.css):
- Sistema de breakpoints completo, drawer, dropdown, cards de tabela,
grid de thumbs reduzido, modais fullscreen, 100dvh, inputs 16px (anti-zoom
iOS), alvos de toque ≥40px, paginação/stat-cards que quebram linha.
Fix: remove chave 'border' duplicada no style do stat card da GtosPage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A refatoração do GET /api/gtos para retornar { rows, total, pages }
quebrou o TransformModal que esperava um array direto.
Correções:
- routes/gtos.js: restaura suporte a ?patient=NOME (match exato) para
o TransformModal continuar filtrando só as GTOs do paciente exibido,
sem retornar resultados de outros pacientes via ILIKE.
- TransformModal.jsx: lê data.rows (novo formato) com fallback para
array direto (compatibilidade durante transição).
As duas superfícies (TransformModal e GtosPage) agora usam a mesma
API, mesmos dados, sem lógica duplicada ou conflitante.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Cria ThumbImg.jsx: wrapper de <img> que intercepta onError e exibe
placeholder 🦷 + label "Aguardando reenvio" em vez de imagem quebrada.
Aplicado em:
- DashboardPage: cards de pacientes e galeria de imagens do paciente
- PatientsPage: miniatura na coluna Nome da tabela
Enquanto o Plan B (reupload-files) não tiver sido concluído pelo cliente
Windows, o card exibe o placeholder informativo em vez de ícone de erro.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- server.js: cron thumb-backfill a cada 10min com rastreio de estado
(lastRun, nextRun, running, lastResult). GET /api/cron/status e
POST /api/cron/run/:key para consulta e disparo manual via painel.
- routes/images.js: PUT /api/images/patients/:name — edita patient_name,
doctor e remark em todas as imagens do paciente de uma vez.
Frontend:
- PatientsPage.jsx (/patients): tabela com colunas Nome (+ miniatura),
Dentista, Imagens, Última atualização, Observações, Ações. Suporta
busca, paginação infinita, editar e excluir por linha.
- SettingsPage.jsx (/settings): seção de Cron Jobs com status, resultado
e botão "Executar agora"; seção de informações do servidor.
- EditPatientModal.jsx: edita nome, dentista e observações do paciente.
- Sidebar.jsx: add "Pacientes" (todos usuários) e "Configurações" (admin).
- App.jsx: rotas /patients (protegida) e /settings (admin).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Adiciona coluna clinic_name em images (ALTER TABLE IF NOT EXISTS)
- Todas as funções de storage recebem clinicName como parâmetro: saveImage,
getImageBuffer, getImageUrl, deleteImage, getDownloadPresignedUrl, getUploadPresignedUrl
- Chave Wasabi passa de pc_name/patient/arquivo → clinic_name/pc_name/patient/arquivo
- server.js: usa socket.user.clinicName (preenchido via machine_token auth do DB) como
fonte primária do nome da clínica nos 3 handlers de upload e na rota thumbnail
- Todos os INSERTs em images passam a gravar clinic_name
- Corrige bug oculto: wipe de clínica agora apaga o prefixo correto no Wasabi
- Scripts utilitários atualizados: backfill-wasabi-thumbs, recreate-all-thumbs, migrate-to-wasabi
- client-monitor.js: envia clinicName no client-identify (fallback para clientes legados)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Form não enviava password → servidor retornava 400 em todo cadastro
- Adicionado input password no modal de cadastro
- pc_name tornado opcional no servidor (já era opcional no UI)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Troca nome do sistema de "RF Dental" para "Score Client" em toda UI (título, sidebar, login, página de download)
- Corrige link de download: era hardcoded /updates/RFDental-Setup.exe (inexistente), agora lê o nome real do arquivo a partir do latest.yml e constrói URL versionada dinamicamente
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- DELETE /admin/clinics/:id/wipe: apaga images/gto_images do banco (WHERE client_name),
lista e deleta em batch todos os objetos Wasabi com prefixo 'clinic_name/',
e remove o registro do dispositivo
- UI: botão 'Limpar dados' (🧹) separado do 🗑️ (remove só o cadastro),
modal com aviso, confirmação e log da operação
- Sidebar: remove link 'Zerar Sistema'
Ao clicar em Orientar, primeiro mostra 4 rotações da imagem para escolher a
orientação grossa; ao clicar numa, abre o ajuste fino (escolhida + original)
com botão para voltar. Rotação salva = orientação escolhida + ajuste fino.
O endpoint /api/images/:id/thumbnail retornava JSON {url}, mas o React aponta
<img src> direto para ele, causando placeholders cinza (sem erro no devtools).
Agora serve image/jpeg diretamente. Quando thumb_filename é null, puxa a original,
gera a thumb na hora, serve imediatamente e faz backfill no Wasabi + atualiza o banco.