From 4d02b55ce7ae81fb84302e4489f1fc3ae9a57705 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Gustavo?= <166467972+JoaoGustavo-dev@users.noreply.github.com> Date: Fri, 7 Nov 2025 00:21:01 -0300 Subject: [PATCH] fix-report-page --- susconecta/app/laudos/[id]/page.tsx | 138 +++++++++++++++++++++++++++- susconecta/app/paciente/page.tsx | 84 ++++++++++++++++- susconecta/package-lock.json | 6 +- susconecta/package.json | 1 + 4 files changed, 216 insertions(+), 13 deletions(-) diff --git a/susconecta/app/laudos/[id]/page.tsx b/susconecta/app/laudos/[id]/page.tsx index 4677027..2d62bb4 100644 --- a/susconecta/app/laudos/[id]/page.tsx +++ b/susconecta/app/laudos/[id]/page.tsx @@ -96,6 +96,136 @@ export default function LaudoPage() { window.print() } + const handleDownloadPDF = async () => { + if (!report) return + + try { + // Para simplificar, vamos usar jsPDF com html2canvas para capturar o conteúdo + const { jsPDF } = await import('jspdf') + const html2canvas = await import('html2canvas').then((m) => m.default) + + // Criar um elemento temporário com o conteúdo + const element = document.createElement('div') + element.style.position = 'absolute' + element.style.left = '-9999px' + element.style.width = '210mm' // A4 width + element.style.padding = '20mm' + element.style.backgroundColor = 'white' + element.style.fontFamily = 'Arial, sans-serif' + + // Extrair informações + const reportDate = new Date(report.report_date || report.created_at || Date.now()).toLocaleDateString('pt-BR') + const cid = report.cid ?? report.cid_code ?? report.cidCode ?? report.cie ?? '' + const exam = report.exam ?? report.exame ?? report.especialidade ?? report.report_type ?? '' + const diagnosis = report.diagnosis ?? report.diagnostico ?? report.diagnosis_text ?? report.diagnostico_text ?? '' + const conclusion = report.conclusion ?? report.conclusao ?? report.conclusion_text ?? report.conclusao_text ?? '' + const notesText = report.content ?? report.body ?? report.conteudo ?? report.notes ?? report.observacoes ?? '' + + // Extrair nome do médico + let doctorName = '' + if (doctor) { + doctorName = doctor.full_name || doctor.name || doctor.fullName || doctor.doctor_name || '' + } + if (!doctorName) { + const rd = report as any + const tryKeys = [ + 'doctor_name', 'doctor_full_name', 'doctorFullName', 'doctorName', + 'requested_by_name', 'requested_by', 'requester_name', 'requester', + 'created_by_name', 'created_by', 'executante', 'executante_name', + ] + for (const k of tryKeys) { + const v = rd[k] + if (v !== undefined && v !== null && String(v).trim() !== '') { + doctorName = String(v) + break + } + } + } + + // Montar HTML do documento + element.innerHTML = ` +
+

RELATÓRIO MÉDICO

+

Data: ${reportDate}

+ ${doctorName ? `

Profissional: ${doctorName}

` : ''} +
+ +
+
+ ${cid ? `

CID

${cid}

` : ''} + ${exam ? `

EXAME / TIPO

${exam}

` : ''} +
+
+ + ${diagnosis ? ` +
+

DIAGNÓSTICO

+

${diagnosis}

+
+ ` : ''} + + ${conclusion ? ` +
+

CONCLUSÃO

+

${conclusion}

+
+ ` : ''} + + ${notesText ? ` +
+

NOTAS DO PROFISSIONAL

+

${notesText}

+
+ ` : ''} + +
+ Documento gerado em ${new Date().toLocaleString('pt-BR')} +
+ ` + + document.body.appendChild(element) + + // Capturar como canvas + const canvas = await html2canvas(element, { + scale: 2, + useCORS: true, + backgroundColor: '#ffffff', + }) + + document.body.removeChild(element) + + // Converter para PDF + const imgData = canvas.toDataURL('image/png') + const pdf = new jsPDF({ + orientation: 'portrait', + unit: 'mm', + format: 'a4', + }) + + const imgWidth = 210 // A4 width in mm + const pageHeight = 297 // A4 height in mm + const imgHeight = (canvas.height * imgWidth) / canvas.width + let heightLeft = imgHeight + + let position = 0 + + while (heightLeft >= 0) { + pdf.addImage(imgData, 'PNG', 0, position, imgWidth, imgHeight) + heightLeft -= pageHeight + position -= pageHeight + if (heightLeft > 0) { + pdf.addPage() + } + } + + // Download + pdf.save(`laudo-${reportDate}-${doctorName || 'profissional'}.pdf`) + } catch (error) { + console.error('Erro ao gerar PDF:', error) + alert('Erro ao gerar PDF. Tente novamente.') + } + } + if (loading) { return ( @@ -158,7 +288,7 @@ export default function LaudoPage() { : 'bg-gradient-to-br from-slate-50 to-slate-100' }`}> {/* Header Toolbar */} -
{/* Main Content Area */} -
+
{/* Document Container */} -
{/* Document Content */} -
+
{/* Title */}
('') const [remoteMatch, setRemoteMatch] = useState(null) const [searchingRemote, setSearchingRemote] = useState(false) + const [sortOrder, setSortOrder] = useState<'newest' | 'oldest' | 'custom'>('newest') + const [filterDate, setFilterDate] = useState('') - // derived filtered list based on search term + // derived filtered list based on search term and date filters const filteredReports = useMemo(() => { if (!reports || !Array.isArray(reports)) return [] const qRaw = String(searchTerm || '').trim() @@ -980,8 +982,8 @@ export default function PacientePage() { return [remoteMatch] } - if (!q) return reports - return reports.filter((r: any) => { + // Start with all reports or filtered by search + let filtered = !q ? reports : reports.filter((r: any) => { try { const id = r.id ? String(r.id).toLowerCase() : '' const title = String(reportTitle(r) || '').toLowerCase() @@ -1013,8 +1015,38 @@ export default function PacientePage() { return false } }) + + // Apply date filter if specified + if (filterDate) { + const filterDateObj = new Date(filterDate) + filterDateObj.setHours(0, 0, 0, 0) + + filtered = filtered.filter((r: any) => { + const reportDateObj = new Date(r.report_date || r.created_at || Date.now()) + reportDateObj.setHours(0, 0, 0, 0) + return reportDateObj.getTime() === filterDateObj.getTime() + }) + } + + // Apply sorting + const sorted = [...filtered] + if (sortOrder === 'newest') { + sorted.sort((a: any, b: any) => { + const dateA = new Date(a.report_date || a.created_at || 0).getTime() + const dateB = new Date(b.report_date || b.created_at || 0).getTime() + return dateB - dateA // Newest first + }) + } else if (sortOrder === 'oldest') { + sorted.sort((a: any, b: any) => { + const dateA = new Date(a.report_date || a.created_at || 0).getTime() + const dateB = new Date(b.report_date || b.created_at || 0).getTime() + return dateA - dateB // Oldest first + }) + } + + return sorted // eslint-disable-next-line react-hooks/exhaustive-deps - }, [reports, searchTerm, doctorsMap, remoteMatch]) + }, [reports, searchTerm, doctorsMap, remoteMatch, sortOrder, filterDate]) // When the search term looks like an id, attempt a direct fetch using the reports API useEffect(() => { @@ -1404,6 +1436,50 @@ export default function PacientePage() { )}
+ + {/* Date filter and sort controls */} +
+ {/* Sort buttons */} +
+ + +
+ + {/* Date picker */} +
+ { setFilterDate(e.target.value); setReportsPage(1) }} + className="text-xs sm:text-sm px-2 sm:px-3 py-1.5 sm:py-2 border border-border rounded bg-background" + /> + {filterDate && ( + + )} +
+
+ {loadingReports ? (
{strings.carregando}
) : reportsError ? ( diff --git a/susconecta/package-lock.json b/susconecta/package-lock.json index 7e517ab..3111032 100644 --- a/susconecta/package-lock.json +++ b/susconecta/package-lock.json @@ -51,6 +51,7 @@ "embla-carousel-react": "latest", "framer-motion": "^12.23.24", "geist": "^1.3.1", + "html2canvas": "^1.4.1", "input-otp": "latest", "jspdf": "^3.0.3", "lucide-react": "^0.454.0", @@ -4017,7 +4018,6 @@ "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", "license": "MIT", - "optional": true, "engines": { "node": ">= 0.6.0" } @@ -4386,7 +4386,6 @@ "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", "license": "MIT", - "optional": true, "dependencies": { "utrie": "^1.0.2" } @@ -6066,7 +6065,6 @@ "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", "license": "MIT", - "optional": true, "dependencies": { "css-line-break": "^2.1.0", "text-segmentation": "^1.0.3" @@ -8802,7 +8800,6 @@ "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", "license": "MIT", - "optional": true, "dependencies": { "utrie": "^1.0.2" } @@ -9214,7 +9211,6 @@ "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", "license": "MIT", - "optional": true, "dependencies": { "base64-arraybuffer": "^1.0.2" } diff --git a/susconecta/package.json b/susconecta/package.json index e7e4ae7..65a7442 100644 --- a/susconecta/package.json +++ b/susconecta/package.json @@ -53,6 +53,7 @@ "embla-carousel-react": "latest", "framer-motion": "^12.23.24", "geist": "^1.3.1", + "html2canvas": "^1.4.1", "input-otp": "latest", "jspdf": "^3.0.3", "lucide-react": "^0.454.0",