init
This commit is contained in:
148
src/pages/dashboard.py
Normal file
148
src/pages/dashboard.py
Normal file
@@ -0,0 +1,148 @@
|
||||
from nicegui import ui
|
||||
from components.circular_progress import MetricCircle, LargeMetricCircle, ColorfulMetricCard
|
||||
from utils import data_manager
|
||||
|
||||
|
||||
class DashboardPage:
|
||||
def __init__(self):
|
||||
# Get real-time data
|
||||
dashboard_data = data_manager.get_dashboard_data()
|
||||
system_info = data_manager.get_system_info()
|
||||
|
||||
# Main content area with proper viewport handling
|
||||
with ui.element('div').classes('main-content w-full'):
|
||||
with ui.column().classes('w-full max-w-6xl mx-auto p-6 gap-6'):
|
||||
# Top stats grid
|
||||
with ui.grid(columns=4).classes('w-full gap-4'):
|
||||
MetricCircle('CPU', f"{dashboard_data['cpu']['percent']}%",
|
||||
dashboard_data['cpu']['percent'] / 100, '#e879f9', 'memory')
|
||||
MetricCircle('Memory', f"{dashboard_data['memory']['used_gb']}GB",
|
||||
dashboard_data['memory']['percent'] / 100, '#10b981', 'storage')
|
||||
|
||||
if dashboard_data['gpu']['available']:
|
||||
MetricCircle('GPU', f"{dashboard_data['gpu']['percent']}%",
|
||||
dashboard_data['gpu']['percent'] / 100, '#f97316', 'gpu_on')
|
||||
MetricCircle('Temp', f"{dashboard_data['gpu']['temperature']}°C",
|
||||
dashboard_data['gpu']['temperature'] / 100, '#06b6d4', 'thermostat')
|
||||
else:
|
||||
MetricCircle('GPU', 'N/A', 0, '#f97316', 'gpu_on')
|
||||
MetricCircle('Temp', 'N/A', 0, '#06b6d4', 'thermostat')
|
||||
|
||||
# Main dashboard content
|
||||
with ui.row().classes('w-full gap-6'):
|
||||
# Left column - charts and graphs
|
||||
with ui.column().classes('flex-grow gap-4'):
|
||||
# Performance chart card
|
||||
with ui.card().classes('chart-area p-6'):
|
||||
ui.label('System Performance').classes('text-lg font-bold text-white mb-4')
|
||||
|
||||
# Simulated chart area
|
||||
with ui.element('div').classes('h-48 w-full relative').style('background: linear-gradient(45deg, #1a1d2e 0%, #2a2d3e 100%); border-radius: 8px'):
|
||||
# Chart lines simulation
|
||||
with ui.element('svg').classes('absolute inset-0 w-full h-full'):
|
||||
ui.html('''
|
||||
<svg viewBox="0 0 400 200" class="w-full h-full">
|
||||
<defs>
|
||||
<linearGradient id="gradient1" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#e879f9;stop-opacity:0.3" />
|
||||
<stop offset="100%" style="stop-color:#e879f9;stop-opacity:0" />
|
||||
</linearGradient>
|
||||
<linearGradient id="gradient2" x1="0%" y1="0%" x2="0%" y2="100%">
|
||||
<stop offset="0%" style="stop-color:#10b981;stop-opacity:0.3" />
|
||||
<stop offset="100%" style="stop-color:#10b981;stop-opacity:0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path d="M 20 100 Q 100 50 200 80 T 380 60" stroke="#e879f9" stroke-width="2" fill="none"/>
|
||||
<path d="M 20 100 Q 100 50 200 80 T 380 60 L 380 180 L 20 180 Z" fill="url(#gradient1)"/>
|
||||
<path d="M 20 120 Q 100 90 200 110 T 380 100" stroke="#10b981" stroke-width="2" fill="none"/>
|
||||
<path d="M 20 120 Q 100 90 200 110 T 380 100 L 380 180 L 20 180 Z" fill="url(#gradient2)"/>
|
||||
</svg>
|
||||
''')
|
||||
|
||||
# Chart legend
|
||||
with ui.row().classes('gap-6 mt-4'):
|
||||
with ui.row().classes('items-center gap-2'):
|
||||
ui.element('div').classes('w-3 h-3 rounded-full').style('background: #e879f9')
|
||||
ui.label('CPU Usage').classes('text-sm text-grey-5')
|
||||
with ui.row().classes('items-center gap-2'):
|
||||
ui.element('div').classes('w-3 h-3 rounded-full').style('background: #10b981')
|
||||
ui.label('Memory Usage').classes('text-sm text-grey-5')
|
||||
|
||||
# Quick actions
|
||||
with ui.row().classes('w-full gap-4'):
|
||||
ColorfulMetricCard('Process Manager', 'terminal', '#e879f9')
|
||||
ColorfulMetricCard('Network Monitor', 'router', '#10b981')
|
||||
ColorfulMetricCard('Log Viewer', 'description', '#f97316')
|
||||
|
||||
# Right column - system info and GPU details
|
||||
with ui.column().classes('w-80 gap-4'):
|
||||
# Large GPU usage circle
|
||||
if dashboard_data['gpu']['available']:
|
||||
gpu_info = data_manager.get_gpu_info()
|
||||
gpu_name = 'Unknown GPU'
|
||||
if gpu_info.get('cards') and len(gpu_info['cards']) > 0:
|
||||
gpu_name = gpu_info['cards'][0].get('name', 'Unknown GPU')
|
||||
LargeMetricCircle('GPU Usage', gpu_name,
|
||||
dashboard_data['gpu']['percent'] / 100, '#f97316')
|
||||
else:
|
||||
LargeMetricCircle('GPU Usage', 'No GPU Detected', 0, '#f97316')
|
||||
|
||||
# System info card
|
||||
with ui.card().classes('metric-card p-4'):
|
||||
ui.label('System Info').classes('text-sm font-bold text-white mb-3')
|
||||
|
||||
with ui.column().classes('gap-2'):
|
||||
self._info_row('OS', system_info.get('os', 'Unknown'))
|
||||
self._info_row('Kernel', system_info.get('kernel', 'Unknown'))
|
||||
self._info_row('CPU', system_info.get('cpu', 'Unknown'))
|
||||
# Get first GPU name for display
|
||||
gpu_info = data_manager.get_gpu_info()
|
||||
gpu_display = 'No GPU'
|
||||
if gpu_info.get('cards') and len(gpu_info['cards']) > 0:
|
||||
gpu_display = gpu_info['cards'][0].get('name', 'Unknown GPU')
|
||||
self._info_row('GPU', gpu_display)
|
||||
self._info_row('Uptime', system_info.get('uptime', 'Unknown'))
|
||||
|
||||
# Ollama status card
|
||||
with ui.card().classes('metric-card p-4'):
|
||||
ui.label('Ollama Status').classes('text-sm font-bold text-white mb-3')
|
||||
|
||||
with ui.row().classes('items-center gap-2 mb-2'):
|
||||
ui.icon('check_circle', color='green', size='sm')
|
||||
ui.label('Online').classes('text-sm text-white')
|
||||
|
||||
with ui.column().classes('gap-1'):
|
||||
ui.label('4 models active').classes('text-xs text-grey-5')
|
||||
ui.label('llama3.2:3b, mistral:7b...').classes('text-xs text-grey-6')
|
||||
|
||||
# Bottom metrics row
|
||||
with ui.grid(columns=5).classes('w-full gap-4 mt-4'):
|
||||
self._bottom_metric(str(dashboard_data['processes']['count']), 'Processes', 'dashboard')
|
||||
|
||||
# Format network data (bytes to human readable)
|
||||
network_mb = (dashboard_data['network']['bytes_recv'] + dashboard_data['network']['bytes_sent']) / (1024 * 1024)
|
||||
if network_mb > 1024:
|
||||
network_display = f"{network_mb/1024:.1f}GB"
|
||||
else:
|
||||
network_display = f"{network_mb:.0f}MB"
|
||||
self._bottom_metric(network_display, 'Network', 'wifi')
|
||||
|
||||
self._bottom_metric(f"{dashboard_data['disk']['percent']:.0f}%", 'Disk', 'storage')
|
||||
|
||||
# CPU core count as services
|
||||
self._bottom_metric(str(dashboard_data['cpu']['count']), 'CPU Cores', 'settings')
|
||||
|
||||
# Memory total
|
||||
self._bottom_metric(f"{dashboard_data['memory']['total_gb']:.0f}GB", 'Total RAM', 'memory')
|
||||
|
||||
def _info_row(self, label: str, value: str):
|
||||
with ui.row().classes('w-full justify-between'):
|
||||
ui.label(label).classes('text-xs text-grey-5')
|
||||
ui.label(value).classes('text-xs text-white font-medium')
|
||||
|
||||
def _bottom_metric(self, value: str, label: str, icon: str):
|
||||
with ui.card().classes('metric-card p-3 text-center'):
|
||||
with ui.column().classes('items-center gap-1'):
|
||||
ui.icon(icon, size='sm', color='grey-5')
|
||||
ui.label(value).classes('text-lg font-bold text-white')
|
||||
ui.label(label).classes('text-xs text-grey-5')
|
||||
Reference in New Issue
Block a user