diff --git a/shop-mode.html b/shop-mode.html index 246f810..8eef019 100644 --- a/shop-mode.html +++ b/shop-mode.html @@ -915,222 +915,277 @@ }, 3000); }, - generaPDF() { - const { jsPDF } = window.jspdf; - const doc = new jsPDF(); +generaPDF() { + const { jsPDF } = window.jspdf; + const doc = new jsPDF(); - let yPos = 20; + let yPos = 0; - // Determine brand color (from logo or default) - const brandColor = this.logoColor || "#10b981"; - const rgb = this.hexToRgb(brandColor); + // 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 with logo - if (this.venditore.logo) { - try { - doc.addImage( - this.venditore.logo, - "PNG", - 15, - yPos, - 30, - 30, - ); - } catch (err) { - console.error("Error adding logo to PDF:", err); - } - } + // ===== 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'); - // Company name - doc.setFontSize(22); - doc.setFont(undefined, "bold"); - doc.setTextColor(rgb.r, rgb.g, rgb.b); - doc.text( - this.venditore.nome || "Venditore", - this.venditore.logo ? 50 : 105, - yPos + 10, - { align: this.venditore.logo ? "left" : "center" }, - ); + yPos = 18; - doc.setFontSize(16); - doc.setFont(undefined, "bold"); - doc.setTextColor(100); - doc.text( - "PREVENTIVO", - this.venditore.logo ? 50 : 105, - yPos + 20, - { align: this.venditore.logo ? "left" : "center" }, - ); + // 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); + } + } - yPos += this.venditore.logo ? 40 : 30; + // 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); - // Decorative line with brand color - doc.setDrawColor(rgb.r, rgb.g, rgb.b); - doc.setLineWidth(0.5); - doc.line(20, yPos, 190, yPos); - yPos += 5; + doc.setFontSize(14); + doc.setFont(undefined, 'bold'); + doc.setTextColor(255, 255, 255); + doc.text("PREVENTIVO", xStart, yPos + 23); - // Dati venditore e cliente - doc.setFontSize(10); - doc.setTextColor(0); - doc.text("VENDITORE:", 20, yPos); - doc.text("CLIENTE:", 120, yPos); - yPos += 5; + // 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' }); - doc.setFontSize(9); - doc.text(this.venditore.nome, 20, yPos); - doc.text(this.cliente.nome, 120, yPos); - yPos += 5; - doc.text(`P.IVA: ${this.venditore.piva}`, 20, yPos); - doc.text(this.cliente.email, 120, yPos); - yPos += 5; - doc.text(this.venditore.indirizzo, 20, yPos); - doc.text(this.cliente.telefono, 120, yPos); - yPos += 10; + yPos = 65; - // Tabella articoli con brand color - doc.setFontSize(10); - doc.setFillColor(rgb.r, rgb.g, rgb.b); - doc.rect(20, yPos, 170, 7, "F"); - doc.setTextColor(255, 255, 255); - doc.setFont(undefined, "bold"); - doc.text("Articolo", 25, yPos + 5); - doc.text("Q.tà", 110, yPos + 5); - doc.text("Prezzo", 130, yPos + 5); - doc.text("Sc%", 150, yPos + 5); - doc.text("IVA%", 165, yPos + 5); - doc.text("Totale", 180, yPos + 5); - yPos += 10; - doc.setFont(undefined, "normal"); + // ===== 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'); - doc.setTextColor(0); - this.items.forEach((item) => { - if (yPos > 260) { - doc.addPage(); - yPos = 20; - } + // 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'); - // Nome prodotto - doc.setFontSize(9); - doc.setFont(undefined, "bold"); - doc.text(item.name.substring(0, 50), 25, yPos); - doc.setFont(undefined, "normal"); + doc.setFontSize(9); + doc.setFont(undefined, 'bold'); + doc.setTextColor(255, 255, 255); + doc.text("VENDITORE", 57.5, yPos + 5.5, { align: 'center' }); - // Dati numerici - doc.text(String(item.quantity), 113, yPos); - doc.text(`€${item.price.toFixed(2)}`, 130, yPos); - doc.text(`${item.discount}%`, 153, yPos); - doc.text(`${item.iva || 0}%`, 168, yPos); - doc.text( - `€${this.calculateItemTotal(item).toFixed(2)}`, - 180, - yPos, - ); - yPos += 5; + // 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); - // SKU e descrizione (se presenti) - if (item.sku || item.description) { - doc.setFontSize(8); - doc.setTextColor(100); - if (item.sku) { - doc.text(`SKU: ${item.sku}`, 25, yPos); - yPos += 4; - } - if (item.description) { - const descLines = doc.splitTextToSize( - item.description, - 160, - ); - descLines.slice(0, 2).forEach((line) => { - doc.text(line, 25, yPos); - yPos += 4; - }); - } - doc.setTextColor(0); - doc.setFontSize(9); - yPos += 2; - } else { - yPos += 5; - } - }); + // 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'); - yPos += 5; + // 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'); - // Totali - doc.line(120, yPos, 190, yPos); - yPos += 7; + doc.setFontSize(9); + doc.setFont(undefined, 'bold'); + doc.setTextColor(255, 255, 255); + doc.text("CLIENTE", 152.5, yPos + 5.5, { align: 'center' }); - doc.text("Subtotale:", 120, yPos); - doc.text( - this.formatCurrency(this.calculateSubtotal()), - 175, - yPos, - ); - yPos += 7; + // 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); - if (this.calculateTotalDiscount() > 0) { - doc.text("Sconto:", 120, yPos); - doc.text( - `-${this.formatCurrency(this.calculateTotalDiscount())}`, - 175, - yPos, - ); - yPos += 7; - } + yPos += 38; - if (this.shippingCost > 0) { - doc.text("Spedizione:", 120, yPos); - doc.text( - this.formatCurrency(this.shippingCost), - 175, - yPos, - ); - yPos += 7; - } + // ===== 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.text("Imponibile:", 120, yPos); - doc.text( - this.formatCurrency(this.calculateTaxableAmount()), - 175, - yPos, - ); - yPos += 7; + 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' }); - if (this.taxRate > 0) { - doc.text(`IVA (${this.taxRate}%):`, 120, yPos); - doc.text( - this.formatCurrency(this.calculateTax()), - 175, - yPos, - ); - yPos += 7; - } + yPos += 14; - doc.setFontSize(14); - doc.setFont(undefined, "bold"); - doc.setTextColor(rgb.r, rgb.g, rgb.b); - doc.text("TOTALE:", 120, yPos); - doc.text( - this.formatCurrency(this.calculateTotal()), - 175, - yPos, - ); + // Items rows with alternating background + let itemIndex = 0; + this.items.forEach((item) => { + if (yPos > 255) { + doc.addPage(); + yPos = 20; + itemIndex = 0; + } - // Footer decorative line - yPos += 5; - doc.setDrawColor(rgb.r, rgb.g, rgb.b); - doc.setLineWidth(0.5); - doc.line(20, yPos, 190, yPos); + // Alternating row background + if (itemIndex % 2 === 0) { + doc.setFillColor(250, 250, 250); + doc.rect(15, yPos - 2, 180, 10, 'F'); + } - const filename = `Preventivo_${this.cliente.nome || "Cliente"}_${new Date().toISOString().split("T")[0]}.pdf`; - doc.save(filename); + // Item data + doc.setFontSize(8); + doc.setFont(undefined, 'bold'); + doc.setTextColor(60, 60, 60); + doc.text(item.name.substring(0, 45), 18, yPos + 2); - this.showNotification( - "PDF generato con successo!", - "success", - ); - }, + 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' }); + + // 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; + } + + // Separator line + doc.setDrawColor(230, 230, 230); + doc.setLineWidth(0.3); + doc.line(15, yPos - 1, 195, yPos - 1); + + itemIndex++; + }); + + 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'); + + let summaryY = yPos + 8; + + 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; + + // 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; + } + + // 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; + } + + // 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' }); + + // 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); + + this.showNotification("PDF generato con successo!", "success"); +}, salvaDati() { const data = { venditore: this.venditore,