PDF: Layout horizontal compacto para categorías padre-hijo
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m14s

- Categorías reestructuradas con jerarquía explícita (padres con hijos)
- Hijos se muestran horizontalmente después del padre con wrap automático
- Fragancia-Aroma y Sabores reducidos a 28mm cada una (más compacto)
- Labels abreviados para subcategorías (F.Deshidr., Az.Morena, etc.)
- Ajustadas posiciones Y de todas las secciones
This commit is contained in:
2025-11-24 17:56:41 -06:00
parent 7c3b9a34b7
commit 9fc1842791
2 changed files with 154 additions and 96 deletions

View File

@@ -10,7 +10,7 @@ import {
PDF_CONFIG,
calcularYInicio,
PARAMETROS_INTENSIDAD,
CATEGORIAS_PDF,
CATEGORIAS_JERARQUICAS,
SENSACIONES_PDF,
GUSTOS_PDF,
DEFECTOS_PDF,
@@ -46,15 +46,16 @@ export function renderizarFormulario(
PDF_CONFIG.lineWidth.thick
)
// Renderizar cada sección con posiciones ajustadas para formulario de 130mm
// Renderizar cada sección con posiciones ajustadas para layout compacto
// Fragancia-Aroma y Sabores: 28mm cada una (layout horizontal)
renderizarEncabezado(doc, xBase, yBase, muestra)
renderizarTablaIntensidades(doc, xBase, yBase + 14, muestra)
renderizarSeccionFraganciaAroma(doc, xBase + 55, yBase + 14, muestra)
renderizarSeccionSabores(doc, xBase + 55, yBase + 49, muestra) // Sabores ahora tiene 34mm igual que Fragancia-Aroma
renderizarSeccionSensacionGustos(doc, xBase, yBase + 84, muestra)
renderizarSeccionTazasDefectos(doc, xBase, yBase + 100, muestra)
renderizarSeccionOtrasNotas(doc, xBase, yBase + 116, muestra)
renderizarSeccionSubTotal(doc, xBase, yBase + 124, muestra)
renderizarSeccionFraganciaAroma(doc, xBase + 55, yBase + 14, muestra) // 28mm
renderizarSeccionSabores(doc, xBase + 55, yBase + 42, muestra) // 28mm (14+28=42)
renderizarSeccionSensacionGustos(doc, xBase, yBase + 70, muestra) // Después de Sabores (42+28=70)
renderizarSeccionTazasDefectos(doc, xBase, yBase + 86, muestra) // 70+16=86
renderizarSeccionOtrasNotas(doc, xBase, yBase + 102, muestra) // 86+16=102
renderizarSeccionSubTotal(doc, xBase, yBase + 112, muestra) // 102+10=112
}
/**
@@ -175,7 +176,7 @@ function renderizarTablaIntensidades(
}
/**
* Renderiza la sección de Fragancia-Aroma
* Renderiza la sección de Fragancia-Aroma con layout horizontal para hijos
*/
function renderizarSeccionFraganciaAroma(
doc: jsPDF,
@@ -184,7 +185,7 @@ function renderizarSeccionFraganciaAroma(
muestra: Muestra
): void {
const width = 144.9 // Ancho total de la sección derecha
const sectionHeight = 34
const sectionHeight = 28 // Más compacto con layout horizontal
// Borde de la sección
dibujarRectangulo(doc, x, y, width, sectionHeight, PDF_CONFIG.lineWidth.normal)
@@ -201,31 +202,15 @@ function renderizarSeccionFraganciaAroma(
const categoriasSeleccionadas = muestra.fraganciaAromaNotas.categorias
const subcategoriasSeleccionadas = muestra.fraganciaAromaNotas.subcategorias
// Espaciado entre checkboxes (compacto)
const checkSpacing = 2.6
const startY = y + 8
// Columna izquierda de checkboxes
let checkY = startY
CATEGORIAS_PDF.columnaIzquierda.forEach((cat) => {
const isChecked =
categoriasSeleccionadas.includes(cat.key as any) ||
subcategoriasSeleccionadas.includes(cat.key)
dibujarCheckboxConLabel(doc, x + 3, checkY, cat.label, isChecked, cat.indent)
checkY += checkSpacing
})
// Columna derecha de checkboxes
checkY = startY
CATEGORIAS_PDF.columnaDerecha.forEach((cat) => {
const isChecked =
categoriasSeleccionadas.includes(cat.key as any) ||
subcategoriasSeleccionadas.includes(cat.key)
dibujarCheckboxConLabel(doc, x + 75, checkY, cat.label, isChecked, cat.indent)
checkY += checkSpacing
})
// Renderizar categorías con layout horizontal
renderizarCategoriasHorizontal(
doc,
x + 2,
y + 7.5,
width - 4,
categoriasSeleccionadas,
subcategoriasSeleccionadas
)
// Notas específica (sin recuadro, solo texto)
doc.setFontSize(PDF_CONFIG.fontSize.tiny)
@@ -245,7 +230,70 @@ function renderizarSeccionFraganciaAroma(
}
/**
* Renderiza la sección de Sabores (exactamente igual a Fragancia-Aroma)
* Renderiza categorías con hijos en layout horizontal con wrap
*/
function renderizarCategoriasHorizontal(
doc: jsPDF,
x: number,
y: number,
maxWidth: number,
categoriasSeleccionadas: string[],
subcategoriasSeleccionadas: string[]
): void {
const checkboxSize = 2.5 // Checkbox más pequeño
const rowHeight = 3.8 // Altura de fila
const parentSpacing = 2 // Espacio después de cada grupo padre+hijos
const childIndent = 3 // Indentación para hijos en wrap
let currentX = x
let currentY = y
CATEGORIAS_JERARQUICAS.forEach((cat) => {
const isParentChecked = categoriasSeleccionadas.includes(cat.key as any)
// Calcular ancho necesario para el padre
doc.setFontSize(PDF_CONFIG.fontSize.tiny)
const parentWidth = checkboxSize + 1 + doc.getTextWidth(cat.label)
// Si no cabe en esta línea, ir a la siguiente
if (currentX + parentWidth > x + maxWidth && currentX > x) {
currentX = x
currentY += rowHeight
}
// Dibujar checkbox del padre
dibujarCheckbox(doc, currentX, currentY, isParentChecked, checkboxSize)
doc.setFont('helvetica', 'bold')
doc.text(cat.label, currentX + checkboxSize + 1, currentY + 2)
currentX += parentWidth + 1
// Si tiene hijos, renderizarlos horizontalmente
if (cat.hijos && cat.hijos.length > 0) {
doc.setFont('helvetica', 'normal')
cat.hijos.forEach((hijo) => {
const isChildChecked = subcategoriasSeleccionadas.includes(hijo.key)
const childWidth = checkboxSize + 1 + doc.getTextWidth(hijo.label)
// Si no cabe, wrap a siguiente línea con indentación
if (currentX + childWidth > x + maxWidth) {
currentX = x + childIndent
currentY += rowHeight
}
dibujarCheckbox(doc, currentX, currentY, isChildChecked, checkboxSize)
doc.text(hijo.label, currentX + checkboxSize + 1, currentY + 2)
currentX += childWidth + 1
})
}
// Añadir espacio después del grupo
currentX += parentSpacing
})
}
/**
* Renderiza la sección de Sabores (mismo layout horizontal que Fragancia-Aroma)
*/
function renderizarSeccionSabores(
doc: jsPDF,
@@ -254,7 +302,7 @@ function renderizarSeccionSabores(
muestra: Muestra
): void {
const width = 144.9
const sectionHeight = 34 // Mismo tamaño que Fragancia-Aroma
const sectionHeight = 28 // Mismo tamaño compacto que Fragancia-Aroma
// Borde de la sección
dibujarRectangulo(doc, x, y, width, sectionHeight, PDF_CONFIG.lineWidth.normal)
@@ -271,31 +319,15 @@ function renderizarSeccionSabores(
const categoriasSeleccionadas = muestra.saborNotas.categorias
const subcategoriasSeleccionadas = muestra.saborNotas.subcategorias
// Espaciado entre checkboxes (compacto, igual que Fragancia-Aroma)
const checkSpacing = 2.6
const startY = y + 8
// Columna izquierda de checkboxes (mismas categorías que Fragancia-Aroma)
let checkY = startY
CATEGORIAS_PDF.columnaIzquierda.forEach((cat) => {
const isChecked =
categoriasSeleccionadas.includes(cat.key as any) ||
subcategoriasSeleccionadas.includes(cat.key)
dibujarCheckboxConLabel(doc, x + 3, checkY, cat.label, isChecked, cat.indent)
checkY += checkSpacing
})
// Columna derecha de checkboxes
checkY = startY
CATEGORIAS_PDF.columnaDerecha.forEach((cat) => {
const isChecked =
categoriasSeleccionadas.includes(cat.key as any) ||
subcategoriasSeleccionadas.includes(cat.key)
dibujarCheckboxConLabel(doc, x + 75, checkY, cat.label, isChecked, cat.indent)
checkY += checkSpacing
})
// Renderizar categorías con layout horizontal (igual que Fragancia-Aroma)
renderizarCategoriasHorizontal(
doc,
x + 2,
y + 7.5,
width - 4,
categoriasSeleccionadas,
subcategoriasSeleccionadas
)
// Notas específica (sin recuadro, solo texto)
doc.setFontSize(PDF_CONFIG.fontSize.tiny)

View File

@@ -147,48 +147,74 @@ export const PARAMETROS_INTENSIDAD = [
] as const
/**
* Tipo para categoría de nota en PDF
* Tipo para subcategoría (hijo)
*/
export interface CategoriaPdf {
export interface SubcategoriaPdf {
key: string
label: string
indent?: boolean
}
/**
* Tipo para categoría padre con hijos opcionales
*/
export interface CategoriaConHijosPdf {
key: string
label: string
hijos?: SubcategoriaPdf[]
}
/**
* Categorías de notas para el formulario PDF
* Distribución correcta según formulario físico EVC-IH01
* Estructura jerárquica: padres con hijos que se renderizan horizontalmente
*/
export const CATEGORIAS_PDF: {
columnaIzquierda: CategoriaPdf[]
columnaDerecha: CategoriaPdf[]
} = {
columnaIzquierda: [
{ key: 'Floral', label: 'Floral' },
{ key: 'Afrutado', label: 'Afrutado' },
{ key: 'Bayas', label: 'Bayas', indent: true },
{ key: 'Frutas Deshidratadas', label: 'Frutas Deshidratadas', indent: true },
{ key: 'Cítricos', label: 'Cítricos', indent: true },
{ key: 'Verde Vegetal', label: 'Verde/Vegetal' },
{ key: 'Otro', label: 'Otra' },
{ key: 'Químico', label: 'Químico', indent: true },
{ key: 'Humedad/Tierra', label: 'Humedad/Tierra', indent: true },
{ key: 'Madera', label: 'Madera', indent: true },
],
columnaDerecha: [
{ key: 'Tostado', label: 'Tostado' },
{ key: 'Cereal', label: 'Cereal', indent: true },
{ key: 'Quemado', label: 'Quemado', indent: true },
{ key: 'Tabaco', label: 'Tabaco', indent: true },
{ key: 'Nueces/Cacao', label: 'Nueces/Cacao' },
{ key: 'Nueces', label: 'Nueces', indent: true },
{ key: 'Cacao', label: 'Cacao', indent: true },
{ key: 'Especias', label: 'Especias' },
{ key: 'Dulce', label: 'Dulce' },
{ key: 'Vainilla', label: 'Vainilla', indent: true },
{ key: 'Azúcar Morena', label: 'Azúcar Morena', indent: true },
],
}
export const CATEGORIAS_JERARQUICAS: CategoriaConHijosPdf[] = [
{ key: 'Floral', label: 'Floral' },
{
key: 'Afrutado',
label: 'Afrutado',
hijos: [
{ key: 'Bayas', label: 'Bayas' },
{ key: 'Frutas Deshidratadas', label: 'F.Deshidr.' },
{ key: 'Cítricos', label: 'Cítricos' },
],
},
{ key: 'Verde Vegetal', label: 'Verde/Vegetal' },
{
key: 'Otro',
label: 'Otra',
hijos: [
{ key: 'Químico', label: 'Químico' },
{ key: 'Humedad/Tierra', label: 'Humedad' },
{ key: 'Madera', label: 'Madera' },
],
},
{
key: 'Tostado',
label: 'Tostado',
hijos: [
{ key: 'Cereal', label: 'Cereal' },
{ key: 'Quemado', label: 'Quemado' },
{ key: 'Tabaco', label: 'Tabaco' },
],
},
{
key: 'Nueces/Cacao',
label: 'Nueces/Cacao',
hijos: [
{ key: 'Nueces', label: 'Nueces' },
{ key: 'Cacao', label: 'Cacao' },
],
},
{ key: 'Especias', label: 'Especias' },
{
key: 'Dulce',
label: 'Dulce',
hijos: [
{ key: 'Vainilla', label: 'Vainilla' },
{ key: 'Azúcar Morena', label: 'Az.Morena' },
],
},
]
/**
* Sensaciones en boca para el formulario PDF