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,