fix(details): corrige erro de id null (RETURNING id) e implementa navegação baseada em rotas (/patient/:patientName)
continuous-integration/webhook Deploy concluído (VPS4)

This commit is contained in:
VPS 4 Deploy Agent
2026-06-01 02:45:06 +02:00
parent bc457b0d51
commit 2fd4ab91d4
4 changed files with 21 additions and 15 deletions
+6
View File
@@ -34,6 +34,12 @@ export default function App() {
</ProtectedRoute> </ProtectedRoute>
} /> } />
<Route path="/patient/:patientName" element={
<ProtectedRoute>
<DashboardPage />
</ProtectedRoute>
} />
{/* Rotas de administrador */} {/* Rotas de administrador */}
<Route path="/clients" element={ <Route path="/clients" element={
<AdminRoute> <AdminRoute>
@@ -1,5 +1,5 @@
import { useState, useEffect, useCallback, useRef } from 'react'; import { useState, useEffect, useCallback, useRef } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom'; import { useNavigate, useSearchParams, useParams } from 'react-router-dom';
import api from '../api/client'; import api from '../api/client';
import { useSocket } from '../contexts/SocketContext'; import { useSocket } from '../contexts/SocketContext';
import { useToast } from '../contexts/ToastContext'; import { useToast } from '../contexts/ToastContext';
@@ -33,6 +33,7 @@ export default function DashboardPage() {
const { on, off } = useSocket(); const { on, off } = useSocket();
const navigate = useNavigate(); const navigate = useNavigate();
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const { patientName: urlPatientName } = useParams();
// View state: 'patients' | 'patient-images' // View state: 'patients' | 'patient-images'
const [view, setView] = useState('patients'); const [view, setView] = useState('patients');
@@ -95,11 +96,15 @@ export default function DashboardPage() {
}, 300); }, 300);
}, [searchParams]); }, [searchParams]);
// ── URL query patient (abre a pasta de RX do paciente se informado) ── // ── URL path patient (abre a pasta de RX do paciente se informado no path) ──
useEffect(() => { useEffect(() => {
const patientName = searchParams.get('patient'); if (!urlPatientName) {
if (!patientName) return; setView('patients');
setSelectedPatient(null);
return;
}
const patientName = decodeURIComponent(urlPatientName);
const loadQueryPatient = async () => { const loadQueryPatient = async () => {
try { try {
const { data } = await api.get(`/images/patients?search=${encodeURIComponent(patientName)}&limit=10`); const { data } = await api.get(`/images/patients?search=${encodeURIComponent(patientName)}&limit=10`);
@@ -123,8 +128,7 @@ export default function DashboardPage() {
}; };
loadQueryPatient(); loadQueryPatient();
navigate('/', { replace: true }); }, [urlPatientName]);
}, [searchParams]);
// ── Socket.IO: nova imagem em tempo real ────────────────────────── // ── Socket.IO: nova imagem em tempo real ──────────────────────────
useEffect(() => { useEffect(() => {
@@ -228,15 +232,11 @@ export default function DashboardPage() {
}; };
const openPatient = (patient) => { const openPatient = (patient) => {
setSelectedPatient(patient); navigate('/patient/' + encodeURIComponent(patient.patient_name));
setView('patient-images');
loadPatientImages(patient.patient_name);
}; };
const goBack = () => { const goBack = () => {
setView('patients'); navigate('/');
setSelectedPatient(null);
loadPatients();
}; };
// ── Toggle image enabled ────────────────────────────────────────── // ── Toggle image enabled ──────────────────────────────────────────
@@ -183,7 +183,7 @@ export default function ImageDetailsPage() {
rotation: 0, flipH: false, flipV: false, remark: '' rotation: 0, flipH: false, flipV: false, remark: ''
}); });
showToast('Imagem restaurada para o original!', 'success'); showToast('Imagem restaurada para o original!', 'success');
navigate('/?patient=' + encodeURIComponent(patientName)); navigate('/patient/' + encodeURIComponent(patientName));
} catch { } catch {
showToast('Erro ao restaurar a imagem.', 'error'); showToast('Erro ao restaurar a imagem.', 'error');
} finally { } finally {
@@ -337,7 +337,7 @@ export default function ImageDetailsPage() {
{/* Top Header para navegação / Voltar */} {/* Top Header para navegação / Voltar */}
<header className="header" style={{ display: 'flex', flexDirection: 'column', gap: 12 }}> <header className="header" style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 12, width: '100%' }}> <div style={{ display: 'flex', alignItems: 'center', gap: 12, width: '100%' }}>
<button className="btn btn-secondary" onClick={() => navigate('/?patient=' + encodeURIComponent(patientName))} style={{ padding: '8px 12px' }} title="Voltar ao Painel"> <button className="btn btn-secondary" onClick={() => navigate('/patient/' + encodeURIComponent(patientName))} style={{ padding: '8px 12px' }} title="Voltar ao Painel">
{isMobile ? '←' : '← Voltar'} {isMobile ? '←' : '← Voltar'}
</button> </button>
<div style={{ flex: 1, minWidth: 0 }}> <div style={{ flex: 1, minWidth: 0 }}>
+1 -1
View File
@@ -403,7 +403,7 @@ router.post('/:id/transform', async (req, res) => {
`INSERT INTO images ( `INSERT INTO images (
image_guid, patient_name, clinic_name, client_name, original_filename, filename, image_guid, patient_name, clinic_name, client_name, original_filename, filename,
enabled, rotation, flip_horizontal, flip_vertical, original_image_id, created_at, remark, doctor enabled, rotation, flip_horizontal, flip_vertical, original_image_id, created_at, remark, doctor
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14)`, ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14) RETURNING id`,
[ [
`${realOriginal.image_guid}_${timestamp}`, `${realOriginal.image_guid}_${timestamp}`,
realOriginal.patient_name, realOriginal.patient_name,