/** * Main App Logic for Intaleq Dashboard (Vanilla Version) */ const app = { state: { tenant: null, keys: [], showKeys: {}, // { id: boolean } activePage: 'home' }, init: async () => { console.log('🚀 Dashboard Initializing Components...'); app.bindEvents(); app.handleRouting(); lucide.createIcons(); }, onAuthenticated: async () => { console.log('🔑 User Authenticated, Fetching Data...'); await app.fetchData(); app.updateStats(); }, bindEvents: () => { window.addEventListener('hashchange', app.handleRouting); // Form submission for new key const createKeyForm = document.getElementById('create-key-form'); if (createKeyForm) { createKeyForm.addEventListener('submit', async (e) => { e.preventDefault(); await app.createKey(); }); } }, handleRouting: () => { const hash = window.location.hash.replace('#', '') || 'home'; app.state.activePage = hash; // Update UI document.querySelectorAll('.page-section').forEach(s => s.classList.remove('active')); const activeSection = document.getElementById(hash); if (activeSection) activeSection.classList.add('active'); // Update Nav document.querySelectorAll('.nav-link').forEach(l => l.classList.remove('active')); const activeLink = document.getElementById(`nav-${hash}`); if (activeLink) activeLink.classList.add('active'); // Specialized page logic if (hash === 'playground') { playground.init(app.state.keys); } else if (hash === 'analytics') { analytics.init(); } else if (hash === 'billing') { billing.init(); } else if (hash === 'refinement') { refinement.init(); } else if (hash === 'docs') { docs.init(); } }, fetchData: async () => { const headers = auth.getAuthHeader(); try { // Fetch Tenant const tenantRes = await fetch('/api/auth/management/me', { headers }); if (tenantRes.ok) { app.state.tenant = await tenantRes.json(); app.updateHeader(); } // Fetch Keys if (app.state.tenant) { const keysRes = await fetch('/api/auth/management/keys', { headers }); if (keysRes.ok) { app.state.keys = await keysRes.json(); app.renderKeysTable(); } } } catch (error) { console.error('Failed to fetch dashboard data', error); } }, updateHeader: () => { if (!app.state.tenant) return; document.getElementById('tenant-name').textContent = app.state.tenant.name; document.getElementById('tenant-email').textContent = app.state.tenant.email; document.getElementById('welcome-msg').textContent = `Welcome back, ${app.state.tenant.name.split(' ')[0]}`; // Role-based UI visibility if (app.state.tenant.role === 'ADMIN') { const auditLink = document.getElementById('nav-refinement'); if (auditLink) auditLink.classList.remove('hidden'); } // Update user avatar if photoUrl exists if (app.state.tenant.photoUrl) { const avatarContainer = document.querySelector('.w-10.h-10.rounded-full.bg-slate-900'); if (avatarContainer) { avatarContainer.innerHTML = `Profile`; } } }, updateStats: async () => { try { const headers = auth.getAuthHeader(); const res = await fetch('/api/usage/summary', { headers }); if (res.ok) { const data = await res.json(); // Update KPI Cards document.getElementById('active-keys-count').textContent = app.state.keys.length; // Detailed Stats from getUsageSummary if (document.getElementById('stat-total-req')) { document.getElementById('stat-total-req').textContent = data.monthlyUsage.toLocaleString(); } if (document.getElementById('stat-success-percent')) { document.getElementById('stat-success-percent').textContent = `${data.successRate}%`; document.getElementById('stat-success-rate').textContent = data.successRate >= 95 ? 'Excellent' : 'Stable'; } if (document.getElementById('stat-avg-latency')) { document.getElementById('stat-avg-latency').textContent = `${data.avgLatency}ms`; document.getElementById('stat-latency-val').textContent = `-${Math.round(data.avgLatency * 0.1)}ms`; } // Update Progress bar & Quota card const bar = document.getElementById('usage-progress-bar'); const label = document.getElementById('usage-percentage-label'); const limitText = document.getElementById('usage-limit-text'); if (bar) bar.style.width = `${data.percentage}%`; if (label) label.textContent = `${data.percentage}% USED`; if (limitText) { const remaining = Math.max(0, data.limit - data.monthlyUsage); limitText.textContent = `${remaining.toLocaleString()} requests left`; } } } catch (error) { console.error('Failed to update stats', error); } // Inject sample bars for visual flair if chart not ready const container = document.getElementById('traffic-bars'); if (container) { container.innerHTML = ''; // High-density bars for a tech/premium feel const values = [40, 60, 55, 80, 70, 45, 90, 85, 60, 40, 30, 55, 75, 40, 60, 55, 80, 70, 45, 90, 85, 60, 40, 30, 55, 75, 50, 65, 80, 70, 30]; values.forEach((h, index) => { const bar = document.createElement('div'); bar.className = 'flex-1 bg-gradient-to-t from-blue-600/10 to-blue-400/60 rounded-t-[2px] relative group hover:to-blue-300 transition-all duration-500'; bar.style.height = `${h}%`; bar.style.transitionDelay = `${index * 20}ms`; bar.innerHTML = `
${h}k
`; container.appendChild(bar); }); } }, renderKeysTable: () => { const tbody = document.getElementById('keys-table-body'); const createBtn = document.getElementById('create-key-btn'); if (!tbody) return; if (app.state.keys.length === 0) { tbody.innerHTML = `No API keys found. Create one to get started.`; return; } // Enable create button (removed previous limit restrictiveness) createBtn.disabled = false; createBtn.title = "Create a new API key"; createBtn.innerHTML = ' Create Key'; tbody.innerHTML = app.state.keys.map(key => { const isVisible = app.state.showKeys[key.id]; const maskedKey = isVisible ? key.key : "in_••••••••••••••••••••••••"; const eyeIcon = isVisible ? 'eye-off' : 'eye'; return ` ${key.name}
${maskedKey}
${key.isActive ? 'Active' : 'Inactive'}
${(key.allowedOrigins && key.allowedOrigins.length > 0) ? key.allowedOrigins.map(org => ` ${org}`).join('') : ` Universal` }
`; }).join(''); lucide.createIcons(); }, toggleKeyVisibility: (id) => { app.state.showKeys[id] = !app.state.showKeys[id]; app.renderKeysTable(); }, copyToClipboard: (text) => { navigator.clipboard.writeText(text); // Simple toast or just feedback console.log('Copied to clipboard'); }, toggleModal: (id, show) => { const modal = document.getElementById(id); if (modal) { if (show) modal.classList.remove('hidden'); else modal.classList.add('hidden'); } }, createKey: async () => { const name = document.getElementById('new-key-name').value; const btn = document.getElementById('btn-submit-key'); if (!app.state.tenant) return; try { btn.disabled = true; btn.textContent = 'Creating...'; const res = await fetch('/api/auth/management/keys', { method: 'POST', headers: { 'Content-Type': 'application/json', ...auth.getAuthHeader() }, body: JSON.stringify({ name, rateLimit: 100 }) }); if (res.ok) { app.toggleModal('create-key-modal', false); await app.fetchData(); document.getElementById('new-key-name').value = ''; } else { alert('Failed to create key'); } } catch (error) { console.error(error); } finally { btn.disabled = false; btn.textContent = 'Create Key'; } } }; // Start app components document.addEventListener('DOMContentLoaded', app.init);