Init repo
This commit is contained in:
872
static/js/dashboard.js
Normal file
872
static/js/dashboard.js
Normal file
@@ -0,0 +1,872 @@
|
||||
// Main Dashboard Controller
|
||||
|
||||
class Dashboard {
|
||||
constructor() {
|
||||
this.currentPage = 'overview';
|
||||
this.pages = {};
|
||||
this.init();
|
||||
}
|
||||
|
||||
init() {
|
||||
// Initialize only if authenticated
|
||||
if (!window.authManager || !window.authManager.isAuthenticated) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.setupNavigation();
|
||||
this.setupSidebar();
|
||||
this.setupRefresh();
|
||||
this.updateTime();
|
||||
this.loadPage(this.currentPage);
|
||||
|
||||
// Start time updates
|
||||
setInterval(() => this.updateTime(), 1000);
|
||||
}
|
||||
|
||||
setupNavigation() {
|
||||
// Handle menu item clicks
|
||||
const menuItems = document.querySelectorAll('.menu-item');
|
||||
menuItems.forEach(item => {
|
||||
item.addEventListener('click', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
// Get page from data attribute or href
|
||||
const page = item.getAttribute('data-page') ||
|
||||
item.getAttribute('href').substring(1);
|
||||
|
||||
// Update active state
|
||||
menuItems.forEach(i => i.classList.remove('active'));
|
||||
item.classList.add('active');
|
||||
|
||||
// Load page
|
||||
this.loadPage(page);
|
||||
});
|
||||
});
|
||||
|
||||
// Handle hash changes (browser back/forward)
|
||||
window.addEventListener('hashchange', () => {
|
||||
const page = window.location.hash.substring(1) || 'overview';
|
||||
this.loadPage(page);
|
||||
});
|
||||
}
|
||||
|
||||
setupSidebar() {
|
||||
const toggleBtn = document.getElementById('sidebar-toggle');
|
||||
const sidebar = document.querySelector('.sidebar');
|
||||
|
||||
if (toggleBtn && sidebar) {
|
||||
toggleBtn.addEventListener('click', () => {
|
||||
sidebar.classList.toggle('collapsed');
|
||||
|
||||
// Save preference
|
||||
const isCollapsed = sidebar.classList.contains('collapsed');
|
||||
localStorage.setItem('sidebar_collapsed', isCollapsed);
|
||||
});
|
||||
|
||||
// Load saved preference
|
||||
const savedState = localStorage.getItem('sidebar_collapsed');
|
||||
if (savedState === 'true') {
|
||||
sidebar.classList.add('collapsed');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setupRefresh() {
|
||||
const refreshBtn = document.getElementById('refresh-btn');
|
||||
if (refreshBtn) {
|
||||
refreshBtn.addEventListener('click', () => {
|
||||
this.refreshCurrentPage();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateTime() {
|
||||
const timeElement = document.getElementById('current-time');
|
||||
if (!timeElement) return;
|
||||
|
||||
const now = luxon.DateTime.now();
|
||||
timeElement.textContent = now.toFormat('HH:mm:ss');
|
||||
}
|
||||
|
||||
async loadPage(page) {
|
||||
// Update current page
|
||||
this.currentPage = page;
|
||||
|
||||
// Update URL hash
|
||||
window.location.hash = page;
|
||||
|
||||
// Update page title
|
||||
this.updatePageTitle(page);
|
||||
|
||||
// Show loading state
|
||||
this.showLoading();
|
||||
|
||||
try {
|
||||
// Load page content
|
||||
await this.loadPageContent(page);
|
||||
|
||||
// Initialize page-specific functionality
|
||||
await this.initializePage(page);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`Error loading page ${page}:`, error);
|
||||
this.showError(`Failed to load ${page} page`);
|
||||
} finally {
|
||||
// Hide loading state
|
||||
this.hideLoading();
|
||||
}
|
||||
}
|
||||
|
||||
updatePageTitle(page) {
|
||||
const titleElement = document.getElementById('page-title');
|
||||
if (!titleElement) return;
|
||||
|
||||
const titles = {
|
||||
'overview': 'Dashboard Overview',
|
||||
'analytics': 'Usage Analytics',
|
||||
'costs': 'Cost Management',
|
||||
'clients': 'Client Management',
|
||||
'providers': 'Provider Configuration',
|
||||
'monitoring': 'Real-time Monitoring',
|
||||
'settings': 'System Settings',
|
||||
'logs': 'System Logs'
|
||||
};
|
||||
|
||||
titleElement.textContent = titles[page] || 'Dashboard';
|
||||
}
|
||||
|
||||
showLoading() {
|
||||
const content = document.getElementById('page-content');
|
||||
if (!content) return;
|
||||
|
||||
content.classList.add('loading');
|
||||
}
|
||||
|
||||
hideLoading() {
|
||||
const content = document.getElementById('page-content');
|
||||
if (!content) return;
|
||||
|
||||
content.classList.remove('loading');
|
||||
}
|
||||
|
||||
async loadPageContent(page) {
|
||||
const content = document.getElementById('page-content');
|
||||
if (!content) return;
|
||||
|
||||
// For now, we'll generate content dynamically
|
||||
// In a real app, you might fetch HTML templates or use a framework
|
||||
|
||||
let html = '';
|
||||
|
||||
switch (page) {
|
||||
case 'overview':
|
||||
html = await this.getOverviewContent();
|
||||
break;
|
||||
case 'analytics':
|
||||
html = await this.getAnalyticsContent();
|
||||
break;
|
||||
case 'costs':
|
||||
html = await this.getCostsContent();
|
||||
break;
|
||||
case 'clients':
|
||||
html = await this.getClientsContent();
|
||||
break;
|
||||
case 'providers':
|
||||
html = await this.getProvidersContent();
|
||||
break;
|
||||
case 'monitoring':
|
||||
html = await this.getMonitoringContent();
|
||||
break;
|
||||
case 'settings':
|
||||
html = await this.getSettingsContent();
|
||||
break;
|
||||
case 'logs':
|
||||
html = await this.getLogsContent();
|
||||
break;
|
||||
default:
|
||||
html = '<div class="empty-state"><h3>Page not found</h3></div>';
|
||||
}
|
||||
|
||||
content.innerHTML = html;
|
||||
}
|
||||
|
||||
async initializePage(page) {
|
||||
// Initialize page-specific JavaScript
|
||||
switch (page) {
|
||||
case 'overview':
|
||||
if (typeof window.initOverview === 'function') {
|
||||
await window.initOverview();
|
||||
}
|
||||
break;
|
||||
case 'analytics':
|
||||
if (typeof window.initAnalytics === 'function') {
|
||||
await window.initAnalytics();
|
||||
}
|
||||
break;
|
||||
case 'costs':
|
||||
if (typeof window.initCosts === 'function') {
|
||||
await window.initCosts();
|
||||
}
|
||||
break;
|
||||
case 'clients':
|
||||
if (typeof window.initClients === 'function') {
|
||||
await window.initClients();
|
||||
}
|
||||
break;
|
||||
case 'providers':
|
||||
if (typeof window.initProviders === 'function') {
|
||||
await window.initProviders();
|
||||
}
|
||||
break;
|
||||
case 'monitoring':
|
||||
if (typeof window.initMonitoring === 'function') {
|
||||
await window.initMonitoring();
|
||||
}
|
||||
break;
|
||||
case 'settings':
|
||||
if (typeof window.initSettings === 'function') {
|
||||
await window.initSettings();
|
||||
}
|
||||
break;
|
||||
case 'logs':
|
||||
if (typeof window.initLogs === 'function') {
|
||||
await window.initLogs();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
refreshCurrentPage() {
|
||||
this.loadPage(this.currentPage);
|
||||
|
||||
// Show refresh animation
|
||||
const refreshBtn = document.getElementById('refresh-btn');
|
||||
if (refreshBtn) {
|
||||
refreshBtn.classList.add('fa-spin');
|
||||
setTimeout(() => {
|
||||
refreshBtn.classList.remove('fa-spin');
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// Show toast notification
|
||||
if (window.authManager) {
|
||||
window.authManager.showToast('Page refreshed', 'success');
|
||||
}
|
||||
}
|
||||
|
||||
showError(message) {
|
||||
const content = document.getElementById('page-content');
|
||||
if (!content) return;
|
||||
|
||||
content.innerHTML = `
|
||||
<div class="empty-state">
|
||||
<i class="fas fa-exclamation-triangle"></i>
|
||||
<h3>Error</h3>
|
||||
<p>${message}</p>
|
||||
<button class="btn btn-primary" onclick="window.dashboard.refreshCurrentPage()">
|
||||
<i class="fas fa-redo"></i> Try Again
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Page content generators
|
||||
async getOverviewContent() {
|
||||
return `
|
||||
<div class="stats-grid" id="overview-stats">
|
||||
<!-- Stats will be loaded dynamically -->
|
||||
</div>
|
||||
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h3 class="chart-title">Request Volume (Last 24 Hours)</h3>
|
||||
<div class="chart-controls">
|
||||
<button class="chart-control-btn active" data-period="24h">24H</button>
|
||||
<button class="chart-control-btn" data-period="7d">7D</button>
|
||||
<button class="chart-control-btn" data-period="30d">30D</button>
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="requests-chart" height="300"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="grid-2">
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h3 class="chart-title">Provider Distribution</h3>
|
||||
</div>
|
||||
<canvas id="providers-chart" height="250"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h3 class="chart-title">System Health</h3>
|
||||
</div>
|
||||
<div id="system-health">
|
||||
<!-- Health indicators will be loaded dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="card-title">Recent Requests</h3>
|
||||
<p class="card-subtitle">Last 50 requests</p>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<button class="card-action-btn" title="Refresh">
|
||||
<i class="fas fa-redo"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<table class="table" id="recent-requests">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Time</th>
|
||||
<th>Client</th>
|
||||
<th>Provider</th>
|
||||
<th>Model</th>
|
||||
<th>Tokens</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Requests will be loaded dynamically -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async getAnalyticsContent() {
|
||||
return `
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="card-title">Usage Analytics</h3>
|
||||
<p class="card-subtitle">Filter and analyze usage data</p>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<button class="btn btn-secondary">
|
||||
<i class="fas fa-download"></i> Export
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-row">
|
||||
<div class="form-control">
|
||||
<label>Date Range</label>
|
||||
<select id="date-range">
|
||||
<option value="24h">Last 24 Hours</option>
|
||||
<option value="7d" selected>Last 7 Days</option>
|
||||
<option value="30d">Last 30 Days</option>
|
||||
<option value="custom">Custom Range</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label>Client</label>
|
||||
<select id="client-filter">
|
||||
<option value="all">All Clients</option>
|
||||
<!-- Client options will be loaded dynamically -->
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label>Provider</label>
|
||||
<select id="provider-filter">
|
||||
<option value="all">All Providers</option>
|
||||
<option value="openai">OpenAI</option>
|
||||
<option value="gemini">Gemini</option>
|
||||
<option value="deepseek">DeepSeek</option>
|
||||
<option value="grok">Grok</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h3 class="chart-title">Request Trends</h3>
|
||||
<div class="chart-controls">
|
||||
<button class="chart-control-btn active" data-metric="requests">Requests</button>
|
||||
<button class="chart-control-btn" data-metric="tokens">Tokens</button>
|
||||
<button class="chart-control-btn" data-metric="cost">Cost</button>
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="analytics-chart" height="350"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="grid-2">
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h3 class="chart-title">Top Clients</h3>
|
||||
</div>
|
||||
<canvas id="clients-chart" height="300"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h3 class="chart-title">Top Models</h3>
|
||||
</div>
|
||||
<canvas id="models-chart" height="300"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Detailed Usage Data</h3>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<table class="table" id="usage-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Client</th>
|
||||
<th>Provider</th>
|
||||
<th>Model</th>
|
||||
<th>Requests</th>
|
||||
<th>Tokens</th>
|
||||
<th>Cost</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Usage data will be loaded dynamically -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async getCostsContent() {
|
||||
return `
|
||||
<div class="stats-grid" id="cost-stats">
|
||||
<!-- Cost stats will be loaded dynamically -->
|
||||
</div>
|
||||
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h3 class="chart-title">Cost Breakdown</h3>
|
||||
<div class="chart-controls">
|
||||
<button class="chart-control-btn active" data-breakdown="provider">By Provider</button>
|
||||
<button class="chart-control-btn" data-breakdown="client">By Client</button>
|
||||
<button class="chart-control-btn" data-breakdown="model">By Model</button>
|
||||
</div>
|
||||
</div>
|
||||
<canvas id="costs-chart" height="300"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="grid-2">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Budget Tracking</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="budget-progress">
|
||||
<!-- Budget progress bars will be loaded dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Cost Projections</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="cost-projections">
|
||||
<!-- Projections will be loaded dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="card-title">Pricing Configuration</h3>
|
||||
<p class="card-subtitle">Current provider pricing</p>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<button class="btn btn-primary" id="edit-pricing">
|
||||
<i class="fas fa-edit"></i> Edit
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<table class="table" id="pricing-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Provider</th>
|
||||
<th>Model</th>
|
||||
<th>Input Price</th>
|
||||
<th>Output Price</th>
|
||||
<th>Last Updated</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Pricing data will be loaded dynamically -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async getClientsContent() {
|
||||
return `
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="card-title">Client Management</h3>
|
||||
<p class="card-subtitle">Manage API clients and tokens</p>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<button class="btn btn-primary" id="add-client">
|
||||
<i class="fas fa-plus"></i> Add Client
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-container">
|
||||
<table class="table" id="clients-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Client ID</th>
|
||||
<th>Name</th>
|
||||
<th>Token</th>
|
||||
<th>Created</th>
|
||||
<th>Last Used</th>
|
||||
<th>Requests</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Clients will be loaded dynamically -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-2">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Client Usage Summary</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<canvas id="client-usage-chart" height="250"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Rate Limit Status</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="rate-limit-status">
|
||||
<!-- Rate limit status will be loaded dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async getProvidersContent() {
|
||||
return `
|
||||
<div class="stats-grid" id="provider-stats">
|
||||
<!-- Provider stats will be loaded dynamically -->
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Provider Configuration</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="providers-list">
|
||||
<!-- Providers will be loaded dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-2">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Model Availability</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="models-list">
|
||||
<!-- Models will be loaded dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Connection Tests</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="connection-tests">
|
||||
<!-- Test results will be loaded dynamically -->
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<button class="btn btn-primary" id="test-all-providers">
|
||||
<i class="fas fa-play"></i> Test All Providers
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async getMonitoringContent() {
|
||||
return `
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="card-title">Real-time Monitoring</h3>
|
||||
<p class="card-subtitle">Live request stream and system metrics</p>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<button class="btn btn-secondary" id="pause-monitoring">
|
||||
<i class="fas fa-pause"></i> Pause
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="grid-2">
|
||||
<div>
|
||||
<h4>Live Request Stream</h4>
|
||||
<div id="request-stream" class="monitoring-stream">
|
||||
<!-- Live requests will appear here -->
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4>System Metrics</h4>
|
||||
<div id="system-metrics">
|
||||
<!-- System metrics will be loaded dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-3">
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h3 class="chart-title">Response Time (ms)</h3>
|
||||
</div>
|
||||
<canvas id="response-time-chart" height="200"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h3 class="chart-title">Error Rate (%)</h3>
|
||||
</div>
|
||||
<canvas id="error-rate-chart" height="200"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="chart-container">
|
||||
<div class="chart-header">
|
||||
<h3 class="chart-title">Rate Limit Usage</h3>
|
||||
</div>
|
||||
<canvas id="rate-limit-chart" height="200"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">System Logs (Live)</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="system-logs" class="log-stream">
|
||||
<!-- System logs will appear here -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async getSettingsContent() {
|
||||
return `
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">System Settings</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="settings-form">
|
||||
<div class="form-section">
|
||||
<h4>General Configuration</h4>
|
||||
<div class="form-row">
|
||||
<div class="form-control">
|
||||
<label>Server Port</label>
|
||||
<input type="number" id="server-port" value="8080" min="1024" max="65535">
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label>Log Level</label>
|
||||
<select id="log-level">
|
||||
<option value="error">Error</option>
|
||||
<option value="warn">Warning</option>
|
||||
<option value="info" selected>Info</option>
|
||||
<option value="debug">Debug</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<h4>Database Settings</h4>
|
||||
<div class="form-row">
|
||||
<div class="form-control">
|
||||
<label>Database Path</label>
|
||||
<input type="text" id="db-path" value="./data/llm-proxy.db">
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label>Backup Interval (hours)</label>
|
||||
<input type="number" id="backup-interval" value="24" min="1" max="168">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-section">
|
||||
<h4>Security Settings</h4>
|
||||
<div class="form-row">
|
||||
<div class="form-control">
|
||||
<label>Dashboard Password</label>
|
||||
<input type="password" id="dashboard-password" placeholder="Leave empty to keep current">
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label>Session Timeout (minutes)</label>
|
||||
<input type="number" id="session-timeout" value="30" min="5" max="1440">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-secondary" id="reset-settings">
|
||||
<i class="fas fa-undo"></i> Reset
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> Save Settings
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-2">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Database Management</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-actions">
|
||||
<button class="btn btn-secondary" id="backup-db">
|
||||
<i class="fas fa-download"></i> Backup Database
|
||||
</button>
|
||||
<button class="btn btn-warning" id="optimize-db">
|
||||
<i class="fas fa-magic"></i> Optimize Database
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">System Information</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="system-info">
|
||||
<!-- System info will be loaded dynamically -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
async getLogsContent() {
|
||||
return `
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div>
|
||||
<h3 class="card-title">System Logs</h3>
|
||||
<p class="card-subtitle">View and filter system logs</p>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<button class="btn btn-secondary" id="download-logs">
|
||||
<i class="fas fa-download"></i> Download
|
||||
</button>
|
||||
<button class="btn btn-danger" id="clear-logs">
|
||||
<i class="fas fa-trash"></i> Clear
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-row">
|
||||
<div class="form-control">
|
||||
<label>Log Level</label>
|
||||
<select id="log-filter">
|
||||
<option value="all">All Levels</option>
|
||||
<option value="error">Error</option>
|
||||
<option value="warn">Warning</option>
|
||||
<option value="info">Info</option>
|
||||
<option value="debug">Debug</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label>Time Range</label>
|
||||
<select id="log-time-range">
|
||||
<option value="1h">Last Hour</option>
|
||||
<option value="24h" selected>Last 24 Hours</option>
|
||||
<option value="7d">Last 7 Days</option>
|
||||
<option value="30d">Last 30 Days</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-control">
|
||||
<label>Search</label>
|
||||
<input type="text" id="log-search" placeholder="Search logs...">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<table class="table" id="logs-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Timestamp</th>
|
||||
<th>Level</th>
|
||||
<th>Source</th>
|
||||
<th>Message</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Logs will be loaded dynamically -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize dashboard when DOM is loaded
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
window.initDashboard = () => {
|
||||
window.dashboard = new Dashboard();
|
||||
};
|
||||
|
||||
// If already authenticated, initialize immediately
|
||||
if (window.authManager && window.authManager.isAuthenticated) {
|
||||
window.initDashboard();
|
||||
}
|
||||
});
|
||||
|
||||
// Export for use in other modules
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = Dashboard;
|
||||
}
|
||||
Reference in New Issue
Block a user