Fix: PDF generado dinámicamente con diseño congruente a la app

This commit is contained in:
Santiago Lavaise
2026-04-13 20:39:29 -03:00
parent b5a1d4e4d3
commit b3d29bd61b
2 changed files with 115 additions and 20 deletions
Binary file not shown.
+115 -20
View File
@@ -112,6 +112,25 @@ function calcularEstadoLaboratorio(parametro: string, valor: string): 'Normal' |
return 'Normal'; return 'Normal';
} }
function wrapText(text: string, font: any, fontSize: number, maxWidth: number): string[] {
const words = text.split(' ');
const lines: string[] = [];
let currentLine = '';
for (const word of words) {
const testLine = currentLine ? `${currentLine} ${word}` : word;
const testWidth = font.widthOfTextAtSize(testLine, fontSize);
if (testWidth > maxWidth && currentLine) {
lines.push(currentLine);
currentLine = word;
} else {
currentLine = testLine;
}
}
if (currentLine) lines.push(currentLine);
return lines;
}
export function HistoriaClinica({ export function HistoriaClinica({
internacion, internacion,
paciente, paciente,
@@ -311,28 +330,104 @@ export function HistoriaClinica({
<Button variant="outline" className="bg-blue-50 border-blue-300 text-blue-700 hover:bg-blue-100" onClick={async (e) => { <Button variant="outline" className="bg-blue-50 border-blue-300 text-blue-700 hover:bg-blue-100" onClick={async (e) => {
e.stopPropagation(); e.stopPropagation();
try { try {
const existingPdfBytes = await fetch('/MODELO_INGRESO.pdf').then(res => res.arrayBuffer()); const { PDFDocument, StandardFonts, rgb } = await import('pdf-lib');
const pdfDoc = await PDFDocument.load(existingPdfBytes); const pdfDoc = await PDFDocument.create();
const form = pdfDoc.getForm(); const page = pdfDoc.addPage([595, 842]);
const { width, height } = page.getSize();
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
const fontBold = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
const colorPrimary = rgb(0.18, 0.34, 0.55);
const colorText = rgb(0.13, 0.13, 0.13);
const colorGray = rgb(0.45, 0.45, 0.45);
const colorLightGray = rgb(0.92, 0.92, 0.92);
const edad = paciente.fechaNacimiento ? calcularEdad(paciente.fechaNacimiento) : ''; const edad = paciente.fechaNacimiento ? calcularEdad(paciente.fechaNacimiento) : '';
const fields = [ const fechaFormateada = formatDateDDMMYYYY(internacion.fechaIngreso);
{ name: 'cama', value: String(cama?.numero || '') },
{ name: 'apellido', value: String(paciente.apellido || '') }, // Header
{ name: 'nombre', value: String(paciente.nombre || '') }, page.drawRectangle({ x: 0, y: height - 80, width, height: 80, color: colorPrimary });
{ name: 'edad', value: String(edad) }, page.drawText('FORMULARIO DE INGRESO', { x: 180, y: height - 45, size: 20, font: fontBold, color: rgb(1, 1, 1) });
{ name: 'dni', value: String(paciente.dni || '') }, page.drawText('Historia Clínica Electrónica - Servicio de Clínica Médica', { x: 110, y: height - 65, size: 10, font, color: rgb(0.85, 0.85, 0.85) });
{ name: 'fechaIngreso', value: String(internacion.fechaIngreso || '') }, page.drawText('Hospital Santojanni', { x: 50, y: height - 90, size: 9, font, color: colorGray });
{ name: 'motivoConsulta', value: String(internacion.motivoConsulta || '') },
{ name: 'enfermedadActual', value: String(internacion.enfermedadActual || '') }, let y = height - 125;
{ name: 'antecedentes', value: String(internacion.antecedentesEnfermedadActual || '') },
{ name: 'diagnostico', value: String(internacion.diagnosticoIngreso || '') }, // Datos del Paciente
]; page.drawRectangle({ x: 45, y: y - 8, width: 505, height: 24, color: colorLightGray });
for (const field of fields) { page.drawText('DATOS DEL PACIENTE', { x: 50, y: y, size: 12, font: fontBold, color: colorPrimary });
try { y -= 35;
const formField = form.getTextField(field.name);
if (formField) formField.setText(field.value); page.drawText('Cama:', { x: 50, y, size: 10, font: fontBold, color: colorGray });
} catch (e) { } page.drawText(cama?.numero || 'N/A', { x: 120, y, size: 10, font, color: colorText });
page.drawText('Fecha de Ingreso:', { x: 280, y, size: 10, font: fontBold, color: colorGray });
page.drawText(fechaFormateada, { x: 390, y, size: 10, font, color: colorText });
y -= 25;
page.drawText('Apellido:', { x: 50, y, size: 10, font: fontBold, color: colorGray });
page.drawText(paciente.apellido || '', { x: 120, y, size: 10, font, color: colorText });
page.drawText('Nombre:', { x: 280, y, size: 10, font: fontBold, color: colorGray });
page.drawText(paciente.nombre || '', { x: 350, y, size: 10, font, color: colorText });
y -= 25;
page.drawText('DNI:', { x: 50, y, size: 10, font: fontBold, color: colorGray });
page.drawText(paciente.dni || '', { x: 120, y, size: 10, font, color: colorText });
page.drawText('Edad:', { x: 280, y, size: 10, font: fontBold, color: colorGray });
page.drawText(edad ? `${edad} años` : '', { x: 330, y, size: 10, font, color: colorText });
y -= 40;
// Datos Clínicos
page.drawRectangle({ x: 45, y: y - 8, width: 505, height: 24, color: colorLightGray });
page.drawText('DATOS CLÍNICOS', { x: 50, y: y, size: 12, font: fontBold, color: colorPrimary });
y -= 35;
page.drawText('Motivo de Consulta:', { x: 50, y, size: 10, font: fontBold, color: colorGray });
y -= 18;
const motivoLines = wrapText(internacion.motivoConsulta || 'Sin información', font, 10, 480);
for (const line of motivoLines.slice(0, 4)) {
page.drawText(line, { x: 50, y, size: 10, font, color: colorText });
y -= 14;
} }
y -= 10;
if (motivoLines.length > 4) y -= (motivoLines.length - 4) * 14;
page.drawText('Enfermedad Actual:', { x: 50, y, size: 10, font: fontBold, color: colorGray });
y -= 18;
const enfermedadLines = wrapText(internacion.enfermedadActual || 'Sin información', font, 10, 480);
for (const line of enfermedadLines.slice(0, 4)) {
page.drawText(line, { x: 50, y, size: 10, font, color: colorText });
y -= 14;
}
y -= 10;
if (enfermedadLines.length > 4) y -= (enfermedadLines.length - 4) * 14;
page.drawText('Antecedentes de la Enfermedad Actual:', { x: 50, y, size: 10, font: fontBold, color: colorGray });
y -= 18;
const antecedentesLines = wrapText(internacion.antecedentesEnfermedadActual || 'Sin antecedentes registrados', font, 10, 480);
for (const line of antecedentesLines.slice(0, 4)) {
page.drawText(line, { x: 50, y, size: 10, font, color: colorText });
y -= 14;
}
y -= 10;
if (antecedentesLines.length > 4) y -= (antecedentesLines.length - 4) * 14;
page.drawText('Diagnóstico de Ingreso:', { x: 50, y, size: 10, font: fontBold, color: colorGray });
y -= 18;
const diagnosticoLines = wrapText(internacion.diagnosticoIngreso || 'Sin diagnóstico', font, 10, 480);
for (const line of diagnosticoLines.slice(0, 3)) {
page.drawText(line, { x: 50, y, size: 10, font, color: colorText });
y -= 14;
}
// Footer
y -= 20;
page.drawLine({ start: { x: 50, y }, end: { x: 250, y }, thickness: 0.5, color: colorGray });
page.drawText('Firma del Médico', { x: 50, y: y - 15, size: 9, font, color: colorGray });
page.drawLine({ start: { x: 320, y }, end: { x: 520, y }, thickness: 0.5, color: colorGray });
page.drawText('Aclaración / Sello', { x: 320, y: y - 15, size: 9, font, color: colorGray });
page.drawText('v1.0.0', { x: width - 60, y: 30, size: 8, font, color: colorGray });
const pdfBytes = await pdfDoc.save(); const pdfBytes = await pdfDoc.save();
const blob = new Blob([new Uint8Array(pdfBytes)], { type: 'application/pdf' }); const blob = new Blob([new Uint8Array(pdfBytes)], { type: 'application/pdf' });
const url = URL.createObjectURL(blob); const url = URL.createObjectURL(blob);