// src/pages/LaudoManager.jsx import React, { useState, useRef, useEffect } from "react"; /* ===== Estilos embutidos ===== */ /* Eu coloquei os estilos aqui para simplificar a edição. */ const styles = ` .laudo-wrap { display:flex; gap:24px; padding:18px; font-family: Inter, Roboto, Arial, sans-serif; } .left-col { width: 100%; max-width: 1160px; background:#ffffff; border-radius:8px; padding:18px; box-shadow: 0 1px 0 rgba(0,0,0,0.03); } /* <-- fundo branco */ .title-row { display:flex; justify-content:space-between; align-items:center; margin-bottom:12px; } .page-title { font-size:20px; color:#2b4a78; font-weight:700; } .laudo-row { display:flex; padding:14px 12px; align-items:center; border-bottom:1px solid #eef3f8; position:relative; overflow:visible; background: transparent; } .col { flex:1; padding:0 8px; font-size:14px; color:#2e3a4b; } .col.small { flex:0 0 90px; text-align:right; } .row-actions { position:relative; flex: 0 0 88px; display:flex; justify-content:flex-end; } .action-btn { background:transparent; border:1px solid #d7e6fb; border-radius:8px; height:40px; width:40px; display:flex; align-items:center; justify-content:center; cursor:pointer; } .dropdown { position:absolute; right:0; top:48px; background:white; border-radius:8px; box-shadow: 0 10px 30px rgba(20,30,50,0.12); min-width:220px; padding:8px 0; z-index:9999; } .dropdown .item { padding:12px 18px; cursor:pointer; font-size:15px; color:#244056; } .dropdown .item:hover { background:#f6fbff; } .viewer-modal, .preview-modal, .confirm-modal { position:fixed; inset:0; display:flex; align-items:center; justify-content:center; z-index:12000; pointer-events:none; } /* deixar pointer-events none para que não bloqueie por padrão */ .modal-backdrop { position:absolute; inset:0; background: rgba(9,20,40,0.45); pointer-events:auto; } /* usado apenas quando necessário */ .modal-card { position:relative; width:92%; max-width:1100px; background:white; border-radius:10px; padding:18px; box-shadow: 0 10px 60px rgba(10,20,40,0.25); max-height:88vh; overflow:auto; pointer-events:auto; } .viewer-header { display:flex; justify-content:space-between; align-items:flex-start; gap:10px; margin-bottom:12px; } .patient-info { font-size:13px; color:#3a556b; } .toolbar { display:flex; gap:8px; align-items:center; flex-wrap:wrap; margin-bottom:12px; } .tool-btn { padding:8px 10px; border-radius:6px; border:1px solid #e6eef8; cursor:pointer; background:#fff; font-size:13px; } .editor-area { border:1px solid #e6eef8; border-radius:8px; padding:14px; min-height:360px; background: #fff; color:#1f2d3d; font-size:15px; line-height:1.5; } .footer-controls { display:flex; justify-content:space-between; align-items:center; margin-top:12px; } .toggle { display:flex; align-items:center; gap:8px; } .btn { padding:8px 12px; border-radius:8px; border:none; cursor:pointer; font-weight:600; } .btn.secondary { background:#eef6ff; color:#2f63a6; border:1px solid #d6e9ff; } .btn.primary { background:#2f63a6; color:white; } .small-muted { color:#7f95a8; font-size:13px; } .empty { padding:40px; text-align:center; color:#7d97b4; } /* notificação simples (sem backdrop escuro) */ .notice-card { position:fixed; top:20vh; left:50%; transform:translateX(-50%); background:#fff; border-radius:8px; padding:14px 18px; box-shadow:0 8px 30px rgba(10,20,40,0.12); z-index:13000; pointer-events:auto; } `; /* ===== Mock data (simula APIDOG) ===== */ function mockFetchLaudos() { return [ { id: "LAU-300551296", pedido: 300551296, data: "29/07/2025", paciente: { nome: "Sarah Mariana Oliveira", cpf: "616.869.070-**", nascimento: "1990-03-25", convenio: "Unimed" }, solicitante: "Sandro Rangel Santos", exame: "US - Abdome Total", conteudo: "RELATÓRIO MÉDICO\n\nAchados: Imagens compatíveis com ...\nConclusão: Órgãos sem alterações significativas.", status: "rascunho" }, { id: "LAU-300659170", pedido: 300659170, data: "29/07/2025", paciente: { nome: "Laissa Helena Marquetti", cpf: "950.684.57-**", nascimento: "1986-09-12", convenio: "Bradesco" }, solicitante: "Sandro Rangel Santos", exame: "US - Mamária Bilateral", conteudo: "RELATÓRIO MÉDICO\n\nAchados: text...", status: "liberado" }, { id: "LAU-300658301", pedido: 300658301, data: "28/07/2025", paciente: { nome: "Vera Lúcia Oliveira Santos", cpf: "928.005.**", nascimento: "1979-02-02", convenio: "Particular" }, solicitante: "Dr. Fulano", exame: "US - Transvaginal", conteudo: "RELATÓRIO MÉDICO\n\nAchados: ...", status: "entregue" } ]; } function mockDeleteLaudo(id) { return new Promise((res) => setTimeout(() => res({ ok: true }), 500)); } /* ===== Componente ===== */ export default function LaudoManager() { const [laudos, setLaudos] = useState([]); const [openDropdownId, setOpenDropdownId] = useState(null); /* viewerLaudo é usado para mostrar o editor/leitura; previewLaudo é usado para a pré-visualização (sem bloquear) */ const [viewerLaudo, setViewerLaudo] = useState(null); const [previewLaudo, setPreviewLaudo] = useState(null); const [showPreview, setShowPreview] = useState(false); const [showConfirmDelete, setShowConfirmDelete] = useState(false); const [toDelete, setToDelete] = useState(null); const [loadingDelete, setLoadingDelete] = useState(false); /* notificação simples (sem backdrop) para 'sem permissão' */ const [showNoPermission, setShowNoPermission] = useState(false); /* Para simplificar: eu assumo aqui que estamos na visão da secretaria */ const isSecretary = true; // eu deixei true para forçar o comportamento "somente leitura" useEffect(() => { const el = document.createElement("style"); el.innerHTML = styles; document.head.appendChild(el); const data = mockFetchLaudos(); setLaudos(data); return () => document.head.removeChild(el); }, []); // Fecha dropdown ao clicar fora useEffect(() => { function onDocClick(e) { if (e.target.closest && e.target.closest('.action-btn')) return; if (e.target.closest && e.target.closest('.dropdown')) return; setOpenDropdownId(null); } document.addEventListener('click', onDocClick); return () => document.removeEventListener('click', onDocClick); }, []); function toggleDropdown(id, e) { e.stopPropagation(); setOpenDropdownId(prev => (prev === id ? null : id)); } /* Quando clicar em Editar: - se for secretaria eu mostro um aviso simples sem fundo escuro - se for médico (isSecretary=false) eu abriria o editor (aqui eu deixei o mecanismo) */ function handleOpenViewer(laudo) { setOpenDropdownId(null); if (isSecretary) { // eu mostro um aviso simples (sem modal que bloqueia) setShowNoPermission(true); return; } setViewerLaudo(laudo); } /* Ao pedir impressão: eu fecho qualquer viewer aberto e abro a preview - a pré-visualização NÃO bloqueia a página (removi backdrop) */ function handlePrint(laudo) { // Evito o bug: fechar viewer antes de abrir preview setViewerLaudo(null); setPreviewLaudo(laudo); setShowPreview(true); setOpenDropdownId(null); } function handleRequestDelete(laudo) { setToDelete(laudo); setOpenDropdownId(null); setShowConfirmDelete(true); } async function doConfirmDelete(confirm) { if (!toDelete) return; if (!confirm) { setShowConfirmDelete(false); setToDelete(null); return; } setLoadingDelete(true); try { const resp = await mockDeleteLaudo(toDelete.id); if (resp.ok || resp === true) { // eu removo o laudo da lista local setLaudos(curr => curr.filter(l => l.id !== toDelete.id)); setShowConfirmDelete(false); setToDelete(null); alert("Laudo excluído com sucesso."); } else { alert("Erro ao excluir. Tente novamente."); } } catch (err) { alert("Erro de rede ao excluir."); } finally { setLoadingDelete(false); } } return (
{line}
))}