Actualizar index2.html
This commit is contained in:
216
index2.html
216
index2.html
@@ -164,12 +164,97 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="w-full bg-blue-600 text-white font-black py-4 rounded-2xl shadow-xl flex items-center justify-center gap-2 uppercase tracking-widest text-xs active:scale-95 transition-transform">
|
||||
<button id="btnDownloadPdf" class="w-full bg-blue-600 text-white font-black py-4 rounded-2xl shadow-xl flex items-center justify-center gap-2 uppercase tracking-widest text-xs active:scale-95 transition-transform">
|
||||
<i data-lucide="download" class="w-4 h-4"></i> Descargar PDF
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="toast" class="fixed bottom-24 left-1/2 -translate-x-1/2 bg-slate-900 text-white px-6 py-3 rounded-full shadow-2xl hidden z-[200] font-bold text-xs uppercase tracking-widest text-center transition-all whitespace-nowrap"></div>
|
||||
|
||||
<div id="pdf-wrapper" class="hidden">
|
||||
<div id="pdf-content" style="background-color: white; color: #1e293b; font-family: Arial, Helvetica, sans-serif; width: 800px; min-height: 1120px; position: relative; padding: 30px 40px; box-sizing: border-box;">
|
||||
|
||||
<div style="display: flex; justify-content: space-between; align-items: flex-start; border-bottom: 2px solid #e2e8f0; padding-bottom: 15px; margin-bottom: 20px;">
|
||||
<div style="max-width: 250px; max-height: 80px;">
|
||||
<img id="pdf-company-logo" src="" alt="Logo Empresa" style="max-width: 100%; max-height: 80px; object-fit: contain; display: none;">
|
||||
<h2 id="pdf-company-name-fallback" style="margin:0; color:#2563eb; font-size: 22px; font-weight: 900; display: none;">IntegraRepara</h2>
|
||||
</div>
|
||||
<div style="text-align: right; font-size: 11px; color: #64748b; line-height: 1.4;">
|
||||
<div id="pdf-company-name" style="font-size: 14px; font-weight: 900; color: #0f172a; margin-bottom: 2px;">Empresa S.L.</div>
|
||||
<div id="pdf-company-dni">CIF: B12345678</div>
|
||||
<div id="pdf-company-address">Calle Falsa 123</div>
|
||||
<div id="pdf-company-location">28000 Madrid (Madrid)</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; justify-content: space-between; margin-bottom: 20px;">
|
||||
<div>
|
||||
<h1 style="font-size: 24px; font-weight: 900; color: #2563eb; margin: 0 0 5px 0; letter-spacing: -1px;">PRESUPUESTO</h1>
|
||||
<div style="font-size: 12px; color: #64748b; line-height: 1.4;">
|
||||
<strong>Referencia:</strong> <span id="pdf-budget-id">#PRE-000</span><br>
|
||||
<strong>Fecha:</strong> <span id="pdf-date">01/01/2026</span><br>
|
||||
<strong>Validez:</strong> 30 días<br>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="background-color: #f8fafc; border: 1px solid #e2e8f0; padding: 12px 15px; border-radius: 8px; width: 280px;">
|
||||
<h3 style="margin: 0 0 5px 0; font-size: 10px; text-transform: uppercase; letter-spacing: 1px; color: #94a3b8; font-weight: 800;">Presupuestado a:</h3>
|
||||
<p id="pdf-client-name" style="font-size: 14px; font-weight: 800; color: #0f172a; margin: 0 0 4px 0;">Nombre Cliente</p>
|
||||
<p style="font-size: 12px; margin: 0; color: #475569; line-height: 1.4;">
|
||||
<span id="pdf-client-phone">Teléfono</span><br>
|
||||
<span id="pdf-client-address">Dirección</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table style="width: 100%; border-collapse: collapse; margin-bottom: 20px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="background: #f1f5f9; color: #475569; font-weight: 800; text-transform: uppercase; font-size: 10px; padding: 8px 10px; text-align: left; border-bottom: 2px solid #cbd5e1; border-radius: 6px 0 0 0;">Concepto</th>
|
||||
<th style="background: #f1f5f9; color: #475569; font-weight: 800; text-transform: uppercase; font-size: 10px; padding: 8px 10px; text-align: center; border-bottom: 2px solid #cbd5e1;">Cant.</th>
|
||||
<th style="background: #f1f5f9; color: #475569; font-weight: 800; text-transform: uppercase; font-size: 10px; padding: 8px 10px; text-align: right; border-bottom: 2px solid #cbd5e1;">Precio Ud.</th>
|
||||
<th style="background: #f1f5f9; color: #475569; font-weight: 800; text-transform: uppercase; font-size: 10px; padding: 8px 10px; text-align: right; border-bottom: 2px solid #cbd5e1; border-radius: 0 6px 0 0;">Subtotal</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="pdf-items">
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div style="width: 250px; margin-left: auto; border: 1px solid #e2e8f0; border-radius: 8px; overflow: hidden; margin-bottom: 20px;">
|
||||
<div style="display: flex; justify-content: space-between; padding: 8px 15px; font-size: 11px; border-bottom: 1px solid #f1f5f9; color: #475569;">
|
||||
<span style="font-weight: 600; text-transform: uppercase;">Base Imponible</span>
|
||||
<span id="pdf-subtotal" style="font-weight: 800; color: #0f172a;">0.00 €</span>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; padding: 8px 15px; font-size: 11px; border-bottom: 1px solid #f1f5f9; color: #475569;">
|
||||
<span style="font-weight: 600; text-transform: uppercase;">IVA (21%)</span>
|
||||
<span id="pdf-tax" style="font-weight: 800; color: #0f172a;">0.00 €</span>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: space-between; padding: 10px 15px; background: #2563eb; color: white;">
|
||||
<span style="font-weight: 900; font-size: 13px;">TOTAL A PAGAR</span>
|
||||
<span id="pdf-total" style="font-weight: 900; font-size: 16px;">0.00 €</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="position: absolute; bottom: 30px; left: 40px; right: 40px;">
|
||||
<div id="pdf-bank-info" style="margin-bottom: 10px; padding: 12px 15px; background-color: #f0fdf4; border-left: 4px solid #22c55e; border-radius: 0 6px 6px 0; font-size: 11px; color: #166534; display: none;">
|
||||
<strong>INFORMACIÓN DE PAGO</strong><br>
|
||||
Para confirmar el presupuesto, por favor realice una transferencia a la siguiente cuenta:<br>
|
||||
<span id="pdf-bank-account" style="font-size: 14px; font-weight: 900; letter-spacing: 1px; margin-top: 4px; display: inline-block;">ES00 0000 0000 0000 0000</span>
|
||||
</div>
|
||||
|
||||
<div id="pdf-obs-info" style="margin-bottom: 15px; padding: 12px 15px; background-color: #fefce8; border-left: 4px solid #f59e0b; border-radius: 0 6px 6px 0; font-size: 10px; color: #713f12; display: none;">
|
||||
<strong>OBSERVACIONES Y CONDICIONES</strong><br>
|
||||
<span id="pdf-obs-text"></span>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center; font-size: 9px; color: #94a3b8; border-top: 1px solid #e2e8f0; padding-top: 10px;">
|
||||
Documento generado por IntegraRepara - Sistema de Gestión de Asistencias
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
const API_URL = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1'
|
||||
@@ -179,6 +264,7 @@
|
||||
let urlToken = "";
|
||||
let etasToInit = [];
|
||||
let currentQuotes = [];
|
||||
let globalCompanyData = null; // 🛑 NUEVO: Guarda los datos para el PDF
|
||||
|
||||
document.addEventListener("DOMContentLoaded", async () => {
|
||||
lucide.createIcons();
|
||||
@@ -339,6 +425,9 @@
|
||||
document.getElementById('qmDate').innerText = fDate;
|
||||
document.getElementById('qmAmount').innerText = parseFloat(q.total || q.amount || 0).toFixed(2) + "€";
|
||||
|
||||
// 🛑 ENLAZAMOS EL BOTÓN DE DESCARGAR PDF
|
||||
document.getElementById('btnDownloadPdf').setAttribute('onclick', `generatePDF(${q.id})`);
|
||||
|
||||
const modal = document.getElementById('quoteModal');
|
||||
const sheet = document.getElementById('quoteModalSheet');
|
||||
modal.classList.remove('hidden');
|
||||
@@ -356,6 +445,129 @@
|
||||
}, 300);
|
||||
}
|
||||
|
||||
// --- LÓGICA DE PDF AÑADIDA ---
|
||||
async function generatePDF(id) {
|
||||
const budget = currentQuotes.find(b => b.id === id);
|
||||
if(!budget) return showToast("❌ Error: Presupuesto no encontrado");
|
||||
|
||||
showToast("⏳ Generando PDF, espera unos segundos...");
|
||||
|
||||
let empNombre = globalCompanyData?.name || "Empresa Reparadora";
|
||||
let empLogo = globalCompanyData?.logo || null;
|
||||
let empDni = globalCompanyData?.billing_settings?.dni ? `CIF/NIF: ${globalCompanyData.billing_settings.dni}` : "";
|
||||
let empAddress = globalCompanyData?.billing_settings?.address || "";
|
||||
let empCity = globalCompanyData?.billing_settings?.city || "";
|
||||
let empState = globalCompanyData?.billing_settings?.state || "";
|
||||
let empZip = globalCompanyData?.billing_settings?.zip || "";
|
||||
let empIban = globalCompanyData?.billing_settings?.iban || null;
|
||||
let empObs = globalCompanyData?.billing_settings?.obs || null;
|
||||
|
||||
document.getElementById('pdf-company-name').innerText = empNombre;
|
||||
document.getElementById('pdf-company-dni').innerText = empDni;
|
||||
document.getElementById('pdf-company-address').innerText = empAddress;
|
||||
const locText = [empZip, empCity, empState ? `(${empState})` : ''].filter(Boolean).join(' ');
|
||||
document.getElementById('pdf-company-location').innerText = locText;
|
||||
|
||||
const logoImg = document.getElementById('pdf-company-logo');
|
||||
const logoTxt = document.getElementById('pdf-company-name-fallback');
|
||||
if (empLogo) {
|
||||
logoImg.src = empLogo;
|
||||
logoImg.style.display = 'block';
|
||||
logoTxt.style.display = 'none';
|
||||
} else {
|
||||
logoImg.style.display = 'none';
|
||||
logoTxt.innerText = empNombre;
|
||||
logoTxt.style.display = 'block';
|
||||
}
|
||||
|
||||
document.getElementById('pdf-budget-id').innerText = `#${budget.quote_ref || budget.ref || budget.id}`;
|
||||
let fDate = budget.created_at || budget.date || "";
|
||||
if(fDate && fDate.includes('T')) fDate = fDate.split('T')[0].split('-').reverse().join('/');
|
||||
document.getElementById('pdf-date').innerText = fDate;
|
||||
|
||||
document.getElementById('pdf-client-name').innerText = budget.client_name || document.getElementById('clientName').innerText || "Cliente";
|
||||
document.getElementById('pdf-client-phone').innerText = budget.client_phone || "-";
|
||||
document.getElementById('pdf-client-address').innerText = budget.client_address || "-";
|
||||
|
||||
const itemsArr = budget.items || [];
|
||||
let itemsHtml = '';
|
||||
if (itemsArr.length > 0) {
|
||||
itemsHtml = itemsArr.map(item => `
|
||||
<tr>
|
||||
<td style="padding: 8px 10px; border-bottom: 1px solid #e2e8f0; font-size: 12px; color: #334155;">${item.concept}</td>
|
||||
<td style="padding: 8px 10px; border-bottom: 1px solid #e2e8f0; font-size: 12px; color: #334155; text-align: center;">${item.qty}</td>
|
||||
<td style="padding: 8px 10px; border-bottom: 1px solid #e2e8f0; font-size: 12px; color: #334155; text-align: right;">${parseFloat(item.price).toFixed(2)} €</td>
|
||||
<td style="padding: 8px 10px; border-bottom: 1px solid #e2e8f0; font-size: 12px; font-weight: 700; color: #0f172a; text-align: right;">${(item.qty * item.price).toFixed(2)} €</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
} else {
|
||||
itemsHtml = `<tr>
|
||||
<td style="padding: 8px 10px; border-bottom: 1px solid #e2e8f0; font-size: 12px; color: #334155;">${budget.title || "Presupuesto General de Reparación"}</td>
|
||||
<td style="padding: 8px 10px; border-bottom: 1px solid #e2e8f0; font-size: 12px; color: #334155; text-align: center;">1</td>
|
||||
<td style="padding: 8px 10px; border-bottom: 1px solid #e2e8f0; font-size: 12px; color: #334155; text-align: right;">${parseFloat(budget.total || budget.amount || 0).toFixed(2)} €</td>
|
||||
<td style="padding: 8px 10px; border-bottom: 1px solid #e2e8f0; font-size: 12px; font-weight: 700; color: #0f172a; text-align: right;">${parseFloat(budget.total || budget.amount || 0).toFixed(2)} €</td>
|
||||
</tr>`;
|
||||
}
|
||||
document.getElementById('pdf-items').innerHTML = itemsHtml;
|
||||
|
||||
let tot = parseFloat(budget.total || budget.amount || 0);
|
||||
let sub = budget.subtotal ? parseFloat(budget.subtotal) : tot / 1.21;
|
||||
let tx = budget.tax ? parseFloat(budget.tax) : tot - sub;
|
||||
|
||||
document.getElementById('pdf-subtotal').innerText = sub.toFixed(2) + " €";
|
||||
document.getElementById('pdf-tax').innerText = tx.toFixed(2) + " €";
|
||||
document.getElementById('pdf-total').innerText = tot.toFixed(2) + " €";
|
||||
|
||||
const bankContainer = document.getElementById('pdf-bank-info');
|
||||
if (empIban) {
|
||||
document.getElementById('pdf-bank-account').innerText = empIban;
|
||||
bankContainer.style.display = 'block';
|
||||
} else {
|
||||
bankContainer.style.display = 'none';
|
||||
}
|
||||
|
||||
const obsContainer = document.getElementById('pdf-obs-info');
|
||||
if (empObs) {
|
||||
document.getElementById('pdf-obs-text').innerHTML = empObs.replace(/\n/g, '<br>');
|
||||
obsContainer.style.display = 'block';
|
||||
} else {
|
||||
obsContainer.style.display = 'none';
|
||||
}
|
||||
|
||||
const wrapper = document.getElementById('pdf-wrapper');
|
||||
wrapper.classList.remove('hidden');
|
||||
wrapper.style.position = 'absolute';
|
||||
wrapper.style.left = '-9999px';
|
||||
|
||||
const element = document.getElementById('pdf-content');
|
||||
const opt = {
|
||||
margin: 0,
|
||||
filename: `Presupuesto_${budget.quote_ref || budget.id}.pdf`,
|
||||
image: { type: 'jpeg', quality: 1 },
|
||||
html2canvas: { scale: 2, useCORS: true, logging: false },
|
||||
jsPDF: { unit: 'in', format: 'a4', orientation: 'portrait' }
|
||||
};
|
||||
|
||||
try {
|
||||
await new Promise(r => setTimeout(r, 500));
|
||||
await html2pdf().set(opt).from(element).save();
|
||||
showToast("✅ PDF Descargado con éxito");
|
||||
} catch (error) {
|
||||
showToast("❌ Error al generar el PDF");
|
||||
} finally {
|
||||
wrapper.classList.add('hidden');
|
||||
wrapper.style.position = '';
|
||||
wrapper.style.left = '';
|
||||
}
|
||||
}
|
||||
|
||||
function showToast(msg) {
|
||||
const t = document.getElementById('toast');
|
||||
t.innerHTML = msg;
|
||||
t.classList.remove('hidden');
|
||||
setTimeout(() => t.classList.add('hidden'), 3000);
|
||||
}
|
||||
|
||||
// --- FUNCIONES DE TEXTO Y FECHAS ---
|
||||
function summarizeDescription(rawText) {
|
||||
if (!rawText) return "Revisión técnica en el domicilio.";
|
||||
@@ -433,6 +645,8 @@
|
||||
// --- RENDER PRINCIPAL DE AVISOS ---
|
||||
function renderPortal(client, company, allServices) {
|
||||
|
||||
globalCompanyData = company; // 🛑 NUEVO: Guardamos la empresa
|
||||
|
||||
if (company.name) document.title = `Portal - ${company.name}`;
|
||||
if (company.logo) {
|
||||
document.getElementById('companyLogo').src = company.logo;
|
||||
|
||||
Reference in New Issue
Block a user