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}