diff --git a/src/pages/LaudoManager.jsx b/src/pages/LaudoManager.jsx index fb73c59..d95de17 100644 --- a/src/pages/LaudoManager.jsx +++ b/src/pages/LaudoManager.jsx @@ -1,13 +1,14 @@ +// 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:#f7fbff; border-radius:8px; padding:18px; box-shadow: 0 1px 0 rgba(0,0,0,0.03);} +.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-table { width:100%; border-collapse:collapse; background:#fff; border-radius:8px; overflow:visible; } -.laudo-row { display:flex; padding:14px 12px; align-items:center; border-bottom:1px solid #eef3f8; position:relative; overflow:visible; } +.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; } @@ -15,9 +16,9 @@ const styles = ` .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; } -.modal-backdrop { position:absolute; inset:0; background: rgba(9,20,40,0.45); } -.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; } +.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; } @@ -30,6 +31,9 @@ const styles = ` .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) ===== */ @@ -53,7 +57,7 @@ function mockFetchLaudos() { solicitante: "Sandro Rangel Santos", exame: "US - Mamária Bilateral", conteudo: "RELATÓRIO MÉDICO\n\nAchados: text...", - status: "rascunho" + status: "liberado" }, { id: "LAU-300658301", @@ -63,7 +67,7 @@ function mockFetchLaudos() { solicitante: "Dr. Fulano", exame: "US - Transvaginal", conteudo: "RELATÓRIO MÉDICO\n\nAchados: ...", - status: "rascunho" + status: "entregue" } ]; } @@ -76,12 +80,23 @@ function mockDeleteLaudo(id) { 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; @@ -94,11 +109,8 @@ export default function LaudoManager() { // Fecha dropdown ao clicar fora useEffect(() => { function onDocClick(e) { - // se clicar em um botão de ação (ícone), não fecha if (e.target.closest && e.target.closest('.action-btn')) return; - // se clicar dentro de um dropdown, não fecha if (e.target.closest && e.target.closest('.dropdown')) return; - // caso contrário, fecha qualquer dropdown aberto setOpenDropdownId(null); } document.addEventListener('click', onDocClick); @@ -106,12 +118,30 @@ export default function LaudoManager() { }, []); function toggleDropdown(id, e) { - e.stopPropagation(); // evita que o document click feche imediatamente + 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); } @@ -121,16 +151,18 @@ export default function LaudoManager() { setShowConfirmDelete(true); } - async function confirmDelete(typed) { + async function doConfirmDelete(confirm) { if (!toDelete) return; - if (typed.trim().toUpperCase() !== "EXCLUIR") { - alert("Confirmação inválida. Digite EXCLUIR para confirmar."); + 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); @@ -145,17 +177,18 @@ export default function LaudoManager() { } } - function handlePrint(laudo) { - setViewerLaudo(laudo); - setShowPreview(true); - setOpenDropdownId(null); - } - return (
-
Gerenciamento de Laudo
+
+
Gerenciamento de Laudo
+
Visualização: Secretaria (Somente leitura)
+
+
+ +
+
{laudos.length === 0 ? ( @@ -174,13 +207,13 @@ export default function LaudoManager() {
{l.exame}
{l.solicitante}
+
{l.status}
toggleDropdown(l.id, e)} title="Ações">
- {/* dropdown associado ao laudo (não será cortado) */} {openDropdownId === l.id && (
handleOpenViewer(l)}>Editar
@@ -197,9 +230,9 @@ export default function LaudoManager() { )}
- {/* Viewer modal (modo leitura) */} - {viewerLaudo && !showPreview && ( -
+ {/* Viewer modal (modo leitura) — só abre para quem tem permissão */} + {viewerLaudo && !showPreview && !isSecretary && ( +
setViewerLaudo(null)} />
@@ -211,7 +244,7 @@ export default function LaudoManager() {
- +
@@ -250,16 +283,16 @@ export default function LaudoManager() {
)} - {/* Preview modal */} - {showPreview && viewerLaudo && ( -
-
setShowPreview(false)} /> -
+ {/* Preview modal — agora não bloqueia a tela (sem backdrop escuro), botão imprimir é interativo */} + {showPreview && previewLaudo && ( +
+
+
-
Pré-visualização - {viewerLaudo.paciente.nome}
+
Pré-visualização - {previewLaudo.paciente.nome}
- - + +
@@ -268,28 +301,40 @@ export default function LaudoManager() { RELATÓRIO MÉDICO
- {viewerLaudo.paciente.nome} • Nasc.: {viewerLaudo.paciente.nascimento} • CPF: {viewerLaudo.paciente.cpf} + {previewLaudo.paciente.nome} • Nasc.: {previewLaudo.paciente.nascimento} • CPF: {previewLaudo.paciente.cpf}
- {viewerLaudo.conteudo} + {previewLaudo.conteudo}
)} - {/* Confirm delete modal */} - {showConfirmDelete && toDelete && ( -
-
{ if (!loadingDelete) setShowConfirmDelete(false); }} /> -
-
Excluir laudo
-
Você está prestes a excluir o laudo {toDelete.pedido} - {toDelete.paciente.nome}. Esta ação é irreversível.
+ {/* Notificação simples: Sem permissão (exibe sem backdrop escuro) */} + {showNoPermission && ( +
+
Sem permissão para editar
+
Você está na visualização da secretaria. Edição disponível somente para médicos autorizados.
+
+ +
+
+ )} -
-
Para confirmar, digite EXCLUIR abaixo e clique em Confirmar.
- setShowConfirmDelete(false)} /> + {/* Confirm delete modal (simples: Sim / Não) */} + {showConfirmDelete && toDelete && ( +
+
+
Confirmar exclusão
+
Você tem certeza que quer excluir o laudo {toDelete.pedido} - {toDelete.paciente.nome} ? Esta ação é irreversível.
+ +
+ +
@@ -309,19 +354,4 @@ function computeAge(birth) { const dd = today.getDate(); if (mm < m || (mm === m && dd < d)) age--; return age; -} - -function ConfirmDeleteInput({ onConfirm, onCancel, loading }) { - const [txt, setTxt] = useState(""); - return ( -
- setTxt(e.target.value)} placeholder="Digite EXCLUIR" style={{ width:"100%", padding:"10px", borderRadius:6, border:"1px solid #e1ecfb", marginBottom:8 }} /> -
- - -
-
- ); -} +} \ No newline at end of file