diff --git a/dental-server/dental-client/src/pages/GtosPage.jsx b/dental-server/dental-client/src/pages/GtosPage.jsx
index 242afec..1b65e04 100644
--- a/dental-server/dental-client/src/pages/GtosPage.jsx
+++ b/dental-server/dental-client/src/pages/GtosPage.jsx
@@ -40,12 +40,99 @@ function StatusBadge({ sent, imageCount }) {
}
/* ── Modal de detalhes/edição de GTO ─────────────────────────── */
-function GtoDetailModal({ isOpen, onClose, gto, onUpdated, onOpenImage }) {
+function GtoDetailModal({ isOpen, onClose, gto, onUpdated }) {
const { showToast } = useToast();
const [detail, setDetail] = useState(null);
const [loading, setLoading] = useState(false);
const [marking, setMarking] = useState(false);
+ // Estados para Visualização e Edição de Observação da Imagem
+ const [selectedImage, setSelectedImage] = useState(null);
+ const [isEditingRemark, setIsEditingRemark] = useState(false);
+ const [tempRemark, setTempRemark] = useState('');
+ const [savingRemark, setSavingRemark] = useState(false);
+
+ useEffect(() => {
+ if (selectedImage) {
+ setTempRemark(selectedImage.remark || '');
+ setIsEditingRemark(false);
+ }
+ }, [selectedImage]);
+
+ const handleDownload = async (img) => {
+ try {
+ const url = img.filename?.startsWith('http')
+ ? img.filename
+ : `/uploads/${img.filename}`;
+ const response = await fetch(url);
+ const blob = await response.blob();
+
+ const pName = (img.patient_name || 'paciente').trim().replace(/[\/\\:*?"<>|]/g, '').replace(/\s+/g, '_');
+ const obs = (img.remark || '').trim().replace(/[\/\\:*?"<>|]/g, '').replace(/\s+/g, '_');
+
+ const fileName = obs ? `${pName}-${obs}.jpg` : `${pName}.jpg`;
+
+ const a = document.createElement('a');
+ a.href = URL.createObjectURL(blob);
+ a.download = fileName;
+ document.body.appendChild(a);
+ a.click();
+ a.remove();
+ URL.revokeObjectURL(a.href);
+ } catch (err) {
+ showToast('Erro ao baixar imagem', 'error');
+ }
+ };
+
+ const handleCopy = (img) => {
+ const url = img.filename?.startsWith('http')
+ ? img.filename
+ : `/uploads/${img.filename}`;
+ const imageObj = new Image();
+ imageObj.crossOrigin = 'anonymous';
+ imageObj.onload = () => {
+ try {
+ const canvas = document.createElement('canvas');
+ canvas.width = imageObj.naturalWidth;
+ canvas.height = imageObj.naturalHeight;
+ const ctx = canvas.getContext('2d');
+ ctx.drawImage(imageObj, 0, 0);
+ canvas.toBlob(async (blob) => {
+ try {
+ await navigator.clipboard.write([
+ new ClipboardItem({ 'image/png': blob })
+ ]);
+ showToast('Imagem copiada para a área de transferência!', 'success');
+ } catch (err) {
+ showToast('Erro ao copiar imagem para área de transferência.', 'error');
+ console.error(err);
+ }
+ }, 'image/png');
+ } catch (e) {
+ showToast('Erro ao processar imagem para cópia.', 'error');
+ console.error(e);
+ }
+ };
+ imageObj.onerror = () => showToast('Erro ao carregar imagem para cópia.', 'error');
+ imageObj.src = url;
+ };
+
+ const saveImageRemark = async () => {
+ setSavingRemark(true);
+ try {
+ await api.put(`/images/${selectedImage.id}/remark`, { remark: tempRemark });
+ showToast('Observação atualizada com sucesso!', 'success');
+ setSelectedImage(prev => ({ ...prev, remark: tempRemark }));
+ setIsEditingRemark(false);
+ loadDetail();
+ onUpdated?.();
+ } catch (err) {
+ showToast('Erro ao atualizar observação.', 'error');
+ } finally {
+ setSavingRemark(false);
+ }
+ };
+
useEffect(() => {
if (isOpen && gto?.id) loadDetail();
}, [isOpen, gto?.id]);
@@ -144,18 +231,18 @@ function GtoDetailModal({ isOpen, onClose, gto, onUpdated, onOpenImage }) {
className="preview-img"
style={{ width:'100%', height:90, objectFit:'cover', display:'block', cursor:'pointer' }}
label=""
- onClick={() => onOpenImage?.(img)}
+ onClick={() => setSelectedImage(img)}
/>
onOpenImage?.(img)}
+ onClick={() => setSelectedImage(img)}
>
{img.patient_name || img.filename}