PDF: Layout horizontal compacto para categorías padre-hijo
All checks were successful
build-and-deploy / build-and-deploy (push) Successful in 1m14s
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:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user