From 67accb56aa61b7e2f18d5c60810041aea5010f08 Mon Sep 17 00:00:00 2001 From: VPS 4 Deploy Agent Date: Sun, 31 May 2026 06:44:45 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20cron=20autom=C3=A1tico=20de=20thumbnail?= =?UTF-8?q?s=20+=20lista=20de=20pacientes=20em=20tabela=20+=20CRUD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- dental-server/dental-client/src/App.jsx | 14 + .../src/components/EditPatientModal.jsx | 81 ++++++ .../dental-client/src/components/Sidebar.jsx | 14 + .../dental-client/src/pages/PatientsPage.jsx | 252 ++++++++++++++++++ .../dental-client/src/pages/SettingsPage.jsx | 215 +++++++++++++++ dental-server/routes/images.js | 43 +++ dental-server/server.js | 74 ++++- 7 files changed, 687 insertions(+), 6 deletions(-) create mode 100644 dental-server/dental-client/src/components/EditPatientModal.jsx create mode 100644 dental-server/dental-client/src/pages/PatientsPage.jsx create mode 100644 dental-server/dental-client/src/pages/SettingsPage.jsx diff --git a/dental-server/dental-client/src/App.jsx b/dental-server/dental-client/src/App.jsx index 9f326ee..48b6c06 100644 --- a/dental-server/dental-client/src/App.jsx +++ b/dental-server/dental-client/src/App.jsx @@ -11,6 +11,8 @@ import ClientsPage from './pages/ClientsPage'; import AdminClinicsPage from './pages/AdminClinicsPage'; import ResetPage from './pages/ResetPage'; import DownloadPage from './pages/DownloadPage'; +import PatientsPage from './pages/PatientsPage'; +import SettingsPage from './pages/SettingsPage'; export default function App() { return ( @@ -55,6 +57,18 @@ export default function App() { } /> + + + + } /> + + + + + } /> + {/* Catch-all: redireciona para home */} } /> diff --git a/dental-server/dental-client/src/components/EditPatientModal.jsx b/dental-server/dental-client/src/components/EditPatientModal.jsx new file mode 100644 index 0000000..d8bf0a1 --- /dev/null +++ b/dental-server/dental-client/src/components/EditPatientModal.jsx @@ -0,0 +1,81 @@ +import { useState, useEffect } from 'react'; +import Modal from './Modal'; +import api from '../api/client'; +import { useToast } from '../contexts/ToastContext'; + +export default function EditPatientModal({ isOpen, onClose, patient, onSaved }) { + const { showToast } = useToast(); + const [form, setForm] = useState({ newName: '', doctor: '', remark: '' }); + const [saving, setSaving] = useState(false); + + useEffect(() => { + if (patient) { + setForm({ + newName: patient.patient_name || '', + doctor: patient.doctor || '', + remark: patient.remark || '', + }); + } + }, [patient]); + + const set = (key) => (e) => setForm((prev) => ({ ...prev, [key]: e.target.value })); + + const handleSubmit = async (e) => { + e.preventDefault(); + if (!form.newName.trim()) return showToast('Nome é obrigatório.', 'error'); + setSaving(true); + try { + await api.put(`/images/patients/${encodeURIComponent(patient.patient_name)}`, form); + showToast('Paciente atualizado com sucesso!', 'success'); + onSaved?.(); + onClose(); + } catch (err) { + showToast(err.response?.data?.error || 'Erro ao salvar.', 'error'); + } finally { + setSaving(false); + } + }; + + return ( + +
+
+ + +
+
+ + +
+
+ +