GST Invoice Pro Dashboard
Recent Invoices
No invoices yet. Create your first one!
Invoice #
Date
Customer
Amount
Status
Actions
Create New Invoice
`);
printWindow.document.close();
setTimeout(() => {
printWindow.focus();
printWindow.print();
}, 250);
}function downloadPDF() {
const inv = allInvoices.find(i => i.__backendId === currentPreviewId);
if (!inv) { showToast('Invoice not found', 'error'); return; }
showToast('📄 Click Print button above, then choose "Save as PDF" from your printer options', 'info');
}// ============ TOAST ============
function showToast(msg, type = 'info') {
const c = document.getElementById('toastContainer');
const colors = { success: 'bg-emerald-500', error: 'bg-rose-500', warning: 'bg-amber-500', info: 'bg-slate-700' };
const t = document.createElement('div');
t.className = `toast-enter ${colors[type]} text-white px-4 py-2.5 rounded-lg text-sm font-medium shadow-lg flex items-center gap-2 max-w-xs`;
const icons = { success: 'check-circle', error: 'x-circle', warning: 'alert-triangle', info: 'info' };
t.innerHTML = `
${escHtml(msg)}`;
c.appendChild(t);
lucide.createIcons();
setTimeout(() => { t.style.opacity = '0'; t.style.transition = 'opacity 0.3s'; setTimeout(() => t.remove(), 300); }, 3000);
}function escHtml(s) { const d = document.createElement('div'); d.textContent = s || ''; return d.innerHTML; }// ============ DATA SDK ============
const dataHandler = {
onDataChanged(data) {
allInvoices = data.filter(d => d.type === 'invoice');
const currentView = document.querySelector('.view-panel:not(.hidden)');
if (currentView) {
if (currentView.id === 'view-invoices') filterInvoices();
if (currentView.id === 'view-dashboard') updateDashboard();
}
}
};// ============ ELEMENT SDK ============
const defaultConfig = {
app_title: 'GST Invoice Pro',
company_tagline: 'Professional GST Billing',
background_color: '#020617',
surface_color: '#0f172a',
text_color: '#e2e8f0',
primary_action_color: '#10b981',
secondary_action_color: '#64748b',
font_family: 'DM Sans',
font_size: 14
};function applyConfig(config) {
const c = { ...defaultConfig, ...config };
document.getElementById('appTitle').textContent = c.app_title;
document.getElementById('appTagline').textContent = c.company_tagline;const shell = document.getElementById('appShell');
shell.style.backgroundColor = c.background_color;
shell.style.color = c.text_color;
shell.style.fontFamily = `${c.font_family}, DM Sans, sans-serif`;// Apply colors via CSS custom properties on the shell
document.querySelectorAll('.bg-slate-900').forEach(el => el.style.backgroundColor = c.surface_color);
document.querySelectorAll('.bg-emerald-500').forEach(el => { el.style.backgroundColor = c.primary_action_color; });
document.querySelectorAll('.text-emerald-400, .text-emerald-500').forEach(el => el.style.color = c.primary_action_color);
}(async () => {
populateStateDropdowns();
renderLineItems();if (window.elementSdk) {
window.elementSdk.init({
defaultConfig,
onConfigChange: async (config) => applyConfig(config),
mapToCapabilities: (config) => ({
recolorables: [
{ get: () => config.background_color || defaultConfig.background_color, set: (v) => { config.background_color = v; window.elementSdk.setConfig({ background_color: v }); } },
{ get: () => config.surface_color || defaultConfig.surface_color, set: (v) => { config.surface_color = v; window.elementSdk.setConfig({ surface_color: v }); } },
{ get: () => config.text_color || defaultConfig.text_color, set: (v) => { config.text_color = v; window.elementSdk.setConfig({ text_color: v }); } },
{ get: () => config.primary_action_color || defaultConfig.primary_action_color, set: (v) => { config.primary_action_color = v; window.elementSdk.setConfig({ primary_action_color: v }); } },
{ get: () => config.secondary_action_color || defaultConfig.secondary_action_color, set: (v) => { config.secondary_action_color = v; window.elementSdk.setConfig({ secondary_action_color: v }); } }
],
borderables: [],
fontEditable: {
get: () => config.font_family || defaultConfig.font_family,
set: (v) => { config.font_family = v; window.elementSdk.setConfig({ font_family: v }); }
},
fontSizeable: {
get: () => config.font_size || defaultConfig.font_size,
set: (v) => { config.font_size = v; window.elementSdk.setConfig({ font_size: v }); }
}
}),
mapToEditPanelValues: (config) => new Map([
['app_title', config.app_title || defaultConfig.app_title],
['company_tagline', config.company_tagline || defaultConfig.company_tagline]
])
});
}if (window.dataSdk) {
const result = await window.dataSdk.init(dataHandler);
if (!result.isOk) console.error('Data SDK init failed');
}lucide.createIcons();
showView('dashboard');
})();