From 546d7201b083142ed3210095b833d46470c5222a Mon Sep 17 00:00:00 2001 From: "d.viti" Date: Tue, 14 Oct 2025 00:16:37 +0200 Subject: [PATCH] feat: Advanced color palette extraction algorithm - Build complete color palette instead of single dominant color - Filter out whites (RGB > 240), blacks (RGB < 15), and grays (saturation < 0.15) - Calculate HSL values for better color analysis - Quantize colors to group similar shades together - Score colors based on: * Saturation (prefer vibrant colors) * Frequency (weight by occurrence) * Optimal lightness (0.4-0.6 range preferred) - Select best scoring color from palette - Skip too light (> 0.85) or too dark (< 0.20) colors - Improved sampling rate (every 4 pixels instead of 10) - Console logging for debugging extracted palette - Fallback to emerald green if no suitable colors found --- shop-mode.html | 652 ++++++++++++++++++++++++++++++------------------- 1 file changed, 407 insertions(+), 245 deletions(-) diff --git a/shop-mode.html b/shop-mode.html index 8eef019..2b1a536 100644 --- a/shop-mode.html +++ b/shop-mode.html @@ -735,17 +735,18 @@ ctx.drawImage(img, 0, 0); try { - const imageData = ctx.getImageData( + const imgData = ctx.getImageData( 0, 0, canvas.width, canvas.height, ); - const data = imageData.data; + const data = imgData.data; - // Sample colors (every 10 pixels for performance) + // Collect color palette with counts const colorMap = {}; - for (let i = 0; i < data.length; i += 40) { + for (let i = 0; i < data.length; i += 16) { + // Sample every 4 pixels for better coverage const r = data[i]; const g = data[i + 1]; const b = data[i + 2]; @@ -754,32 +755,93 @@ // Skip transparent pixels if (a < 125) continue; - // Skip very light colors (likely background) + // Skip whites (> 240 in all channels) if (r > 240 && g > 240 && b > 240) continue; - const rgb = `${r},${g},${b}`; + // Skip blacks (< 15 in all channels) + if (r < 15 && g < 15 && b < 15) continue; + + // Skip grays (low saturation: when R≈G≈B) + const maxChan = Math.max(r, g, b); + const minChan = Math.min(r, g, b); + const saturation = + maxChan === 0 + ? 0 + : (maxChan - minChan) / maxChan; + if (saturation < 0.15) continue; // Skip low saturation (grays) + + // Quantize colors to reduce variations (group similar colors) + const qr = Math.round(r / 10) * 10; + const qg = Math.round(g / 10) * 10; + const qb = Math.round(b / 10) * 10; + const rgb = `${qr},${qg},${qb}`; colorMap[rgb] = (colorMap[rgb] || 0) + 1; } - // Find most common color - let maxCount = 0; - let dominantColor = null; + // Build palette with color scores + const palette = []; for (const [rgb, count] of Object.entries( colorMap, )) { - if (count > maxCount) { - maxCount = count; - dominantColor = rgb; - } - } - - if (dominantColor) { - const [r, g, b] = dominantColor + const [r, g, b] = rgb .split(",") .map(Number); - this.logoColor = this.rgbToHex(r, g, b); + + // Calculate HSL for better color selection + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + const l = (max + min) / 2 / 255; // Lightness 0-1 + const s = + max === min + ? 0 + : (max - min) / (max + min); // Saturation 0-1 + + // Skip too light (> 0.85) or too dark (< 0.20) + if (l > 0.85 || l < 0.2) continue; + + // Calculate color "score" based on: + // - Saturation (higher is better) + // - Frequency (more common is better) + // - Ideal lightness (0.4-0.6 is best) + const lightnessScore = + 1 - Math.abs(l - 0.5) * 2; // Peaks at 0.5 + const score = + s * 2 + count / 100 + lightnessScore; + + palette.push({ + r, + g, + b, + count, + saturation: s, + lightness: l, + score, + }); + } + + // Sort by score (best colors first) + palette.sort((a, b) => b.score - a.score); + + // Get the best color from palette + if (palette.length > 0) { + const bestColor = palette[0]; + this.logoColor = this.rgbToHex( + bestColor.r, + bestColor.g, + bestColor.b, + ); + console.log( + "Extracted color:", + this.logoColor, + "from palette of", + palette.length, + "colors", + ); } else { this.logoColor = "#10b981"; // Fallback emerald + console.log( + "No suitable colors found, using fallback", + ); } } catch (err) { console.error("Error extracting color:", err); @@ -915,277 +977,377 @@ }, 3000); }, -generaPDF() { - const { jsPDF } = window.jspdf; - const doc = new jsPDF(); + generaPDF() { + const { jsPDF } = window.jspdf; + const doc = new jsPDF(); - let yPos = 0; + let yPos = 0; - // Determine brand color (from logo or default) - const brandColor = this.logoColor || '#10b981'; - const rgb = this.hexToRgb(brandColor); - const lightRgb = { - r: Math.min(255, rgb.r + 180), - g: Math.min(255, rgb.g + 180), - b: Math.min(255, rgb.b + 180) - }; + // Determine brand color (from logo or default) + const brandColor = this.logoColor || "#10b981"; + const rgb = this.hexToRgb(brandColor); + const lightRgb = { + r: Math.min(255, rgb.r + 180), + g: Math.min(255, rgb.g + 180), + b: Math.min(255, rgb.b + 180), + }; - // ===== HEADER SECTION with gradient effect ===== - doc.setFillColor(lightRgb.r, lightRgb.g, lightRgb.b); - doc.rect(0, 0, 210, 55, 'F'); - doc.setFillColor(rgb.r, rgb.g, rgb.b); - doc.rect(0, 35, 210, 20, 'F'); + // ===== HEADER SECTION with gradient effect ===== + doc.setFillColor(lightRgb.r, lightRgb.g, lightRgb.b); + doc.rect(0, 0, 210, 55, "F"); + doc.setFillColor(rgb.r, rgb.g, rgb.b); + doc.rect(0, 35, 210, 20, "F"); - yPos = 18; + yPos = 18; - // Logo in header with white card background - if (this.venditore.logo) { - try { - doc.setFillColor(255, 255, 255); - doc.roundedRect(15, yPos - 8, 35, 35, 3, 3, 'F'); - doc.setDrawColor(230, 230, 230); - doc.setLineWidth(0.5); - doc.roundedRect(15, yPos - 8, 35, 35, 3, 3, 'S'); - doc.addImage(this.venditore.logo, 'PNG', 18, yPos - 5, 29, 29); - } catch (err) { - console.error('Error adding logo to PDF:', err); - } - } + // Logo in header with white card background + if (this.venditore.logo) { + try { + doc.setFillColor(255, 255, 255); + doc.roundedRect( + 15, + yPos - 8, + 35, + 35, + 3, + 3, + "F", + ); + doc.setDrawColor(230, 230, 230); + doc.setLineWidth(0.5); + doc.roundedRect( + 15, + yPos - 8, + 35, + 35, + 3, + 3, + "S", + ); + doc.addImage( + this.venditore.logo, + "PNG", + 18, + yPos - 5, + 29, + 29, + ); + } catch (err) { + console.error("Error adding logo to PDF:", err); + } + } - // Company name and title - const xStart = this.venditore.logo ? 55 : 15; - doc.setFontSize(26); - doc.setFont(undefined, 'bold'); - doc.setTextColor(60, 60, 60); - doc.text(this.venditore.nome || "Venditore", xStart, yPos); + // Company name and title + const xStart = this.venditore.logo ? 55 : 15; + doc.setFontSize(26); + doc.setFont(undefined, "bold"); + doc.setTextColor(60, 60, 60); + doc.text( + this.venditore.nome || "Venditore", + xStart, + yPos, + ); - doc.setFontSize(14); - doc.setFont(undefined, 'bold'); - doc.setTextColor(255, 255, 255); - doc.text("PREVENTIVO", xStart, yPos + 23); + doc.setFontSize(14); + doc.setFont(undefined, "bold"); + doc.setTextColor(255, 255, 255); + doc.text("PREVENTIVO", xStart, yPos + 23); - // Date badge - const dateStr = new Date(this.cliente.data).toLocaleDateString('it-IT'); - doc.setFillColor(255, 255, 255); - doc.roundedRect(155, yPos + 15, 40, 12, 2, 2, 'F'); - doc.setFontSize(10); - doc.setFont(undefined, 'bold'); - doc.setTextColor(rgb.r, rgb.g, rgb.b); - doc.text(dateStr, 175, yPos + 22, { align: 'center' }); + // Date badge + const dateStr = new Date( + this.cliente.data, + ).toLocaleDateString("it-IT"); + doc.setFillColor(255, 255, 255); + doc.roundedRect(155, yPos + 15, 40, 12, 2, 2, "F"); + doc.setFontSize(10); + doc.setFont(undefined, "bold"); + doc.setTextColor(rgb.r, rgb.g, rgb.b); + doc.text(dateStr, 175, yPos + 22, { align: "center" }); - yPos = 65; + yPos = 65; - // ===== INFO CARDS SECTION ===== - // Venditore card - doc.setFillColor(250, 250, 250); - doc.roundedRect(15, yPos, 85, 28, 3, 3, 'F'); - doc.setDrawColor(220, 220, 220); - doc.setLineWidth(0.5); - doc.roundedRect(15, yPos, 85, 28, 3, 3, 'S'); + // ===== INFO CARDS SECTION ===== + // Venditore card + doc.setFillColor(250, 250, 250); + doc.roundedRect(15, yPos, 85, 28, 3, 3, "F"); + doc.setDrawColor(220, 220, 220); + doc.setLineWidth(0.5); + doc.roundedRect(15, yPos, 85, 28, 3, 3, "S"); - // Venditore header with brand color - doc.setFillColor(rgb.r, rgb.g, rgb.b); - doc.roundedRect(15, yPos, 85, 8, 3, 3, 'F'); - doc.rect(15, yPos + 5, 85, 3, 'F'); + // Venditore header with brand color + doc.setFillColor(rgb.r, rgb.g, rgb.b); + doc.roundedRect(15, yPos, 85, 8, 3, 3, "F"); + doc.rect(15, yPos + 5, 85, 3, "F"); - doc.setFontSize(9); - doc.setFont(undefined, 'bold'); - doc.setTextColor(255, 255, 255); - doc.text("VENDITORE", 57.5, yPos + 5.5, { align: 'center' }); + doc.setFontSize(9); + doc.setFont(undefined, "bold"); + doc.setTextColor(255, 255, 255); + doc.text("VENDITORE", 57.5, yPos + 5.5, { + align: "center", + }); - // Venditore data - doc.setFontSize(8); - doc.setFont(undefined, 'normal'); - doc.setTextColor(60, 60, 60); - doc.text(this.venditore.nome || '-', 18, yPos + 13); - doc.text(`P.IVA: ${this.venditore.piva || '-'}`, 18, yPos + 18); - doc.text(this.venditore.indirizzo || '-', 18, yPos + 23); + // Venditore data + doc.setFontSize(8); + doc.setFont(undefined, "normal"); + doc.setTextColor(60, 60, 60); + doc.text(this.venditore.nome || "-", 18, yPos + 13); + doc.text( + `P.IVA: ${this.venditore.piva || "-"}`, + 18, + yPos + 18, + ); + doc.text( + this.venditore.indirizzo || "-", + 18, + yPos + 23, + ); - // Cliente card - doc.setFillColor(250, 250, 250); - doc.roundedRect(110, yPos, 85, 28, 3, 3, 'F'); - doc.setDrawColor(220, 220, 220); - doc.setLineWidth(0.5); - doc.roundedRect(110, yPos, 85, 28, 3, 3, 'S'); + // Cliente card + doc.setFillColor(250, 250, 250); + doc.roundedRect(110, yPos, 85, 28, 3, 3, "F"); + doc.setDrawColor(220, 220, 220); + doc.setLineWidth(0.5); + doc.roundedRect(110, yPos, 85, 28, 3, 3, "S"); - // Cliente header with brand color - doc.setFillColor(rgb.r, rgb.g, rgb.b); - doc.roundedRect(110, yPos, 85, 8, 3, 3, 'F'); - doc.rect(110, yPos + 5, 85, 3, 'F'); + // Cliente header with brand color + doc.setFillColor(rgb.r, rgb.g, rgb.b); + doc.roundedRect(110, yPos, 85, 8, 3, 3, "F"); + doc.rect(110, yPos + 5, 85, 3, "F"); - doc.setFontSize(9); - doc.setFont(undefined, 'bold'); - doc.setTextColor(255, 255, 255); - doc.text("CLIENTE", 152.5, yPos + 5.5, { align: 'center' }); + doc.setFontSize(9); + doc.setFont(undefined, "bold"); + doc.setTextColor(255, 255, 255); + doc.text("CLIENTE", 152.5, yPos + 5.5, { + align: "center", + }); - // Cliente data - doc.setFontSize(8); - doc.setFont(undefined, 'normal'); - doc.setTextColor(60, 60, 60); - doc.text(this.cliente.nome || '-', 113, yPos + 13); - doc.text(this.cliente.email || '-', 113, yPos + 18); - doc.text(this.cliente.telefono || '-', 113, yPos + 23); + // Cliente data + doc.setFontSize(8); + doc.setFont(undefined, "normal"); + doc.setTextColor(60, 60, 60); + doc.text(this.cliente.nome || "-", 113, yPos + 13); + doc.text(this.cliente.email || "-", 113, yPos + 18); + doc.text(this.cliente.telefono || "-", 113, yPos + 23); - yPos += 38; + yPos += 38; - // ===== ITEMS TABLE ===== - // Table header with gradient - doc.setFillColor(lightRgb.r, lightRgb.g, lightRgb.b); - doc.roundedRect(15, yPos, 180, 10, 2, 2, 'F'); - doc.setFillColor(rgb.r, rgb.g, rgb.b); - doc.roundedRect(15, yPos, 180, 10, 2, 2, 'F'); + // ===== ITEMS TABLE ===== + // Table header with gradient + doc.setFillColor(lightRgb.r, lightRgb.g, lightRgb.b); + doc.roundedRect(15, yPos, 180, 10, 2, 2, "F"); + doc.setFillColor(rgb.r, rgb.g, rgb.b); + doc.roundedRect(15, yPos, 180, 10, 2, 2, "F"); - doc.setFontSize(8); - doc.setFont(undefined, 'bold'); - doc.setTextColor(255, 255, 255); - doc.text("ARTICOLO", 18, yPos + 6.5); - doc.text("Q.TÀ", 110, yPos + 6.5, { align: 'center' }); - doc.text("PREZZO", 130, yPos + 6.5, { align: 'center' }); - doc.text("SC%", 150, yPos + 6.5, { align: 'center' }); - doc.text("IVA%", 165, yPos + 6.5, { align: 'center' }); - doc.text("TOTALE", 188, yPos + 6.5, { align: 'right' }); + doc.setFontSize(8); + doc.setFont(undefined, "bold"); + doc.setTextColor(255, 255, 255); + doc.text("ARTICOLO", 18, yPos + 6.5); + doc.text("Q.TÀ", 110, yPos + 6.5, { align: "center" }); + doc.text("PREZZO", 130, yPos + 6.5, { + align: "center", + }); + doc.text("SC%", 150, yPos + 6.5, { align: "center" }); + doc.text("IVA%", 165, yPos + 6.5, { align: "center" }); + doc.text("TOTALE", 188, yPos + 6.5, { align: "right" }); - yPos += 14; + yPos += 14; - // Items rows with alternating background - let itemIndex = 0; - this.items.forEach((item) => { - if (yPos > 255) { - doc.addPage(); - yPos = 20; - itemIndex = 0; - } + // Items rows with alternating background + let itemIndex = 0; + this.items.forEach((item) => { + if (yPos > 255) { + doc.addPage(); + yPos = 20; + itemIndex = 0; + } - // Alternating row background - if (itemIndex % 2 === 0) { - doc.setFillColor(250, 250, 250); - doc.rect(15, yPos - 2, 180, 10, 'F'); - } + // Alternating row background + if (itemIndex % 2 === 0) { + doc.setFillColor(250, 250, 250); + doc.rect(15, yPos - 2, 180, 10, "F"); + } - // Item data - doc.setFontSize(8); - doc.setFont(undefined, 'bold'); - doc.setTextColor(60, 60, 60); - doc.text(item.name.substring(0, 45), 18, yPos + 2); + // Item data + doc.setFontSize(8); + doc.setFont(undefined, "bold"); + doc.setTextColor(60, 60, 60); + doc.text(item.name.substring(0, 45), 18, yPos + 2); - doc.setFont(undefined, 'normal'); - doc.text(String(item.quantity), 110, yPos + 2, { align: 'center' }); - doc.text(`€${item.price.toFixed(2)}`, 130, yPos + 2, { align: 'center' }); - doc.text(`${item.discount}%`, 150, yPos + 2, { align: 'center' }); - doc.text(`${item.iva || 0}%`, 165, yPos + 2, { align: 'center' }); + doc.setFont(undefined, "normal"); + doc.text(String(item.quantity), 110, yPos + 2, { + align: "center", + }); + doc.text( + `€${item.price.toFixed(2)}`, + 130, + yPos + 2, + { align: "center" }, + ); + doc.text(`${item.discount}%`, 150, yPos + 2, { + align: "center", + }); + doc.text(`${item.iva || 0}%`, 165, yPos + 2, { + align: "center", + }); - doc.setFont(undefined, 'bold'); - doc.setTextColor(rgb.r, rgb.g, rgb.b); - doc.text(`€${this.calculateItemTotal(item).toFixed(2)}`, 188, yPos + 2, { align: 'right' }); + doc.setFont(undefined, "bold"); + doc.setTextColor(rgb.r, rgb.g, rgb.b); + doc.text( + `€${this.calculateItemTotal(item).toFixed(2)}`, + 188, + yPos + 2, + { align: "right" }, + ); - // SKU and description - if (item.sku || item.description) { - yPos += 5; - doc.setFontSize(7); - doc.setTextColor(120, 120, 120); - doc.setFont(undefined, 'italic'); + // SKU and description + if (item.sku || item.description) { + yPos += 5; + doc.setFontSize(7); + doc.setTextColor(120, 120, 120); + doc.setFont(undefined, "italic"); - if (item.sku) { - doc.text(`SKU: ${item.sku}`, 18, yPos); - yPos += 3.5; - } - if (item.description) { - const descLines = doc.splitTextToSize(item.description, 165); - descLines.slice(0, 2).forEach(line => { - doc.text(line, 18, yPos); - yPos += 3.5; - }); - } - yPos += 2; - } else { - yPos += 10; - } + if (item.sku) { + doc.text(`SKU: ${item.sku}`, 18, yPos); + yPos += 3.5; + } + if (item.description) { + const descLines = doc.splitTextToSize( + item.description, + 165, + ); + descLines.slice(0, 2).forEach((line) => { + doc.text(line, 18, yPos); + yPos += 3.5; + }); + } + yPos += 2; + } else { + yPos += 10; + } - // Separator line - doc.setDrawColor(230, 230, 230); - doc.setLineWidth(0.3); - doc.line(15, yPos - 1, 195, yPos - 1); + // Separator line + doc.setDrawColor(230, 230, 230); + doc.setLineWidth(0.3); + doc.line(15, yPos - 1, 195, yPos - 1); - itemIndex++; - }); + itemIndex++; + }); - yPos += 5; + yPos += 5; - // ===== TOTALS SECTION ===== - // Summary box - doc.setFillColor(250, 250, 250); - doc.roundedRect(115, yPos, 80, 45, 3, 3, 'F'); - doc.setDrawColor(220, 220, 220); - doc.setLineWidth(0.5); - doc.roundedRect(115, yPos, 80, 45, 3, 3, 'S'); + // ===== TOTALS SECTION ===== + // Summary box + doc.setFillColor(250, 250, 250); + doc.roundedRect(115, yPos, 80, 45, 3, 3, "F"); + doc.setDrawColor(220, 220, 220); + doc.setLineWidth(0.5); + doc.roundedRect(115, yPos, 80, 45, 3, 3, "S"); - let summaryY = yPos + 8; + let summaryY = yPos + 8; - doc.setFontSize(9); - doc.setFont(undefined, 'normal'); - doc.setTextColor(80, 80, 80); + doc.setFontSize(9); + doc.setFont(undefined, "normal"); + doc.setTextColor(80, 80, 80); - // Subtotal - doc.text("Subtotale:", 120, summaryY); - doc.text(this.formatCurrency(this.calculateSubtotal()), 190, summaryY, { align: 'right' }); - summaryY += 6; + // Subtotal + doc.text("Subtotale:", 120, summaryY); + doc.text( + this.formatCurrency(this.calculateSubtotal()), + 190, + summaryY, + { align: "right" }, + ); + summaryY += 6; - // Discount - if (this.calculateTotalDiscount() > 0) { - doc.text("Sconto:", 120, summaryY); - doc.setTextColor(220, 50, 50); - doc.text(`-${this.formatCurrency(this.calculateTotalDiscount())}`, 190, summaryY, { align: 'right' }); - doc.setTextColor(80, 80, 80); - summaryY += 6; - } + // Discount + if (this.calculateTotalDiscount() > 0) { + doc.text("Sconto:", 120, summaryY); + doc.setTextColor(220, 50, 50); + doc.text( + `-${this.formatCurrency(this.calculateTotalDiscount())}`, + 190, + summaryY, + { align: "right" }, + ); + doc.setTextColor(80, 80, 80); + summaryY += 6; + } - // Shipping - if (this.shippingCost > 0) { - doc.text("Spedizione:", 120, summaryY); - doc.text(this.formatCurrency(this.shippingCost), 190, summaryY, { align: 'right' }); - summaryY += 6; - } + // Shipping + if (this.shippingCost > 0) { + doc.text("Spedizione:", 120, summaryY); + doc.text( + this.formatCurrency(this.shippingCost), + 190, + summaryY, + { align: "right" }, + ); + summaryY += 6; + } - // Taxable amount - doc.text("Imponibile:", 120, summaryY); - doc.text(this.formatCurrency(this.calculateTaxableAmount()), 190, summaryY, { align: 'right' }); - summaryY += 6; + // Taxable amount + doc.text("Imponibile:", 120, summaryY); + doc.text( + this.formatCurrency(this.calculateTaxableAmount()), + 190, + summaryY, + { align: "right" }, + ); + summaryY += 6; - // VAT - if (this.calculateTax() > 0) { - doc.text("IVA:", 120, summaryY); - doc.text(this.formatCurrency(this.calculateTax()), 190, summaryY, { align: 'right' }); - summaryY += 8; - } else { - summaryY += 8; - } + // VAT + if (this.calculateTax() > 0) { + doc.text("IVA:", 120, summaryY); + doc.text( + this.formatCurrency(this.calculateTax()), + 190, + summaryY, + { align: "right" }, + ); + summaryY += 8; + } else { + summaryY += 8; + } - // Total with brand color background - doc.setFillColor(rgb.r, rgb.g, rgb.b); - doc.roundedRect(115, summaryY - 4, 80, 10, 2, 2, 'F'); + // Total with brand color background + doc.setFillColor(rgb.r, rgb.g, rgb.b); + doc.roundedRect(115, summaryY - 4, 80, 10, 2, 2, "F"); - doc.setFontSize(11); - doc.setFont(undefined, 'bold'); - doc.setTextColor(255, 255, 255); - doc.text("TOTALE:", 120, summaryY + 2.5); - doc.setFontSize(13); - doc.text(this.formatCurrency(this.calculateTotal()), 190, summaryY + 2.5, { align: 'right' }); + doc.setFontSize(11); + doc.setFont(undefined, "bold"); + doc.setTextColor(255, 255, 255); + doc.text("TOTALE:", 120, summaryY + 2.5); + doc.setFontSize(13); + doc.text( + this.formatCurrency(this.calculateTotal()), + 190, + summaryY + 2.5, + { align: "right" }, + ); - // Footer - yPos += 55; - if (yPos < 270) { - doc.setFontSize(7); - doc.setFont(undefined, 'italic'); - doc.setTextColor(150, 150, 150); - doc.text(`Documento generato il ${new Date().toLocaleString('it-IT')}`, 105, 285, { align: 'center' }); - } + // Footer + yPos += 55; + if (yPos < 270) { + doc.setFontSize(7); + doc.setFont(undefined, "italic"); + doc.setTextColor(150, 150, 150); + doc.text( + `Documento generato il ${new Date().toLocaleString("it-IT")}`, + 105, + 285, + { align: "center" }, + ); + } - // Save PDF - const filename = `Preventivo_${this.cliente.nome || "Cliente"}_${new Date().toISOString().split("T")[0]}.pdf`; - doc.save(filename); + // Save PDF + const filename = `Preventivo_${this.cliente.nome || "Cliente"}_${new Date().toISOString().split("T")[0]}.pdf`; + doc.save(filename); - this.showNotification("PDF generato con successo!", "success"); -}, + this.showNotification( + "PDF generato con successo!", + "success", + ); + }, salvaDati() { const data = { venditore: this.venditore,