diff --git a/src/data/data_icons_b64.ts b/src/data/data_icons_b64.ts index 28c6987..46e6b2d 100644 --- a/src/data/data_icons_b64.ts +++ b/src/data/data_icons_b64.ts @@ -4,4 +4,9 @@ -export const warningIconB64 = 'iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAABlUlEQVR4nOyVvWtTURjGf+feEOONix9DwEkIiB/EQcE5omCIIKg4OYqDOOomCIpbof07QjukTSDQoXOXtkNKaZZOpe3WoUnTpslb7hfNx7m594ZmKPSBAznvOe/ze/Oec7gGU9Y1IFSJqBulxE0sZpxJi5/qM8eXCsDiH/Dd+90CfkVJU1E2SZksJnXghhc6xSSnCmyH5UY7A5O5PnNbSc68doUo9B9IhdfAsjNJ3XeD7V0/u6CK1CYGyAoJmqwDT+1596XrZa6+9bdskeGZekEnyGN8i5r88M1d51vuuNAj9ryDjwuQGneA32MLwOnBH1niXmwAXf4Dd0MBcBvF31gAWeQJwteRYjuHztDom5TJ6Rb0D81gVrdmbHwJKtT0rvKrUavh6qt8BN7oXCT90BkBykuFD8PBgWsqJZJYbAJZnUPv+QKIYKx9CoLskOaxytP2A4NtsHgfZG7LqI+9kbYecMQ7YF4PEBooh57Spp8chAHa9Gj0B0ZeslTJIH2PK44UdVVkf6LcSXX1P5lTB5wHAAD//0kDVOGS5cmMAAAAAElFTkSuQmCC'; \ No newline at end of file +// Imágenes generadas automáticamente + +export const precaucionIconB64 = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAByUlEQVR4nOxVu4sTQRj/fZMzuUviwXnnq7hCuUY8Ym0364OgMRGUkN5XESwNNoKoKAoBtfZP0MoiCBamswo2ATGNhYUa8n6sMbr7SUYjCftMMIVwPxh29pv5PWZ2hhVYMHYMPLHkd+Kn4+mVSFDPj/r9YfjG5tvn3/6pQTQ0uMeg7KgfCek6gJwfHvmZ1JapLQNmGUBIFRhDQ4jY3jcvP3hxfX0DA/zkr/jvWMEAm3k/XM8V1GTilAC9Vmn2b6ia+bWmngzzzHqx8GpuA5ZyqYnoOwDbo/fVZ49UvXP15njK+7Xu52NUKv1w0nDdohYi18fiKk14WbUJHGntPph103A0aMfjexh0y40MtU243ZHJjZkNjO+77gNY9zIAsPaTcHcmg7qWPArgiiVttw/u9KwE5muNk6mYnZbtRRNsPmaQZaybe+AUNABDHeUTniuoa8mLDDptq3J4UzV7sNbUEhdcDTidDhLzQ6eY4csZrFzKOA2DmfIfpZw6ZlPb0Kjq54loy0mgd+epo/gfHFpF9ByAF7YGQlCFGQMAy3Zss970MhhQQFQmC5abXJVnDwjQtpeSbQBweV+x8GUe7tz4/3+ZCzf4FQAA//8XSHr/N7SpsQAAAABJRU5ErkJggg==" + +export const advertenciaIconB64 = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAABlUlEQVR4nOyVvWtTURjGf+feEOONix9DwEkIiB/EQcE5omCIIKg4OYqDOOomCIpbof07QjukTSDQoXOXtkNKaZZOpe3WoUnTpslb7hfNx7m594ZmKPSBAznvOe/ze/Oec7gGU9Y1IFSJqBulxE0sZpxJi5/qM8eXCsDiH/Dd+90CfkVJU1E2SZksJnXghhc6xSSnCmyH5UY7A5O5PnNbSc68doUo9B9IhdfAsjNJ3XeD7V0/u6CK1CYGyAoJmqwDT+1596XrZa6+9bdskeGZekEnyGN8i5r88M1d51vuuNAj9ryDjwuQGneA32MLwOnBH1niXmwAXf4Dd0MBcBvF31gAWeQJwteRYjuHztDom5TJ6Rb0D81gVrdmbHwJKtT0rvKrUavh6qt8BN7oXCT90BkBykuFD8PBgWsqJZJYbAJZnUPv+QKIYKx9CoLskOaxytP2A4NtsHgfZG7LqI+9kbYecMQ7YF4PEBooh57Spp8chAHa9Gj0B0ZeslTJIH2PK44UdVVkf6LcSXX1P5lTB5wHAAD//0kDVOGS5cmMAAAAAElFTkSuQmCC" + diff --git a/src/data/data_proyecto.ts b/src/data/data_proyecto.ts index 994b6bb..31a8c23 100644 --- a/src/data/data_proyecto.ts +++ b/src/data/data_proyecto.ts @@ -187,13 +187,13 @@ export const proyecto : Proyecto = { nombre : 'Viggo Fresno', value : 'sufr', material: 'suelo_viggo_fresno', - filtro : 'filterSuelos' + filtro : 'suelo' }, { nombre : 'Viggo Roble', value : 'suro', material: 'suelo_viggo_roble', - filtro : 'filterSuelos' + filtro : 'suelo' } ] }, @@ -273,13 +273,13 @@ export const proyecto : Proyecto = { { nombre : 'Viggo Fresno', value : 'sufr', - filtro : 'filterSuelos', + filtro : 'suelo', material: 'suelo_viggo_fresno', }, { nombre : 'Viggo Roble', material: 'suelo_viggo_roble', - filtro : 'filterSuelos', + filtro : 'suelo', value : 'suro' } ] diff --git a/src/lib/components/PdfBoton.svelte b/src/lib/components/PdfBoton.svelte index 9239c0f..c9da083 100644 --- a/src/lib/components/PdfBoton.svelte +++ b/src/lib/components/PdfBoton.svelte @@ -4,13 +4,21 @@ import { jsPDF} from "jspdf"; import { pdfImages } from "../../data/data_pdfs_b64"; import { renderImages } from "../../data/data_images.b64"; - import {estanciasImagen, getSelecciones, imagesNames} from "../stores/configurador.svelte.js"; + import { + type EstanciaImagen, type EstanciaSeleccion, + estanciasImagen, + getSelecciones, + imagesNames, + type Selecciones + } from "../stores/configurador.svelte.js"; import {proyecto} from "../../data/data_proyecto"; import { projectFonts } from "../../data/data_fonts"; import {formatearPrecio} from "../utils/formaters.js"; - import { warningIconB64 } from "../../data/data_icons_b64"; - import * as JSPdf from "jspdf"; + import { advertenciaIconB64, precaucionIconB64 } from "../../data/data_icons_b64"; + import {filtros} from "../utils/filtros"; + import type {Estancia} from "../../data/data_types"; + let pdfEstado : PdfEstado = 'idle'; @@ -23,271 +31,13 @@ const textNotify = 'Consulte si las opciones seleccionadas estan disponibles para la tipología de su vivienda.'; const textError = 'El pavimento es diferente al seleccionado en el salón, que es el que determina el pavimento de la vivienda.' -/* - async function generatePdf() { - pdfVisible = true; - pdfEstado = 'creating'; - const estancias = estanciasImagen(); - const selecciones = getSelecciones(); - - - try { - - const pdfDoc = new jsPDF({ - orientation: 'landscape', - unit: 'mm', - format: 'a4', - }); - - // Registrar Didot - const tf = projectFonts['Didot'] - pdfDoc.addFileToVFS(tf.filename, tf.base64); - pdfDoc.addFont(tf.filename, tf.name,tf.style); - - // Registrar Proxima Nova Regular - const pf = projectFonts['proxima_nova_regular'] - pdfDoc.addFileToVFS(pf.filename, pf.base64); - pdfDoc.addFont(pf.filename, pf.name, pf.style); - - // Registrar Proxima Nova Bold - const pb = projectFonts['proxima_nova_bold'] - pdfDoc.addFileToVFS(pb.filename, pb.base64); - pdfDoc.addFont(pb.filename, pb.name, pb.style); - - // Registrar Proxima Nova Light - const pl = projectFonts['proxima_nova_light'] - pdfDoc.addFileToVFS(pl.filename, pl.base64); - pdfDoc.addFont(pl.filename, pl.name, pl.style); - - const titleFont = tf.name; - const textFont = pf.name; - const textLightFont = pl.name; - const textBoldFont = pb.name; - - console.log(pdfDoc.getFontList()); - - - const pageWidth = pdfDoc.internal.pageSize.getWidth(); - const pageHeight = pdfDoc.internal.pageSize.getHeight(); - const margin = 15; - - // === Portada === - const ini = pdfImages['pdf_quaddro_ini']; - if (ini) { - const iniFormat = ini.includes('jpeg') ? 'JPEG' : 'PNG'; - pdfDoc.addImage(ini, iniFormat, 0, 0, pageWidth, pageHeight, '', 'FAST'); - pdfDoc.addPage(); - } - - // === Imágenes del array === - estancias.forEach((entrada, index) => { - const base64 = renderImages[entrada.imagen]; - if (!base64) { - console.log('no existe la imagen ', entrada.imagen) - return; - } - - // Datos de la imagen original - const imgPixelWidth = 1500; - const imgPixelHeight = 1500; - const pageRatio = pageWidth / pageHeight; - const imgRatio = imgPixelWidth / imgPixelHeight; - - // Calcular tamaño para que mantenga proporción y ajuste a la altura de página - let renderWidth: number; - let renderHeight: number; - - if (imgRatio > pageRatio) { - // Imagen más ancha que alta → ajustamos al alto - renderHeight = pageHeight - 2 * margin; - renderWidth = renderHeight * imgRatio; - } else { - // Imagen más alta → también ajustamos al alto - renderHeight = pageHeight - 2 * margin; - renderWidth = renderHeight * imgRatio; - } - - // Posición alineada a la izquierda con margen de 15 mm - const x = margin; - const y = (pageHeight - renderHeight) / 2; - - pdfDoc.addImage(base64, 'JPEG', x, y, renderWidth, renderHeight, '', 'FAST'); - - - - - - -//---------------------------------------- -// TEXTO -//---------------------------------------- - const estanciaId = entrada.estanciaId; - const estancia = proyecto.estancias[estanciaId]; - const seleccion = selecciones?[estanciaId] : {}; - - const textStartX = margin + renderWidth + 10; // derecha de la imagen + margen - let cursorY = margin + 5; // margen superior - -// Función auxiliar para texto multilínea - function addTextBlock( - text: string | string[] | null | undefined, - x: number, - yStart: number, - maxWidth: number, - fontSize: number, - lineHeight: number - ) { - // Normalizar entrada - if (text == null) return yStart; - let safeText: string; - if (Array.isArray(text)) safeText = text.join("\n"); - else safeText = String(text); - - if (safeText.trim().length === 0) return yStart; - - // Configurar fuente y tamaño - pdfDoc.setFont(textFont, "normal"); - pdfDoc.setFontSize(fontSize); - - // Dividir en párrafos respetando CRLF y LF - const paragraphs = safeText.split(/\r?\n/); - - let cursorY = yStart; - for (let p = 0; p < paragraphs.length; p++) { - const paragraph = paragraphs[p]; - - // Si es párrafo vacío, avanzamos una línea (preserva dobles saltos) - if (paragraph.trim().length === 0) { - cursorY += lineHeight; - continue; - } - - // splitTextToSize por cada párrafo para respetar maxWidth - const lines = pdfDoc.splitTextToSize(paragraph, maxWidth); - - for (let i = 0; i < lines.length; i++) { - pdfDoc.text(lines[i], x, cursorY + i * lineHeight); - } - - // avanzar cursorY: todas las líneas del párrafo + 1*lineHeight como espacio entre párrafos - cursorY += lines.length * lineHeight + lineHeight; - } - - // devolver la siguiente posición Y disponible - return cursorY; - } - -// === Título === - pdfDoc.setFontSize(18); - pdfDoc.setFont(titleFont, "normal"); - pdfDoc.text((estancia.nombre || estanciaId).toUpperCase(), textStartX, cursorY); - -// === Descripción === - pdfDoc.setFont(textLightFont, "normal"); - if (estancia.texto) { - - cursorY += 8; - cursorY = addTextBlock(estancia.texto, textStartX, cursorY, pageWidth - textStartX - margin, 12, 5); - } - -// === Opciones y Add-ons al margen inferior === - const opciones = seleccion.opciones ?? []; - const adds = seleccion.adds ?? []; - - const lineHeightOpc = 5; - const lineHeightAdd = 5; - -// --- Calcular altura total de los bloques --- - let totalOpcHeight = 0; - opciones.forEach((grupo: Record) => { - const nombreGrupo = Object.keys(grupo)[0]; - const opcion = grupo[nombreGrupo]; - totalOpcHeight += pdfDoc.splitTextToSize(`${nombreGrupo}: ${opcion.nombre}`, pageWidth - textStartX - margin - 5).length * lineHeightOpc + 2; - }); - let totalAddsHeight = 0; - adds.forEach((add: Record) => { - const nombreAdd = Object.keys(add)[0]; - totalAddsHeight += pdfDoc.splitTextToSize(nombreAdd, pageWidth - textStartX - margin - 5).length * lineHeightAdd + 2; - }); - - // --- Posicionar el cursorY desde el fondo --- - let cursorBottomY = pageHeight - margin - totalOpcHeight - totalAddsHeight - 10; // 10 mm margen entre bloques - - // === Opciones seleccionadas === - if (opciones.length > 0) { - pdfDoc.setFont(textFont, "normal"); - pdfDoc.setFontSize(11); - pdfDoc.text("Opciones seleccionadas:", textStartX, cursorBottomY); - - pdfDoc.setFont(textFont, "normal"); - pdfDoc.setFontSize(10); - cursorBottomY += 5; - opciones.forEach((grupo: Record) => { - const nombreGrupo = Object.keys(grupo)[0]; - const opcion = grupo[nombreGrupo]; - cursorBottomY = addTextBlock(`${nombreGrupo}: ${opcion.nombre}`, textStartX + 5, cursorBottomY, pageWidth - textStartX - margin - 5, 10, lineHeightOpc); - }); - } - - // === Add-ons seleccionados === - if (adds.length > 0) { - cursorBottomY += 5; - pdfDoc.setFont(textFont, "normal"); - pdfDoc.setFontSize(11); - pdfDoc.text("Add-ons seleccionados:", textStartX, cursorBottomY); - - pdfDoc.setFont(textFont, "normal"); - pdfDoc.setFontSize(10); - cursorBottomY += 5; - adds.forEach((add: Record) => { - const nombreAdd = Object.keys(add)[0]; - cursorBottomY = addTextBlock(nombreAdd, textStartX + 5, cursorBottomY, pageWidth - textStartX - margin - 5, 10, lineHeightAdd); - }); - } - - - //--------------------------------- - // NUEVA PAGINA - //--------------------------------- - - // Añadir nueva página excepto en la última - if (index < estancias.length - 1) pdfDoc.addPage(); - }); - - // === Página final === - pdfDoc.addPage(); - const fin = pdfImages['pdf_quaddro_fin']; - if (fin) { - const finFormat = fin.includes('jpeg') ? 'JPEG' : 'PNG'; - pdfDoc.addImage(fin, finFormat, 0, 0, pageWidth, pageHeight, '', 'FAST'); - } - - await new Promise(r => setTimeout(r, stepTimeout)) - pdfEstado = 'downloading'; - - pdfDoc.save(proyecto.textos.nombre_pdf ?? 'mi_seleccion.pdf'); - pdfEstado = 'success' - - } catch (err: any) { - pdfEstado = 'error'; - pdfMensaje = err?.message || 'Error desconocido'; - console.log(err) - } finally { - setTimeout(() => { - pdfVisible = false; - pdfEstado = 'idle'; - }, stepTimeout); - } - } -*/ - async function generatePdf() { pdfVisible = true; pdfEstado = 'creating'; - const estancias = estanciasImagen(); - const selecciones = getSelecciones(); + const estancias : EstanciaImagen[] = estanciasImagen(); + const selecciones : Selecciones | null = getSelecciones(); try { const pdfDoc = new jsPDF({ @@ -412,6 +162,7 @@ const lineHeightAdd = 3; let notify = false; + let prohibido = false; // === Opciones seleccionadas === if (opciones.length > 0) { @@ -427,10 +178,19 @@ for (const grupo of opciones) { const nombreGrupo = Object.keys(grupo)[0]; const opcion = grupo[nombreGrupo]; + + let icon: string | null = null; + if (opcion.filtro && filtros[opcion.filtro]) { + if (!filtros[opcion.filtro].check(opcion.value)) { + icon = precaucionIconB64; + if (!prohibido) prohibido = true; + } + } + const label = `${nombreGrupo}: ${opcion?.nombre ?? ''}`; // precio puede estar en opcion.precio o opcion.price const priceVal = opcion?.precio ?? opcion?.price ?? null; - cursorBoxY = addOptionLine(pdfDoc,label, priceVal, textBoxX, cursorBoxY, textBoxMaxWidth - 6, textFont, 10, textFont, 10, { r: 30, g: 30, b: 30 }); + cursorBoxY = addOptionLine(pdfDoc,label, priceVal, textBoxX, cursorBoxY, textBoxMaxWidth - 6, textFont, 10, textFont, 10, { r: 30, g: 30, b: 30 },icon); } } @@ -450,7 +210,7 @@ const info = add[Object.keys(add)[0]]; const priceVal = info?.precio ?? info?.price ?? null; let label = info.nombre; - let icon : string | null = info.filtro ? warningIconB64 : null; + let icon : string | null = info.filtro ? advertenciaIconB64 : null; if (info.filtro) { notify = true; } @@ -459,8 +219,14 @@ } } + let pieY = 200; + const pieX = 15; + if (prohibido) { + addOptionLine(pdfDoc,textError,null,margin,pieY,1000,textFont,9,null,null,undefined,precaucionIconB64); + pieY += 4; + } if (notify) { - addOptionLine(pdfDoc,textNotify,null,15,200,1000,textFont,9,null,null,undefined,warningIconB64); + addOptionLine(pdfDoc,textNotify,null,margin,pieY,1000,textFont,9,null,null,undefined,advertenciaIconB64); } diff --git a/src/lib/stores/configurador.svelte.ts b/src/lib/stores/configurador.svelte.ts index 065f850..122a996 100644 --- a/src/lib/stores/configurador.svelte.ts +++ b/src/lib/stores/configurador.svelte.ts @@ -314,12 +314,12 @@ export function getIncidencias(): IncidenciasEstancia | null { -type EstanciaSeleccion = { +export type EstanciaSeleccion = { opciones: Record[]; // { "grupo": "valor seleccionado" } adds: Record[]; // { "add": "valor seleccionado" } }; -type Selecciones = Record; // { "estanciaId": {...} } +export type Selecciones = Record; // { "estanciaId": {...} } export function getSelecciones (): Selecciones | null { @@ -368,45 +368,3 @@ export function getSelecciones (): Selecciones | null { } - -/* Deprecated -export function getSelecciones(): Selecciones | null { - const result: Selecciones = {}; - - for (const estanciaId of Object.keys(proyecto.estancias || {})) { - const estanciaData = proyecto.estancias[estanciaId]; - const estanciaSelections = selections[estanciaId]; - const estanciaAddOns = selectedAdds[estanciaId]; - - if (!estanciaData) continue; - - const opciones: Record[] = []; - const adds: Record[] = []; - - // Opciones - if (estanciaData.opciones && estanciaSelections) { - for (const grupo of estanciaData.opciones) { - const selectedValue = estanciaSelections[grupo.nombre]; - if (selectedValue && grupo.opciones) { - const opcion = grupo.opciones.find(o => o.value === selectedValue); - if (opcion) opciones.push({ [grupo.nombre]: opcion }); - } - } - } - - // Add-ons - if (estanciaData.adds && estanciaAddOns) { - for (const [addKey, add] of Object.entries(estanciaData.adds)) { - if (estanciaAddOns[addKey]) adds.push({ [add.nombre]: add }); - } - } - - if (opciones.length > 0 || adds.length > 0) { - result[estanciaId] = { opciones, adds }; - } - } - - return Object.keys(result).length > 0 ? result : null; -} - - */ \ No newline at end of file diff --git a/src/lib/stores/navegation.svelte.ts b/src/lib/stores/navegation.svelte.ts index 7068d68..1b494f0 100644 --- a/src/lib/stores/navegation.svelte.ts +++ b/src/lib/stores/navegation.svelte.ts @@ -29,7 +29,7 @@ export const navegationState = { { id: 'ini', label: 'Inicio', menu: false }, { id: 'cfg', label: 'Personalizar', menu: true }, { id: 'sel', label: 'Mi Selección', menu: true }, - { id: 'at' , label: 'Active Thing', menu: true }, + { id: 'at' , label: 'Active Thing', menu: false }, ] as PageInfo[], }