Logo

DiBlanco Premier

Tax & Accounting Portal

Step 1: Identity

AES-256 Encryption Active
+ item.rate.toFixed(2) + '' + item.qty + ' document.getElementById("btn-close-preview").onclick = () => document.getElementById("modal-preview-invoice").classList.add("hidden"); document.getElementById("btn-close-prev-modal").onclick = () => document.getElementById("modal-preview-invoice").classList.add("hidden"); document.getElementById("btn-print-invoice").onclick = () => window.print(); document.getElementById("btn-send-email-action").onclick = async function() { if (!currentActiveInvoice) return; const token = localStorage.getItem("diblanco_token"); const email = currentActiveInvoice.emailDest || prompt("Enter client email destination:", "client@company.com"); if (!email) return; try { const res = await fetch("/api/invoices/" + currentActiveInvoice.id + "/send", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ channel: "email", destination: email }) }); const data = await res.json(); alert(data.message || "Email successfully sent!"); } catch(e) { alert("Error sending email"); } }; document.getElementById("btn-send-sms-action").onclick = async function() { if (!currentActiveInvoice) return; const token = localStorage.getItem("diblanco_token"); const phone = currentActiveInvoice.phoneDest || prompt("Enter client SMS mobile phone:", "+1 (555) 000-0000"); if (!phone) return; try { const res = await fetch("/api/invoices/" + currentActiveInvoice.id + "/send", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ channel: "sms", destination: phone }) }); const data = await res.json(); alert(data.message || "SMS payment link successfully sent!"); } catch(e) { alert("Error sending SMS"); } }; // RENDER INVOICES DEL CLIENTE EN EXPEDIENTE function renderClientInvoices(client) { const allInvoices = (window.cachedData && window.cachedData.invoices) ? window.cachedData.invoices : []; const clientInvoices = allInvoices.filter(i => i.clientId === client.id); let paidSum = 0; let unpaidSum = 0; const tbody = document.getElementById("client-invoices-tbody"); tbody.innerHTML = ""; if (clientInvoices.length === 0) { tbody.innerHTML = 'No invoices issued for this client account. Click + New Invoice to create one.'; } else { clientInvoices.forEach(inv => { if (inv.status === "Paid") paidSum += inv.amount; if (inv.status === "Unpaid") unpaidSum += inv.amount; const tr = document.createElement("tr"); tr.className = "hover:bg-slate-50 transition-colors"; tr.innerHTML = '#' + inv.number + '' + inv.description + '$' + inv.amount.toFixed(2) + '' + inv.status + ''; tr.querySelectorAll(".btn-view-inv").forEach(b => b.onclick = () => openInvoicePreviewModal(inv)); tr.querySelector(".btn-pay").onclick = () => updateInvoiceStatus(inv.id, "Paid"); tr.querySelector(".btn-void").onclick = () => updateInvoiceStatus(inv.id, "Void"); tbody.appendChild(tr); }); } document.getElementById("billing-summary-text").textContent = "Paid: $" + paidSum.toFixed(2) + " • Unpaid: $" + unpaidSum.toFixed(2); } async function updateInvoiceStatus(invId, newStatus) { const token = localStorage.getItem("diblanco_token"); try { const res = await fetch("/api/invoices/" + invId, { method: "PATCH", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ status: newStatus }) }); if (res.ok) { await loadDash(); if (currentDetailClient) renderClientInvoices(currentDetailClient); } } catch(e) { alert("Error updating invoice"); } } // OPEN EXPEDIENT MODAL function openClientDetailModal(client) { currentDetailClient = client; document.getElementById("detail-avatar").textContent = client.name ? client.name.charAt(0) : "C"; document.getElementById("detail-client-title").textContent = client.name; document.getElementById("detail-client-ssn").textContent = "Masked Tax ID: " + (client.ssnMasked || "N/A") + " • AES-256 Encrypted"; renderClientInvoices(client); document.getElementById("modal-client-detail").classList.remove("hidden"); } document.getElementById("btn-close-detail").onclick = () => document.getElementById("modal-client-detail").classList.add("hidden"); // CREATE INVOICE MODAL HANDLERS document.getElementById("btn-open-create-invoice").onclick = () => { if (currentDetailClient) { document.getElementById("inv-account-name").value = currentDetailClient.name; document.getElementById("inv-email-dest").value = currentDetailClient.email || ""; document.getElementById("inv-date").value = new Date().toISOString().split("T")[0]; const nextNum = (window.cachedData && window.cachedData.nextInvoiceNumber) ? window.cachedData.nextInvoiceNumber : 1001; document.getElementById("inv-next-number").textContent = "Invoice #" + nextNum; document.getElementById("inv-discount").value = "0.00"; document.getElementById("inv-tax-rate").value = "0.00"; const container = document.getElementById("line-items-container"); container.innerHTML = '
$0.00
'; container.querySelector(".item-rate").oninput = calculateInvoiceTotals; container.querySelector(".item-qty").oninput = calculateInvoiceTotals; calculateInvoiceTotals(); document.getElementById("modal-add-invoice").classList.remove("hidden"); } }; document.getElementById("btn-close-invoice").onclick = () => document.getElementById("modal-add-invoice").classList.add("hidden"); document.getElementById("btn-cancel-invoice").onclick = () => document.getElementById("modal-add-invoice").classList.add("hidden"); document.getElementById("form-add-invoice").onsubmit = async function(e) { e.preventDefault(); const token = localStorage.getItem("diblanco_token"); const description = document.getElementById("inv-desc").value; const emailDest = document.getElementById("inv-email-dest").value; const phoneDest = document.getElementById("inv-phone-dest").value; const finalAmount = calculateInvoiceTotals(); try { const res = await fetch("/api/invoices", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ clientId: currentDetailClient.id, description, amount: finalAmount, emailDest, phoneDest }) }); if (res.ok) { document.getElementById("modal-add-invoice").classList.add("hidden"); document.getElementById("form-add-invoice").reset(); await loadDash(); if (currentDetailClient) renderClientInvoices(currentDetailClient); } else { alert("Error creating invoice"); } } catch(e) { alert("Server connection error"); } }; // TAB SWITCHING document.querySelectorAll("#expedient-tabs .tab-btn").forEach(btn => { btn.onclick = function() { document.querySelectorAll("#expedient-tabs .tab-btn").forEach(b => b.classList.remove("active")); document.querySelectorAll(".tab-content").forEach(c => c.classList.add("hidden")); this.classList.add("active"); const target = this.getAttribute("data-tab"); document.getElementById(target).classList.remove("hidden"); }; }); // SIDEBAR NAV document.querySelectorAll("#sidebar-menu .nav-link").forEach(link => { link.onclick = function() { document.querySelectorAll("#sidebar-menu .nav-link").forEach(l => l.classList.remove("active")); this.classList.add("active"); const mod = this.getAttribute("data-module"); document.getElementById("view-clients").classList.add("hidden"); document.getElementById("view-integrations").classList.add("hidden"); if (mod === "integrations") { document.getElementById("view-integrations").classList.remove("hidden"); } else { document.getElementById("view-clients").classList.remove("hidden"); } }; }); // ADD CLIENT document.getElementById("btn-open-add-client").onclick = () => document.getElementById("modal-add-client").classList.remove("hidden"); document.getElementById("btn-close-add-client").onclick = () => document.getElementById("modal-add-client").classList.add("hidden"); document.getElementById("form-add-client").onsubmit = async function(e) { e.preventDefault(); const token = localStorage.getItem("diblanco_token"); const name = document.getElementById("new-client-name").value; const ssn = document.getElementById("new-client-ssn").value; const email = document.getElementById("new-client-email").value; try { const res = await fetch("/api/clients", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ name, ssn, email }) }); const data = await res.json(); if (res.ok) { document.getElementById("modal-add-client").classList.add("hidden"); document.getElementById("form-add-client").reset(); loadDash(); } else { alert(data.error || "Error adding client"); } } catch(err) { alert("Server connection error"); } }; function doLogout() { localStorage.removeItem("diblanco_token"); window.location.reload(); } document.getElementById("btn-logout").onclick = doLogout; applyLang(); if (localStorage.getItem("diblanco_token")) loadDash(); + (item.rate * item.qty).toFixed(2) + ''; }); } else { tbody.innerHTML = '' + (inv.description || "Tax & Accounting Services") + ' document.getElementById("btn-close-preview").onclick = () => document.getElementById("modal-preview-invoice").classList.add("hidden"); document.getElementById("btn-close-prev-modal").onclick = () => document.getElementById("modal-preview-invoice").classList.add("hidden"); document.getElementById("btn-print-invoice").onclick = () => window.print(); document.getElementById("btn-send-email-action").onclick = async function() { if (!currentActiveInvoice) return; const token = localStorage.getItem("diblanco_token"); const email = currentActiveInvoice.emailDest || prompt("Enter client email destination:", "client@company.com"); if (!email) return; try { const res = await fetch("/api/invoices/" + currentActiveInvoice.id + "/send", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ channel: "email", destination: email }) }); const data = await res.json(); alert(data.message || "Email successfully sent!"); } catch(e) { alert("Error sending email"); } }; document.getElementById("btn-send-sms-action").onclick = async function() { if (!currentActiveInvoice) return; const token = localStorage.getItem("diblanco_token"); const phone = currentActiveInvoice.phoneDest || prompt("Enter client SMS mobile phone:", "+1 (555) 000-0000"); if (!phone) return; try { const res = await fetch("/api/invoices/" + currentActiveInvoice.id + "/send", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ channel: "sms", destination: phone }) }); const data = await res.json(); alert(data.message || "SMS payment link successfully sent!"); } catch(e) { alert("Error sending SMS"); } }; // RENDER INVOICES DEL CLIENTE EN EXPEDIENTE function renderClientInvoices(client) { const allInvoices = (window.cachedData && window.cachedData.invoices) ? window.cachedData.invoices : []; const clientInvoices = allInvoices.filter(i => i.clientId === client.id); let paidSum = 0; let unpaidSum = 0; const tbody = document.getElementById("client-invoices-tbody"); tbody.innerHTML = ""; if (clientInvoices.length === 0) { tbody.innerHTML = 'No invoices issued for this client account. Click + New Invoice to create one.'; } else { clientInvoices.forEach(inv => { if (inv.status === "Paid") paidSum += inv.amount; if (inv.status === "Unpaid") unpaidSum += inv.amount; const tr = document.createElement("tr"); tr.className = "hover:bg-slate-50 transition-colors"; tr.innerHTML = '#' + inv.number + '' + inv.description + '$' + inv.amount.toFixed(2) + '' + inv.status + ''; tr.querySelectorAll(".btn-view-inv").forEach(b => b.onclick = () => openInvoicePreviewModal(inv)); tr.querySelector(".btn-pay").onclick = () => updateInvoiceStatus(inv.id, "Paid"); tr.querySelector(".btn-void").onclick = () => updateInvoiceStatus(inv.id, "Void"); tbody.appendChild(tr); }); } document.getElementById("billing-summary-text").textContent = "Paid: $" + paidSum.toFixed(2) + " • Unpaid: $" + unpaidSum.toFixed(2); } async function updateInvoiceStatus(invId, newStatus) { const token = localStorage.getItem("diblanco_token"); try { const res = await fetch("/api/invoices/" + invId, { method: "PATCH", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ status: newStatus }) }); if (res.ok) { await loadDash(); if (currentDetailClient) renderClientInvoices(currentDetailClient); } } catch(e) { alert("Error updating invoice"); } } // OPEN EXPEDIENT MODAL function openClientDetailModal(client) { currentDetailClient = client; document.getElementById("detail-avatar").textContent = client.name ? client.name.charAt(0) : "C"; document.getElementById("detail-client-title").textContent = client.name; document.getElementById("detail-client-ssn").textContent = "Masked Tax ID: " + (client.ssnMasked || "N/A") + " • AES-256 Encrypted"; renderClientInvoices(client); document.getElementById("modal-client-detail").classList.remove("hidden"); } document.getElementById("btn-close-detail").onclick = () => document.getElementById("modal-client-detail").classList.add("hidden"); // CREATE INVOICE MODAL HANDLERS document.getElementById("btn-open-create-invoice").onclick = () => { if (currentDetailClient) { document.getElementById("inv-account-name").value = currentDetailClient.name; document.getElementById("inv-email-dest").value = currentDetailClient.email || ""; document.getElementById("inv-date").value = new Date().toISOString().split("T")[0]; const nextNum = (window.cachedData && window.cachedData.nextInvoiceNumber) ? window.cachedData.nextInvoiceNumber : 1001; document.getElementById("inv-next-number").textContent = "Invoice #" + nextNum; document.getElementById("inv-discount").value = "0.00"; document.getElementById("inv-tax-rate").value = "0.00"; const container = document.getElementById("line-items-container"); container.innerHTML = '
$0.00
'; container.querySelector(".item-rate").oninput = calculateInvoiceTotals; container.querySelector(".item-qty").oninput = calculateInvoiceTotals; calculateInvoiceTotals(); document.getElementById("modal-add-invoice").classList.remove("hidden"); } }; document.getElementById("btn-close-invoice").onclick = () => document.getElementById("modal-add-invoice").classList.add("hidden"); document.getElementById("btn-cancel-invoice").onclick = () => document.getElementById("modal-add-invoice").classList.add("hidden"); document.getElementById("form-add-invoice").onsubmit = async function(e) { e.preventDefault(); const token = localStorage.getItem("diblanco_token"); const description = document.getElementById("inv-desc").value; const emailDest = document.getElementById("inv-email-dest").value; const phoneDest = document.getElementById("inv-phone-dest").value; const finalAmount = calculateInvoiceTotals(); try { const res = await fetch("/api/invoices", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ clientId: currentDetailClient.id, description, amount: finalAmount, emailDest, phoneDest }) }); if (res.ok) { document.getElementById("modal-add-invoice").classList.add("hidden"); document.getElementById("form-add-invoice").reset(); await loadDash(); if (currentDetailClient) renderClientInvoices(currentDetailClient); } else { alert("Error creating invoice"); } } catch(e) { alert("Server connection error"); } }; // TAB SWITCHING document.querySelectorAll("#expedient-tabs .tab-btn").forEach(btn => { btn.onclick = function() { document.querySelectorAll("#expedient-tabs .tab-btn").forEach(b => b.classList.remove("active")); document.querySelectorAll(".tab-content").forEach(c => c.classList.add("hidden")); this.classList.add("active"); const target = this.getAttribute("data-tab"); document.getElementById(target).classList.remove("hidden"); }; }); // SIDEBAR NAV document.querySelectorAll("#sidebar-menu .nav-link").forEach(link => { link.onclick = function() { document.querySelectorAll("#sidebar-menu .nav-link").forEach(l => l.classList.remove("active")); this.classList.add("active"); const mod = this.getAttribute("data-module"); document.getElementById("view-clients").classList.add("hidden"); document.getElementById("view-integrations").classList.add("hidden"); if (mod === "integrations") { document.getElementById("view-integrations").classList.remove("hidden"); } else { document.getElementById("view-clients").classList.remove("hidden"); } }; }); // ADD CLIENT document.getElementById("btn-open-add-client").onclick = () => document.getElementById("modal-add-client").classList.remove("hidden"); document.getElementById("btn-close-add-client").onclick = () => document.getElementById("modal-add-client").classList.add("hidden"); document.getElementById("form-add-client").onsubmit = async function(e) { e.preventDefault(); const token = localStorage.getItem("diblanco_token"); const name = document.getElementById("new-client-name").value; const ssn = document.getElementById("new-client-ssn").value; const email = document.getElementById("new-client-email").value; try { const res = await fetch("/api/clients", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ name, ssn, email }) }); const data = await res.json(); if (res.ok) { document.getElementById("modal-add-client").classList.add("hidden"); document.getElementById("form-add-client").reset(); loadDash(); } else { alert(data.error || "Error adding client"); } } catch(err) { alert("Server connection error"); } }; function doLogout() { localStorage.removeItem("diblanco_token"); window.location.reload(); } document.getElementById("btn-logout").onclick = doLogout; applyLang(); if (localStorage.getItem("diblanco_token")) loadDash(); + inv.amount.toFixed(2) + '1 document.getElementById("btn-close-preview").onclick = () => document.getElementById("modal-preview-invoice").classList.add("hidden"); document.getElementById("btn-close-prev-modal").onclick = () => document.getElementById("modal-preview-invoice").classList.add("hidden"); document.getElementById("btn-print-invoice").onclick = () => window.print(); document.getElementById("btn-send-email-action").onclick = async function() { if (!currentActiveInvoice) return; const token = localStorage.getItem("diblanco_token"); const email = currentActiveInvoice.emailDest || prompt("Enter client email destination:", "client@company.com"); if (!email) return; try { const res = await fetch("/api/invoices/" + currentActiveInvoice.id + "/send", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ channel: "email", destination: email }) }); const data = await res.json(); alert(data.message || "Email successfully sent!"); } catch(e) { alert("Error sending email"); } }; document.getElementById("btn-send-sms-action").onclick = async function() { if (!currentActiveInvoice) return; const token = localStorage.getItem("diblanco_token"); const phone = currentActiveInvoice.phoneDest || prompt("Enter client SMS mobile phone:", "+1 (555) 000-0000"); if (!phone) return; try { const res = await fetch("/api/invoices/" + currentActiveInvoice.id + "/send", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ channel: "sms", destination: phone }) }); const data = await res.json(); alert(data.message || "SMS payment link successfully sent!"); } catch(e) { alert("Error sending SMS"); } }; // RENDER INVOICES DEL CLIENTE EN EXPEDIENTE function renderClientInvoices(client) { const allInvoices = (window.cachedData && window.cachedData.invoices) ? window.cachedData.invoices : []; const clientInvoices = allInvoices.filter(i => i.clientId === client.id); let paidSum = 0; let unpaidSum = 0; const tbody = document.getElementById("client-invoices-tbody"); tbody.innerHTML = ""; if (clientInvoices.length === 0) { tbody.innerHTML = 'No invoices issued for this client account. Click + New Invoice to create one.'; } else { clientInvoices.forEach(inv => { if (inv.status === "Paid") paidSum += inv.amount; if (inv.status === "Unpaid") unpaidSum += inv.amount; const tr = document.createElement("tr"); tr.className = "hover:bg-slate-50 transition-colors"; tr.innerHTML = '#' + inv.number + '' + inv.description + '$' + inv.amount.toFixed(2) + '' + inv.status + ''; tr.querySelectorAll(".btn-view-inv").forEach(b => b.onclick = () => openInvoicePreviewModal(inv)); tr.querySelector(".btn-pay").onclick = () => updateInvoiceStatus(inv.id, "Paid"); tr.querySelector(".btn-void").onclick = () => updateInvoiceStatus(inv.id, "Void"); tbody.appendChild(tr); }); } document.getElementById("billing-summary-text").textContent = "Paid: $" + paidSum.toFixed(2) + " • Unpaid: $" + unpaidSum.toFixed(2); } async function updateInvoiceStatus(invId, newStatus) { const token = localStorage.getItem("diblanco_token"); try { const res = await fetch("/api/invoices/" + invId, { method: "PATCH", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ status: newStatus }) }); if (res.ok) { await loadDash(); if (currentDetailClient) renderClientInvoices(currentDetailClient); } } catch(e) { alert("Error updating invoice"); } } // OPEN EXPEDIENT MODAL function openClientDetailModal(client) { currentDetailClient = client; document.getElementById("detail-avatar").textContent = client.name ? client.name.charAt(0) : "C"; document.getElementById("detail-client-title").textContent = client.name; document.getElementById("detail-client-ssn").textContent = "Masked Tax ID: " + (client.ssnMasked || "N/A") + " • AES-256 Encrypted"; renderClientInvoices(client); document.getElementById("modal-client-detail").classList.remove("hidden"); } document.getElementById("btn-close-detail").onclick = () => document.getElementById("modal-client-detail").classList.add("hidden"); // CREATE INVOICE MODAL HANDLERS document.getElementById("btn-open-create-invoice").onclick = () => { if (currentDetailClient) { document.getElementById("inv-account-name").value = currentDetailClient.name; document.getElementById("inv-email-dest").value = currentDetailClient.email || ""; document.getElementById("inv-date").value = new Date().toISOString().split("T")[0]; const nextNum = (window.cachedData && window.cachedData.nextInvoiceNumber) ? window.cachedData.nextInvoiceNumber : 1001; document.getElementById("inv-next-number").textContent = "Invoice #" + nextNum; document.getElementById("inv-discount").value = "0.00"; document.getElementById("inv-tax-rate").value = "0.00"; const container = document.getElementById("line-items-container"); container.innerHTML = '
$0.00
'; container.querySelector(".item-rate").oninput = calculateInvoiceTotals; container.querySelector(".item-qty").oninput = calculateInvoiceTotals; calculateInvoiceTotals(); document.getElementById("modal-add-invoice").classList.remove("hidden"); } }; document.getElementById("btn-close-invoice").onclick = () => document.getElementById("modal-add-invoice").classList.add("hidden"); document.getElementById("btn-cancel-invoice").onclick = () => document.getElementById("modal-add-invoice").classList.add("hidden"); document.getElementById("form-add-invoice").onsubmit = async function(e) { e.preventDefault(); const token = localStorage.getItem("diblanco_token"); const description = document.getElementById("inv-desc").value; const emailDest = document.getElementById("inv-email-dest").value; const phoneDest = document.getElementById("inv-phone-dest").value; const finalAmount = calculateInvoiceTotals(); try { const res = await fetch("/api/invoices", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ clientId: currentDetailClient.id, description, amount: finalAmount, emailDest, phoneDest }) }); if (res.ok) { document.getElementById("modal-add-invoice").classList.add("hidden"); document.getElementById("form-add-invoice").reset(); await loadDash(); if (currentDetailClient) renderClientInvoices(currentDetailClient); } else { alert("Error creating invoice"); } } catch(e) { alert("Server connection error"); } }; // TAB SWITCHING document.querySelectorAll("#expedient-tabs .tab-btn").forEach(btn => { btn.onclick = function() { document.querySelectorAll("#expedient-tabs .tab-btn").forEach(b => b.classList.remove("active")); document.querySelectorAll(".tab-content").forEach(c => c.classList.add("hidden")); this.classList.add("active"); const target = this.getAttribute("data-tab"); document.getElementById(target).classList.remove("hidden"); }; }); // SIDEBAR NAV document.querySelectorAll("#sidebar-menu .nav-link").forEach(link => { link.onclick = function() { document.querySelectorAll("#sidebar-menu .nav-link").forEach(l => l.classList.remove("active")); this.classList.add("active"); const mod = this.getAttribute("data-module"); document.getElementById("view-clients").classList.add("hidden"); document.getElementById("view-integrations").classList.add("hidden"); if (mod === "integrations") { document.getElementById("view-integrations").classList.remove("hidden"); } else { document.getElementById("view-clients").classList.remove("hidden"); } }; }); // ADD CLIENT document.getElementById("btn-open-add-client").onclick = () => document.getElementById("modal-add-client").classList.remove("hidden"); document.getElementById("btn-close-add-client").onclick = () => document.getElementById("modal-add-client").classList.add("hidden"); document.getElementById("form-add-client").onsubmit = async function(e) { e.preventDefault(); const token = localStorage.getItem("diblanco_token"); const name = document.getElementById("new-client-name").value; const ssn = document.getElementById("new-client-ssn").value; const email = document.getElementById("new-client-email").value; try { const res = await fetch("/api/clients", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ name, ssn, email }) }); const data = await res.json(); if (res.ok) { document.getElementById("modal-add-client").classList.add("hidden"); document.getElementById("form-add-client").reset(); loadDash(); } else { alert(data.error || "Error adding client"); } } catch(err) { alert("Server connection error"); } }; function doLogout() { localStorage.removeItem("diblanco_token"); window.location.reload(); } document.getElementById("btn-logout").onclick = doLogout; applyLang(); if (localStorage.getItem("diblanco_token")) loadDash(); + inv.amount.toFixed(2) + ''; } document.getElementById("prev-inv-subtotal").textContent = "$" + inv.amount.toFixed(2); document.getElementById("prev-inv-tax").textContent = "$" + (inv.taxRate ? (inv.amount * (inv.taxRate / 100)).toFixed(2) : "0.00"); document.getElementById("prev-inv-total").textContent = "$" + inv.amount.toFixed(2); if (inv.note) document.getElementById("prev-inv-note").textContent = inv.note; document.getElementById("modal-preview-invoice").classList.remove("hidden"); } document.getElementById("btn-close-preview").onclick = () => document.getElementById("modal-preview-invoice").classList.add("hidden"); document.getElementById("btn-close-prev-modal").onclick = () => document.getElementById("modal-preview-invoice").classList.add("hidden"); document.getElementById("btn-print-invoice").onclick = () => window.print(); document.getElementById("btn-send-email-action").onclick = async function() { if (!currentActiveInvoice) return; const token = localStorage.getItem("diblanco_token"); const email = currentActiveInvoice.emailDest || prompt("Enter client email destination:", "client@company.com"); if (!email) return; try { const res = await fetch("/api/invoices/" + currentActiveInvoice.id + "/send", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ channel: "email", destination: email }) }); const data = await res.json(); alert(data.message || "Email successfully sent!"); } catch(e) { alert("Error sending email"); } }; document.getElementById("btn-send-sms-action").onclick = async function() { if (!currentActiveInvoice) return; const token = localStorage.getItem("diblanco_token"); const phone = currentActiveInvoice.phoneDest || prompt("Enter client SMS mobile phone:", "+1 (555) 000-0000"); if (!phone) return; try { const res = await fetch("/api/invoices/" + currentActiveInvoice.id + "/send", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ channel: "sms", destination: phone }) }); const data = await res.json(); alert(data.message || "SMS payment link successfully sent!"); } catch(e) { alert("Error sending SMS"); } }; // RENDER INVOICES DEL CLIENTE EN EXPEDIENTE function renderClientInvoices(client) { const allInvoices = (window.cachedData && window.cachedData.invoices) ? window.cachedData.invoices : []; const clientInvoices = allInvoices.filter(i => i.clientId === client.id); let paidSum = 0; let unpaidSum = 0; const tbody = document.getElementById("client-invoices-tbody"); tbody.innerHTML = ""; if (clientInvoices.length === 0) { tbody.innerHTML = 'No invoices issued for this client account. Click + New Invoice to create one.'; } else { clientInvoices.forEach(inv => { if (inv.status === "Paid") paidSum += inv.amount; if (inv.status === "Unpaid") unpaidSum += inv.amount; const tr = document.createElement("tr"); tr.className = "hover:bg-slate-50 transition-colors"; tr.innerHTML = '#' + inv.number + '' + inv.description + '$' + inv.amount.toFixed(2) + '' + inv.status + ''; tr.querySelectorAll(".btn-view-inv").forEach(b => b.onclick = () => openInvoicePreviewModal(inv)); tr.querySelector(".btn-pay").onclick = () => updateInvoiceStatus(inv.id, "Paid"); tr.querySelector(".btn-void").onclick = () => updateInvoiceStatus(inv.id, "Void"); tbody.appendChild(tr); }); } document.getElementById("billing-summary-text").textContent = "Paid: $" + paidSum.toFixed(2) + " • Unpaid: $" + unpaidSum.toFixed(2); } async function updateInvoiceStatus(invId, newStatus) { const token = localStorage.getItem("diblanco_token"); try { const res = await fetch("/api/invoices/" + invId, { method: "PATCH", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ status: newStatus }) }); if (res.ok) { await loadDash(); if (currentDetailClient) renderClientInvoices(currentDetailClient); } } catch(e) { alert("Error updating invoice"); } } // OPEN EXPEDIENT MODAL function openClientDetailModal(client) { currentDetailClient = client; document.getElementById("detail-avatar").textContent = client.name ? client.name.charAt(0) : "C"; document.getElementById("detail-client-title").textContent = client.name; document.getElementById("detail-client-ssn").textContent = "Masked Tax ID: " + (client.ssnMasked || "N/A") + " • AES-256 Encrypted"; renderClientInvoices(client); document.getElementById("modal-client-detail").classList.remove("hidden"); } document.getElementById("btn-close-detail").onclick = () => document.getElementById("modal-client-detail").classList.add("hidden"); // CREATE INVOICE MODAL HANDLERS document.getElementById("btn-open-create-invoice").onclick = () => { if (currentDetailClient) { document.getElementById("inv-account-name").value = currentDetailClient.name; document.getElementById("inv-email-dest").value = currentDetailClient.email || ""; document.getElementById("inv-date").value = new Date().toISOString().split("T")[0]; const nextNum = (window.cachedData && window.cachedData.nextInvoiceNumber) ? window.cachedData.nextInvoiceNumber : 1001; document.getElementById("inv-next-number").textContent = "Invoice #" + nextNum; document.getElementById("inv-discount").value = "0.00"; document.getElementById("inv-tax-rate").value = "0.00"; const container = document.getElementById("line-items-container"); container.innerHTML = '
$0.00
'; container.querySelector(".item-rate").oninput = calculateInvoiceTotals; container.querySelector(".item-qty").oninput = calculateInvoiceTotals; calculateInvoiceTotals(); document.getElementById("modal-add-invoice").classList.remove("hidden"); } }; document.getElementById("btn-close-invoice").onclick = () => document.getElementById("modal-add-invoice").classList.add("hidden"); document.getElementById("btn-cancel-invoice").onclick = () => document.getElementById("modal-add-invoice").classList.add("hidden"); document.getElementById("form-add-invoice").onsubmit = async function(e) { e.preventDefault(); const token = localStorage.getItem("diblanco_token"); const description = document.getElementById("inv-desc").value; const emailDest = document.getElementById("inv-email-dest").value; const phoneDest = document.getElementById("inv-phone-dest").value; const finalAmount = calculateInvoiceTotals(); try { const res = await fetch("/api/invoices", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ clientId: currentDetailClient.id, description, amount: finalAmount, emailDest, phoneDest }) }); if (res.ok) { document.getElementById("modal-add-invoice").classList.add("hidden"); document.getElementById("form-add-invoice").reset(); await loadDash(); if (currentDetailClient) renderClientInvoices(currentDetailClient); } else { alert("Error creating invoice"); } } catch(e) { alert("Server connection error"); } }; // TAB SWITCHING document.querySelectorAll("#expedient-tabs .tab-btn").forEach(btn => { btn.onclick = function() { document.querySelectorAll("#expedient-tabs .tab-btn").forEach(b => b.classList.remove("active")); document.querySelectorAll(".tab-content").forEach(c => c.classList.add("hidden")); this.classList.add("active"); const target = this.getAttribute("data-tab"); document.getElementById(target).classList.remove("hidden"); }; }); // SIDEBAR NAV document.querySelectorAll("#sidebar-menu .nav-link").forEach(link => { link.onclick = function() { document.querySelectorAll("#sidebar-menu .nav-link").forEach(l => l.classList.remove("active")); this.classList.add("active"); const mod = this.getAttribute("data-module"); document.getElementById("view-clients").classList.add("hidden"); document.getElementById("view-integrations").classList.add("hidden"); if (mod === "integrations") { document.getElementById("view-integrations").classList.remove("hidden"); } else { document.getElementById("view-clients").classList.remove("hidden"); } }; }); // ADD CLIENT document.getElementById("btn-open-add-client").onclick = () => document.getElementById("modal-add-client").classList.remove("hidden"); document.getElementById("btn-close-add-client").onclick = () => document.getElementById("modal-add-client").classList.add("hidden"); document.getElementById("form-add-client").onsubmit = async function(e) { e.preventDefault(); const token = localStorage.getItem("diblanco_token"); const name = document.getElementById("new-client-name").value; const ssn = document.getElementById("new-client-ssn").value; const email = document.getElementById("new-client-email").value; try { const res = await fetch("/api/clients", { method: "POST", headers: { "Content-Type": "application/json", "Authorization": "Bearer " + token }, body: JSON.stringify({ name, ssn, email }) }); const data = await res.json(); if (res.ok) { document.getElementById("modal-add-client").classList.add("hidden"); document.getElementById("form-add-client").reset(); loadDash(); } else { alert(data.error || "Error adding client"); } } catch(err) { alert("Server connection error"); } }; function doLogout() { localStorage.removeItem("diblanco_token"); window.location.reload(); } document.getElementById("btn-logout").onclick = doLogout; applyLang(); if (localStorage.getItem("diblanco_token")) loadDash();