// Clients Page Module class ClientsPage { constructor() { this.clients = []; this.init(); } async init() { // Load data await this.loadClients(); await this.loadClientUsageChart(); await this.loadRateLimitStatus(); // Setup event listeners this.setupEventListeners(); } async loadClients() { try { // In a real app, this would fetch from /api/clients this.clients = [ { id: 'client-1', name: 'Web Application', token: 'sk-*****abc123', created: '2024-01-01', lastUsed: '2024-01-15', requests: 1245, status: 'active' }, { id: 'client-2', name: 'Mobile App', token: 'sk-*****def456', created: '2024-01-05', lastUsed: '2024-01-15', requests: 890, status: 'active' }, { id: 'client-3', name: 'API Integration', token: 'sk-*****ghi789', created: '2024-01-08', lastUsed: '2024-01-14', requests: 1560, status: 'active' }, { id: 'client-4', name: 'Internal Tools', token: 'sk-*****jkl012', created: '2024-01-10', lastUsed: '2024-01-13', requests: 340, status: 'inactive' }, { id: 'client-5', name: 'Testing Suite', token: 'sk-*****mno345', created: '2024-01-12', lastUsed: '2024-01-12', requests: 120, status: 'active' }, { id: 'client-6', name: 'Backup Service', token: 'sk-*****pqr678', created: '2024-01-14', lastUsed: null, requests: 0, status: 'pending' } ]; this.renderClientsTable(); } catch (error) { console.error('Error loading clients:', error); } } renderClientsTable() { const tableBody = document.querySelector('#clients-table tbody'); if (!tableBody) return; tableBody.innerHTML = this.clients.map(client => { const statusClass = client.status === 'active' ? 'success' : client.status === 'inactive' ? 'warning' : 'secondary'; const statusIcon = client.status === 'active' ? 'check-circle' : client.status === 'inactive' ? 'exclamation-triangle' : 'clock'; return ` ${client.id} ${client.name} ${client.token} ${client.created} ${client.lastUsed || 'Never'} ${client.requests.toLocaleString()} ${client.status}
`; }).join(''); // Add CSS for action buttons this.addActionStyles(); } addActionStyles() { const style = document.createElement('style'); style.textContent = ` .token-display { background-color: var(--bg-secondary); padding: 0.25rem 0.5rem; border-radius: 4px; font-family: monospace; font-size: 0.75rem; margin-right: 0.5rem; } .btn-copy-token { background: none; border: none; color: var(--text-secondary); cursor: pointer; font-size: 0.875rem; padding: 0.25rem; transition: color 0.2s ease; } .btn-copy-token:hover { color: var(--primary); } .action-buttons { display: flex; gap: 0.5rem; } .btn-action { background: none; border: none; color: var(--text-secondary); cursor: pointer; font-size: 0.875rem; padding: 0.25rem; transition: color 0.2s ease; } .btn-action:hover { color: var(--primary); } .btn-action.danger:hover { color: var(--danger); } `; document.head.appendChild(style); } async loadClientUsageChart() { try { const data = { labels: ['Web App', 'Mobile App', 'API Integration', 'Internal Tools', 'Testing'], datasets: [{ label: 'Requests', data: [1245, 890, 1560, 340, 120], color: '#3b82f6' }] }; window.chartManager.createHorizontalBarChart('client-usage-chart', data); } catch (error) { console.error('Error loading client usage chart:', error); } } async loadRateLimitStatus() { const container = document.getElementById('rate-limit-status'); if (!container) return; const rateLimits = [ { client: 'Web Application', limit: 1000, used: 645, remaining: 355 }, { client: 'Mobile App', limit: 500, used: 320, remaining: 180 }, { client: 'API Integration', limit: 2000, used: 1560, remaining: 440 }, { client: 'Internal Tools', limit: 100, used: 34, remaining: 66 }, { client: 'Testing Suite', limit: 200, used: 120, remaining: 80 } ]; container.innerHTML = rateLimits.map(limit => { const percentage = (limit.used / limit.limit) * 100; let color = 'success'; if (percentage > 80) color = 'warning'; if (percentage > 95) color = 'danger'; return `
${limit.client} ${limit.used} / ${limit.limit}
`; }).join(''); // Add CSS for rate limit items this.addRateLimitStyles(); } addRateLimitStyles() { const style = document.createElement('style'); style.textContent = ` .rate-limit-item { margin-bottom: 1rem; } .rate-limit-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; } .rate-limit-client { font-size: 0.875rem; color: var(--text-primary); } .rate-limit-numbers { font-size: 0.875rem; color: var(--text-secondary); } .rate-limit-footer { display: flex; justify-content: space-between; align-items: center; margin-top: 0.5rem; font-size: 0.75rem; } .rate-limit-percentage { color: var(--text-secondary); } .rate-limit-remaining { color: var(--success); font-weight: 500; } `; document.head.appendChild(style); } setupEventListeners() { // Add client button const addBtn = document.getElementById('add-client'); if (addBtn) { addBtn.addEventListener('click', () => { this.showAddClientModal(); }); } // Copy token buttons document.addEventListener('click', (e) => { if (e.target.closest('.btn-copy-token')) { const button = e.target.closest('.btn-copy-token'); const token = button.dataset.token; this.copyToClipboard(token); if (window.authManager) { window.authManager.showToast('Token copied to clipboard', 'success'); } } }); // Action buttons document.addEventListener('click', (e) => { if (e.target.closest('.btn-action')) { const button = e.target.closest('.btn-action'); const action = button.dataset.action; const clientId = button.dataset.id; switch (action) { case 'edit': this.editClient(clientId); break; case 'rotate': this.rotateToken(clientId); break; case 'revoke': this.revokeClient(clientId); break; } } }); } showAddClientModal() { const modal = document.createElement('div'); modal.className = 'modal active'; modal.innerHTML = ` `; document.body.appendChild(modal); // Setup event listeners const closeBtn = modal.querySelector('.modal-close'); const closeModalBtn = modal.querySelector('.close-modal'); const createBtn = modal.querySelector('.create-client'); const closeModal = () => { modal.classList.remove('active'); setTimeout(() => modal.remove(), 300); }; closeBtn.addEventListener('click', closeModal); closeModalBtn.addEventListener('click', closeModal); createBtn.addEventListener('click', () => { const name = modal.querySelector('#client-name').value; if (!name.trim()) { if (window.authManager) { window.authManager.showToast('Client name is required', 'error'); } return; } // In a real app, this would create the client via API if (window.authManager) { window.authManager.showToast(`Client "${name}" created successfully`, 'success'); } // Refresh clients list this.loadClients(); closeModal(); }); // Close on background click modal.addEventListener('click', (e) => { if (e.target === modal) { closeModal(); } }); } editClient(clientId) { const client = this.clients.find(c => c.id === clientId); if (!client) return; // Show edit modal const modal = document.createElement('div'); modal.className = 'modal active'; modal.innerHTML = ` `; document.body.appendChild(modal); // Setup event listeners const closeBtn = modal.querySelector('.modal-close'); const closeModalBtn = modal.querySelector('.close-modal'); const saveBtn = modal.querySelector('.save-client'); const closeModal = () => { modal.classList.remove('active'); setTimeout(() => modal.remove(), 300); }; closeBtn.addEventListener('click', closeModal); closeModalBtn.addEventListener('click', closeModal); saveBtn.addEventListener('click', () => { // In a real app, this would save client changes if (window.authManager) { window.authManager.showToast('Client updated successfully', 'success'); } closeModal(); }); // Close on background click modal.addEventListener('click', (e) => { if (e.target === modal) { closeModal(); } }); } rotateToken(clientId) { const client = this.clients.find(c => c.id === clientId); if (!client) return; // Show confirmation modal if (confirm(`Are you sure you want to rotate the token for "${client.name}"? The old token will be invalidated.`)) { // In a real app, this would rotate the token via API if (window.authManager) { window.authManager.showToast(`Token rotated for "${client.name}"`, 'success'); } // Refresh clients list this.loadClients(); } } revokeClient(clientId) { const client = this.clients.find(c => c.id === clientId); if (!client) return; // Show confirmation modal if (confirm(`Are you sure you want to revoke client "${client.name}"? This action cannot be undone.`)) { // In a real app, this would revoke the client via API if (window.authManager) { window.authManager.showToast(`Client "${client.name}" revoked`, 'success'); } // Refresh clients list this.loadClients(); } } copyToClipboard(text) { navigator.clipboard.writeText(text).catch(err => { console.error('Failed to copy:', err); }); } refresh() { this.loadClients(); this.loadClientUsageChart(); this.loadRateLimitStatus(); } } // Initialize clients page when needed window.initClients = async () => { window.clientsPage = new ClientsPage(); }; // Export for use in other modules if (typeof module !== 'undefined' && module.exports) { module.exports = ClientsPage; }