import { useState, useEffect } from 'react'; import Modal from './Modal'; import api from '../api/client'; import { useToast } from '../contexts/ToastContext'; function formatDate(dateString) { if (!dateString) return '—'; return new Date(dateString).toLocaleString('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); } export default function TransformModal({ isOpen, onClose, image, onSaved }) { const { showToast } = useToast(); const [activeView, setActiveView] = useState('transform'); // 'transform' | 'gto' const [showInfo, setShowInfo] = useState(false); const [fineTuneAngle, setFineTuneAngle] = useState(0); const [remark, setRemark] = useState(''); const [saving, setSaving] = useState(false); const [originalUrl, setOriginalUrl] = useState(''); const [gtos, setGtos] = useState([]); const [gtoNumber, setGtoNumber] = useState(''); const [gtoDesc, setGtoDesc] = useState(''); const [loadingGtos, setLoadingGtos] = useState(false); const [resetting, setResetting] = useState(false); // Quando a imagem muda, reinicia estado useEffect(() => { if (!image) return; setFineTuneAngle(0); setRemark(''); setShowInfo(false); setActiveView('transform'); // Carrega URL da imagem original (mãe) se existir if (image.original_image_id) { api.get(`/images/${image.original_image_id}/file-url`) .then(({ data }) => setOriginalUrl(data.url || imageUrl)) .catch(() => setOriginalUrl(imageUrl)); } else { setOriginalUrl(imageUrl); } // Carrega GTOs do paciente loadGtos(image.patient_name); }, [image]); if (!image) return null; const imageUrl = image.filename?.startsWith('http') ? image.filename : `/uploads/${image.filename}`; const loadGtos = async (patientName) => { setLoadingGtos(true); try { const { data } = await api.get(`/gtos?patient=${encodeURIComponent(patientName)}`); setGtos(data || []); } catch { setGtos([]); } finally { setLoadingGtos(false); } }; const adjustAngle = (delta) => { setFineTuneAngle((prev) => prev + delta); }; const resetAngle = () => setFineTuneAngle(0); const saveTransform = async () => { if (fineTuneAngle === 0) { showToast('Nenhuma edição aplicada.', 'error'); return; } setSaving(true); try { await api.post(`/images/${image.id}/transform`, { rotation: fineTuneAngle, flipH: false, flipV: false, remark }); showToast('Orientação salva!', 'success'); onClose(); onSaved?.(); } catch { showToast('Erro ao salvar orientação', 'error'); } finally { setSaving(false); } }; const resetToOriginal = async () => { if (!confirm('Restaurar esta imagem para o estado original? Rotações serão descartadas.')) return; setResetting(true); try { await api.post(`/images/${image.id}/transform`, { rotation: 0, flipH: false, flipV: false, remark: '' }); showToast('Imagem restaurada para o original!', 'success'); onClose(); onSaved?.(); } catch { showToast('Erro ao restaurar a imagem.', 'error'); } finally { setResetting(false); } }; const createGto = async () => { if (!gtoNumber.trim()) { showToast('O Número da GTO é obrigatório.', 'error'); return; } try { await api.post('/gtos', { gto_number: gtoNumber.trim(), description: gtoDesc.trim(), patient_name: image.patient_name }); showToast('GTO criada com sucesso!', 'success'); setGtoNumber(''); setGtoDesc(''); loadGtos(image.patient_name); } catch { showToast('Erro ao criar GTO.', 'error'); } }; const linkToGto = async (gtoId) => { try { await api.post(`/gtos/${gtoId}/images`, { image_id: image.id }); showToast('Imagem vinculada à GTO!', 'success'); } catch (e) { showToast(e.response?.data?.error || 'Erro ao vincular', 'error'); } }; const headerExtras = (
{image.original_image_id && ( )}
); return ( {/* Info extra */} {showInfo && (
👨‍⚕️ Dentista: {image.doctor || 'Não informado'}
📝 Obs: {image.remark || 'Nenhuma observação'}
)} {/* View: Transformação */} {activeView === 'transform' && (
{/* Original */}

Original

Original
{/* Preview (com rotação aplicada) */}

Preview

Preview
{/* Controles de rotação */}

Ajuste

{[ { label: '-5°', delta: -5, icon: '↺' }, { label: '-1°', delta: -1, icon: '↺' }, ].map(({ label, delta, icon }) => ( ))}
{fineTuneAngle > 0 ? '+' : ''}{fineTuneAngle}°
{[ { label: '+1°', delta: 1, icon: '↻' }, { label: '+5°', delta: 5, icon: '↻' }, ].map(({ label, delta, icon }) => ( ))} {fineTuneAngle !== 0 && ( )}
{/* Área de ação */}