// Clients Page Module class ClientsPage { constructor() { this.clients = []; this.init(); } async init() { // Load data await Promise.all([ this.loadClients(), this.loadClientUsageChart() ]); // Setup event listeners this.setupEventListeners(); } async loadClients() { try { const data = await window.api.get('/clients'); this.clients = data; this.renderClientsTable(); } catch (error) { console.error('Error loading clients:', error); window.authManager.showToast('Failed to load clients', 'error'); } } renderClientsTable() { const tableBody = document.querySelector('#clients-table tbody'); if (!tableBody) return; if (this.clients.length === 0) { tableBody.innerHTML = 'No clients configured'; return; } tableBody.innerHTML = this.clients.map(client => { const statusClass = client.status === 'active' ? 'success' : 'secondary'; const statusIcon = client.status === 'active' ? 'check-circle' : 'clock'; const created = luxon.DateTime.fromISO(client.created_at).toFormat('MMM dd, yyyy'); return ` ${client.id} ${client.name} sk-••••${client.id.substring(client.id.length - 4)} ${created} ${client.last_used ? window.api.formatTimeAgo(client.last_used) : 'Never'} ${client.requests_count.toLocaleString()} ${client.status}
`; }).join(''); } async loadClientUsageChart() { try { const cm = window.chartManager || await window.waitForChartManager(); if (!cm) { this.showEmptyChart('client-usage-chart', 'Chart system unavailable'); return; } const data = await window.api.get('/usage/clients'); if (!data || data.length === 0) { this.showEmptyChart('client-usage-chart', 'No client usage data yet'); return; } const chartData = { labels: data.map(item => item.client_id), datasets: [{ label: 'Requests', data: data.map(item => item.requests), color: '#6366f1' }] }; cm.createHorizontalBarChart('client-usage-chart', chartData); } catch (error) { console.error('Error loading client usage chart:', error); this.showEmptyChart('client-usage-chart', 'Failed to load usage data'); } } showEmptyChart(canvasId, message) { const canvas = document.getElementById(canvasId); if (!canvas) return; const container = canvas.closest('.chart-container'); if (container) { canvas.style.display = 'none'; let msg = container.querySelector('.empty-chart-msg'); if (!msg) { msg = document.createElement('div'); msg.className = 'empty-chart-msg'; msg.style.cssText = 'display:flex;align-items:center;justify-content:center;height:200px;color:var(--fg4);font-size:0.9rem;'; container.appendChild(msg); } msg.textContent = message; } } setupEventListeners() { const addBtn = document.getElementById('add-client'); if (addBtn) { addBtn.onclick = () => this.showAddClientModal(); } } showAddClientModal() { const modal = document.createElement('div'); modal.className = 'modal active'; modal.innerHTML = ` `; document.body.appendChild(modal); modal.querySelector('#confirm-create-client').onclick = async () => { const name = modal.querySelector('#new-client-name').value; const id = modal.querySelector('#new-client-id').value; if (!name) { window.authManager.showToast('Name is required', 'error'); return; } try { await window.api.post('/clients', { name, client_id: id || null }); window.authManager.showToast(`Client "${name}" created`, 'success'); modal.remove(); this.loadClients(); } catch (error) { window.authManager.showToast(error.message, 'error'); } }; } async deleteClient(id) { if (!confirm(`Are you sure you want to delete client ${id}? This cannot be undone.`)) return; try { await window.api.delete(`/clients/${id}`); window.authManager.showToast('Client deleted', 'success'); this.loadClients(); } catch (error) { window.authManager.showToast(error.message, 'error'); } } editClient(id) { window.authManager.showToast('Edit client not implemented yet', 'info'); } } window.initClients = async () => { window.clientsPage = new ClientsPage(); };