feat: cron automático de thumbnails + lista de pacientes em tabela + CRUD
continuous-integration/webhook Deploy concluído (VPS4)

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>
This commit is contained in:
VPS 4 Deploy Agent
2026-05-31 06:44:45 +02:00
parent 003e568318
commit 67accb56aa
7 changed files with 687 additions and 6 deletions
+43
View File
@@ -207,6 +207,49 @@ router.get('/by-patient', async (req, res) => {
}
});
// ================================================================
// PUT /api/images/patients/:name - Editar dados de um paciente
// Atualiza patient_name, doctor e remark em todas as imagens do paciente.
// ================================================================
router.put('/patients/:name', async (req, res) => {
try {
const oldName = decodeURIComponent(req.params.name);
const { newName, doctor, remark } = req.body;
if (!oldName) return res.status(400).json({ error: 'Nome do paciente obrigatório.' });
const updates = [];
const params = [];
let i = 1;
if (newName !== undefined && newName.trim() !== '') {
updates.push(`patient_name = $${i++}`);
params.push(newName.trim());
}
if (doctor !== undefined) {
updates.push(`doctor = $${i++}`);
params.push(doctor);
}
if (remark !== undefined) {
updates.push(`remark = $${i++}`);
params.push(remark);
}
if (updates.length === 0) return res.status(400).json({ error: 'Nenhum campo para atualizar.' });
params.push(oldName);
const result = await db.run(
`UPDATE images SET ${updates.join(', ')} WHERE patient_name = $${i}`,
params
);
res.json({ success: true, updated: result.changes });
} catch (error) {
console.error('Erro ao editar paciente:', error);
res.status(500).json({ error: error.message });
}
});
// ================================================================
// DELETE /api/images/by-patient - Excluir todas imagens de um paciente
// ================================================================