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,
|
PDF_CONFIG,
|
||||||
calcularYInicio,
|
calcularYInicio,
|
||||||
PARAMETROS_INTENSIDAD,
|
PARAMETROS_INTENSIDAD,
|
||||||
CATEGORIAS_PDF,
|
CATEGORIAS_JERARQUICAS,
|
||||||
SENSACIONES_PDF,
|
SENSACIONES_PDF,
|
||||||
GUSTOS_PDF,
|
GUSTOS_PDF,
|
||||||
DEFECTOS_PDF,
|
DEFECTOS_PDF,
|
||||||
@@ -46,15 +46,16 @@ export function renderizarFormulario(
|
|||||||
PDF_CONFIG.lineWidth.thick
|
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)
|
renderizarEncabezado(doc, xBase, yBase, muestra)
|
||||||
renderizarTablaIntensidades(doc, xBase, yBase + 14, muestra)
|
renderizarTablaIntensidades(doc, xBase, yBase + 14, muestra)
|
||||||
renderizarSeccionFraganciaAroma(doc, xBase + 55, yBase + 14, muestra)
|
renderizarSeccionFraganciaAroma(doc, xBase + 55, yBase + 14, muestra) // 28mm
|
||||||
renderizarSeccionSabores(doc, xBase + 55, yBase + 49, muestra) // Sabores ahora tiene 34mm igual que Fragancia-Aroma
|
renderizarSeccionSabores(doc, xBase + 55, yBase + 42, muestra) // 28mm (14+28=42)
|
||||||
renderizarSeccionSensacionGustos(doc, xBase, yBase + 84, muestra)
|
renderizarSeccionSensacionGustos(doc, xBase, yBase + 70, muestra) // Después de Sabores (42+28=70)
|
||||||
renderizarSeccionTazasDefectos(doc, xBase, yBase + 100, muestra)
|
renderizarSeccionTazasDefectos(doc, xBase, yBase + 86, muestra) // 70+16=86
|
||||||
renderizarSeccionOtrasNotas(doc, xBase, yBase + 116, muestra)
|
renderizarSeccionOtrasNotas(doc, xBase, yBase + 102, muestra) // 86+16=102
|
||||||
renderizarSeccionSubTotal(doc, xBase, yBase + 124, muestra)
|
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(
|
function renderizarSeccionFraganciaAroma(
|
||||||
doc: jsPDF,
|
doc: jsPDF,
|
||||||
@@ -184,7 +185,7 @@ function renderizarSeccionFraganciaAroma(
|
|||||||
muestra: Muestra
|
muestra: Muestra
|
||||||
): void {
|
): void {
|
||||||
const width = 144.9 // Ancho total de la sección derecha
|
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
|
// Borde de la sección
|
||||||
dibujarRectangulo(doc, x, y, width, sectionHeight, PDF_CONFIG.lineWidth.normal)
|
dibujarRectangulo(doc, x, y, width, sectionHeight, PDF_CONFIG.lineWidth.normal)
|
||||||
@@ -201,31 +202,15 @@ function renderizarSeccionFraganciaAroma(
|
|||||||
const categoriasSeleccionadas = muestra.fraganciaAromaNotas.categorias
|
const categoriasSeleccionadas = muestra.fraganciaAromaNotas.categorias
|
||||||
const subcategoriasSeleccionadas = muestra.fraganciaAromaNotas.subcategorias
|
const subcategoriasSeleccionadas = muestra.fraganciaAromaNotas.subcategorias
|
||||||
|
|
||||||
// Espaciado entre checkboxes (compacto)
|
// Renderizar categorías con layout horizontal
|
||||||
const checkSpacing = 2.6
|
renderizarCategoriasHorizontal(
|
||||||
const startY = y + 8
|
doc,
|
||||||
|
x + 2,
|
||||||
// Columna izquierda de checkboxes
|
y + 7.5,
|
||||||
let checkY = startY
|
width - 4,
|
||||||
CATEGORIAS_PDF.columnaIzquierda.forEach((cat) => {
|
categoriasSeleccionadas,
|
||||||
const isChecked =
|
subcategoriasSeleccionadas
|
||||||
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
|
|
||||||
})
|
|
||||||
|
|
||||||
// Notas específica (sin recuadro, solo texto)
|
// Notas específica (sin recuadro, solo texto)
|
||||||
doc.setFontSize(PDF_CONFIG.fontSize.tiny)
|
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(
|
function renderizarSeccionSabores(
|
||||||
doc: jsPDF,
|
doc: jsPDF,
|
||||||
@@ -254,7 +302,7 @@ function renderizarSeccionSabores(
|
|||||||
muestra: Muestra
|
muestra: Muestra
|
||||||
): void {
|
): void {
|
||||||
const width = 144.9
|
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
|
// Borde de la sección
|
||||||
dibujarRectangulo(doc, x, y, width, sectionHeight, PDF_CONFIG.lineWidth.normal)
|
dibujarRectangulo(doc, x, y, width, sectionHeight, PDF_CONFIG.lineWidth.normal)
|
||||||
@@ -271,31 +319,15 @@ function renderizarSeccionSabores(
|
|||||||
const categoriasSeleccionadas = muestra.saborNotas.categorias
|
const categoriasSeleccionadas = muestra.saborNotas.categorias
|
||||||
const subcategoriasSeleccionadas = muestra.saborNotas.subcategorias
|
const subcategoriasSeleccionadas = muestra.saborNotas.subcategorias
|
||||||
|
|
||||||
// Espaciado entre checkboxes (compacto, igual que Fragancia-Aroma)
|
// Renderizar categorías con layout horizontal (igual que Fragancia-Aroma)
|
||||||
const checkSpacing = 2.6
|
renderizarCategoriasHorizontal(
|
||||||
const startY = y + 8
|
doc,
|
||||||
|
x + 2,
|
||||||
// Columna izquierda de checkboxes (mismas categorías que Fragancia-Aroma)
|
y + 7.5,
|
||||||
let checkY = startY
|
width - 4,
|
||||||
CATEGORIAS_PDF.columnaIzquierda.forEach((cat) => {
|
categoriasSeleccionadas,
|
||||||
const isChecked =
|
subcategoriasSeleccionadas
|
||||||
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
|
|
||||||
})
|
|
||||||
|
|
||||||
// Notas específica (sin recuadro, solo texto)
|
// Notas específica (sin recuadro, solo texto)
|
||||||
doc.setFontSize(PDF_CONFIG.fontSize.tiny)
|
doc.setFontSize(PDF_CONFIG.fontSize.tiny)
|
||||||
|
|||||||
@@ -147,48 +147,74 @@ export const PARAMETROS_INTENSIDAD = [
|
|||||||
] as const
|
] as const
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tipo para categoría de nota en PDF
|
* Tipo para subcategoría (hijo)
|
||||||
*/
|
*/
|
||||||
export interface CategoriaPdf {
|
export interface SubcategoriaPdf {
|
||||||
key: string
|
key: string
|
||||||
label: 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
|
* 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: {
|
export const CATEGORIAS_JERARQUICAS: CategoriaConHijosPdf[] = [
|
||||||
columnaIzquierda: CategoriaPdf[]
|
{ key: 'Floral', label: 'Floral' },
|
||||||
columnaDerecha: CategoriaPdf[]
|
{
|
||||||
} = {
|
key: 'Afrutado',
|
||||||
columnaIzquierda: [
|
label: 'Afrutado',
|
||||||
{ key: 'Floral', label: 'Floral' },
|
hijos: [
|
||||||
{ key: 'Afrutado', label: 'Afrutado' },
|
{ key: 'Bayas', label: 'Bayas' },
|
||||||
{ key: 'Bayas', label: 'Bayas', indent: true },
|
{ key: 'Frutas Deshidratadas', label: 'F.Deshidr.' },
|
||||||
{ key: 'Frutas Deshidratadas', label: 'Frutas Deshidratadas', indent: true },
|
{ key: 'Cítricos', label: 'Cítricos' },
|
||||||
{ key: 'Cítricos', label: 'Cítricos', indent: true },
|
],
|
||||||
{ key: 'Verde Vegetal', label: 'Verde/Vegetal' },
|
},
|
||||||
{ key: 'Otro', label: 'Otra' },
|
{ key: 'Verde Vegetal', label: 'Verde/Vegetal' },
|
||||||
{ key: 'Químico', label: 'Químico', indent: true },
|
{
|
||||||
{ key: 'Humedad/Tierra', label: 'Humedad/Tierra', indent: true },
|
key: 'Otro',
|
||||||
{ key: 'Madera', label: 'Madera', indent: true },
|
label: 'Otra',
|
||||||
],
|
hijos: [
|
||||||
columnaDerecha: [
|
{ key: 'Químico', label: 'Químico' },
|
||||||
{ key: 'Tostado', label: 'Tostado' },
|
{ key: 'Humedad/Tierra', label: 'Humedad' },
|
||||||
{ key: 'Cereal', label: 'Cereal', indent: true },
|
{ key: 'Madera', label: 'Madera' },
|
||||||
{ 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: 'Tostado',
|
||||||
{ key: 'Cacao', label: 'Cacao', indent: true },
|
label: 'Tostado',
|
||||||
{ key: 'Especias', label: 'Especias' },
|
hijos: [
|
||||||
{ key: 'Dulce', label: 'Dulce' },
|
{ key: 'Cereal', label: 'Cereal' },
|
||||||
{ key: 'Vainilla', label: 'Vainilla', indent: true },
|
{ key: 'Quemado', label: 'Quemado' },
|
||||||
{ key: 'Azúcar Morena', label: 'Azúcar Morena', indent: true },
|
{ 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
|
* Sensaciones en boca para el formulario PDF
|
||||||
|
|||||||
Reference in New Issue
Block a user