develop #1

Merged
Arnike merged 21 commits from develop into main 2026-02-07 18:06:51 +03:00
3 changed files with 84 additions and 53 deletions
Showing only changes of commit 38163210d6 - Show all commits

View File

@@ -1,6 +1,10 @@
@import "tailwindcss";
@plugin "daisyui";
@theme {
--font-sans: "DM Sans", ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}
@plugin "daisyui/theme" {
name: "light";
default: true;
@@ -71,9 +75,28 @@
--noise: 0;
}
/* Dark theme: main content area uses base-200 so navbar and cards (base-100) stand out */
/* Main content area: distinct background so cards (base-100) stand out */
[data-theme=light] main {
background-color: var(--color-base-200);
}
[data-theme=dark] main {
background-color: var(--color-base-200);
}
/* Accessibility: ensure focus ring is visible on key controls */
.btn:focus-visible,
.select:focus-within,
label.swap:focus-within {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
}
/* Light theme: slightly stronger card shadow for depth */
[data-theme=light] .card.shadow-sm {
box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.08), 0 1px 2px -1px rgb(0 0 0 / 0.06);
}
[data-theme=light] .card.shadow-sm:hover {
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.08), 0 2px 4px -2px rgb(0 0 0 / 0.06);
}
@source "../../templates";

View File

@@ -5,6 +5,9 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}SWatcher{% endblock %}</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{% static 'css/output.css' %}">
{% block imports %}
{% endblock %}
@@ -13,12 +16,10 @@
</head>
<body>
<!-- Navbar -->
<div class="navbar bg-base-100 shadow-lg">
<div class="navbar bg-base-100 shadow-lg border-b border-base-200 sticky top-0 z-10">
<div class="navbar-start">
<a class="btn btn-ghost text-xl" href="{% url 'index' %}">SWatcher</a>
</div>
<div class="navbar-center hidden lg:flex">
</div>
<div class="navbar-end">
<div class="px-1 flex gap-3 pr-10">
<span id="regionBadge" class="badge badge-primary badge-lg">{{ region.name }}</span>

View File

@@ -10,19 +10,19 @@
{% block content %}
<!-- MAIN DASHBOARD -->
<div class="p-4 space-y-4" {% if skeleton %}data-dashboard="skeleton"{% endif %}>
<div class="p-4 space-y-8" {% if skeleton %}data-dashboard="skeleton"{% endif %}>
<!-- QUICK STATS ROW -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4">
<section class="grid grid-cols-1 lg:grid-cols-3 gap-4" aria-label="Quick stats">
<!-- CPU Utilization -->
<div class="card bg-base-100 shadow-sm hover:shadow transition-shadow" id="statsPcpuCard">
<div class="card-body p-4">
<div class="card-body p-5">
{% if skeleton %}
<div class="flex items-center justify-between mb-3">
<div>
<h3 class="text-sm font-medium text-base-content/70">CPU Utilization</h3>
<div class="text-xs text-base-content/40 mt-0.5 animate-pulse"><span data-stats="pcpu.usage"></span> / <span data-stats="pcpu.total"></span> CPU</div>
<div class="text-xs text-base-content/60 mt-0.5 animate-pulse"><span data-stats="pcpu.usage"></span> / <span data-stats="pcpu.total"></span> CPU</div>
</div>
<div class="text-lg font-bold text-primary animate-pulse" data-stats="pcpu.used_percentage">—%</div>
<div class="text-xl font-bold text-primary animate-pulse" data-stats="pcpu.used_percentage">—%</div>
</div>
<div class="space-y-2">
<div class="flex justify-between text-xs">
@@ -39,9 +39,9 @@
<div class="flex items-center justify-between mb-3">
<div>
<h3 class="text-sm font-medium text-base-content/70">CPU Utilization</h3>
<div class="text-xs text-base-content/40 mt-0.5">{{ pcpu.usage|floatformat:1 }} / {{ pcpu.total }} CPU</div>
<div class="text-xs text-base-content/60 mt-0.5">{{ pcpu.usage|floatformat:1 }} / {{ pcpu.total }} CPU</div>
</div>
<div class="text-lg font-bold text-primary">{{ pcpu.used_percentage|floatformat:1 }}%</div>
<div class="text-xl font-bold text-primary">{{ pcpu.used_percentage|floatformat:1 }}%</div>
</div>
<div class="space-y-2">
<div class="flex justify-between text-xs">
@@ -60,14 +60,14 @@
<!-- RAM Utilization -->
<div class="card bg-base-100 shadow-sm hover:shadow transition-shadow" id="statsPramCard">
<div class="card-body p-4">
<div class="card-body p-5">
{% if skeleton %}
<div class="flex items-center justify-between mb-3">
<div>
<h3 class="text-sm font-medium text-base-content/70">RAM Utilization</h3>
<div class="text-xs text-base-content/40 mt-0.5 animate-pulse"><span data-stats="pram.usage_gb"></span> / <span data-stats="pram.total_gb"></span> GB</div>
<div class="text-xs text-base-content/60 mt-0.5 animate-pulse"><span data-stats="pram.usage_gb"></span> / <span data-stats="pram.total_gb"></span> GB</div>
</div>
<div class="text-lg font-bold text-secondary animate-pulse" data-stats="pram.used_percentage">—%</div>
<div class="text-xl font-bold text-secondary animate-pulse" data-stats="pram.used_percentage">—%</div>
</div>
<div class="space-y-2">
<div class="flex justify-between text-xs">
@@ -84,9 +84,9 @@
<div class="flex items-center justify-between mb-3">
<div>
<h3 class="text-sm font-medium text-base-content/70">RAM Utilization</h3>
<div class="text-xs text-base-content/40 mt-0.5">{{ pram.usage|convert_bytes }} / {{ pram.total|convert_bytes }} GB</div>
<div class="text-xs text-base-content/60 mt-0.5">{{ pram.usage|convert_bytes }} / {{ pram.total|convert_bytes }} GB</div>
</div>
<div class="text-lg font-bold text-secondary">{{ pram.used_percentage|floatformat:1 }}%</div>
<div class="text-xl font-bold text-secondary">{{ pram.used_percentage|floatformat:1 }}%</div>
</div>
<div class="space-y-2">
<div class="flex justify-between text-xs">
@@ -105,14 +105,14 @@
<!-- Instance Summary -->
<div class="card bg-base-100 shadow-sm hover:shadow transition-shadow" id="statsVmCard">
<div class="card-body p-4">
<div class="card-body p-5">
{% if skeleton %}
<div class="flex items-center justify-between mb-3">
<div>
<h3 class="text-sm font-medium text-base-content/70">Instances</h3>
<div class="text-xs text-base-content/40 mt-0.5 animate-pulse"><span data-stats="vm.active"></span> active / <span data-stats="vm.stopped"></span> stopped</div>
<div class="text-xs text-base-content/60 mt-0.5 animate-pulse"><span data-stats="vm.active"></span> active / <span data-stats="vm.stopped"></span> stopped</div>
</div>
<div class="text-lg font-bold text-accent animate-pulse" data-stats="vm.count"></div>
<div class="text-xl font-bold text-accent animate-pulse" data-stats="vm.count"></div>
</div>
<div class="space-y-3">
<div class="flex justify-between items-center text-xs">
@@ -141,9 +141,9 @@
<div class="flex items-center justify-between mb-3">
<div>
<h3 class="text-sm font-medium text-base-content/70">Instances</h3>
<div class="text-xs text-base-content/40 mt-0.5">{{ vm.active }} active / {{ vm.stopped }} stopped</div>
<div class="text-xs text-base-content/60 mt-0.5">{{ vm.active }} active / {{ vm.stopped }} stopped</div>
</div>
<div class="text-lg font-bold text-accent">{{ vm.count }}</div>
<div class="text-xl font-bold text-accent">{{ vm.count }}</div>
</div>
<div class="space-y-3">
<div class="flex justify-between items-center text-xs">
@@ -171,19 +171,19 @@
{% endif %}
</div>
</div>
</div>
</section>
<!-- DETAILED OVERVIEW -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
<section class="grid grid-cols-1 lg:grid-cols-2 gap-4" aria-label="Resource allocation and flavors">
<!-- Resource Allocation -->
<div class="card bg-base-100 shadow-sm" id="statsAllocationCard">
<div class="card-body p-4">
<h3 class="text-sm font-semibold mb-4 flex items-center gap-2">
<div class="card bg-base-100 shadow-sm border-t-2 border-primary" id="statsAllocationCard">
<div class="card-body p-5">
<h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
</svg>
Resource Allocation
</h3>
</h2>
{% if skeleton %}
<div class="mb-4">
<div class="flex justify-between text-xs mb-1">
@@ -219,8 +219,8 @@
<span class="text-xs font-medium w-12 text-right">{{ vcpu.allocated_percentage|floatformat:1 }}%</span>
</div>
<div class="flex justify-between text-xs mt-1">
<span class="text-base-content/50">overcommit: {{ vcpu.overcommit_ratio|floatformat:1 }} / {{ vcpu.overcommit_max|floatformat:1 }}</span>
<span class="text-base-content/50">{{ vcpu.allocated_percentage|floatformat:1 }}% allocated</span>
<span class="text-base-content/60">overcommit: {{ vcpu.overcommit_ratio|floatformat:1 }} / {{ vcpu.overcommit_max|floatformat:1 }}</span>
<span class="text-base-content/60">{{ vcpu.allocated_percentage|floatformat:1 }}% allocated</span>
</div>
</div>
@@ -235,8 +235,8 @@
<span class="text-xs font-medium w-12 text-right">{{ vram.allocated_percentage|floatformat:1 }}%</span>
</div>
<div class="flex justify-between text-xs mt-1">
<span class="text-base-content/50">overcommit: {{ vram.overcommit_ratio|floatformat:1 }} / {{ vram.overcommit_max|floatformat:1 }}</span>
<span class="text-base-content/50">{{ vram.allocated_percentage|floatformat:1 }}% allocated</span>
<span class="text-base-content/60">overcommit: {{ vram.overcommit_ratio|floatformat:1 }} / {{ vram.overcommit_max|floatformat:1 }}</span>
<span class="text-base-content/60">{{ vram.allocated_percentage|floatformat:1 }}% allocated</span>
</div>
</div>
{% endif %}
@@ -244,15 +244,15 @@
</div>
<!-- Flavor Distribution -->
<div class="card bg-base-100 shadow-sm" id="statsFlavorsCard">
<div class="card-body p-4">
<h3 class="text-sm font-semibold mb-4 flex items-center gap-2">
<div class="card bg-base-100 shadow-sm border-t-2 border-accent" id="statsFlavorsCard">
<div class="card-body p-5">
<h2 class="text-lg font-semibold mb-4 flex items-center gap-2">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 3.055A9.001 9.001 0 1020.945 13H11V3.055z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.488 9H15V3.512A9.025 9.025 0 0120.488 9z"/>
</svg>
Top Flavors
</h3>
</h2>
{% if skeleton %}
<div class="space-y-3">
<div class="bg-base-200/50 rounded-lg p-3">
@@ -310,18 +310,19 @@
{% endif %}
</div>
</div>
</div>
</section>
<!-- AUDIT CONTROL -->
<section aria-label="Audit analysis">
<div class="card bg-base-100 shadow-sm" id="auditSection">
<div class="card-body p-4">
<div class="card-body p-5">
<div class="flex flex-col sm:flex-row sm:items-center justify-between gap-3 mb-4">
<div>
<h3 class="text-sm font-semibold">Audit Analysis</h3>
<div class="text-xs text-base-content/50 mt-0.5">Select an audit to analyze resource distribution</div>
<h2 class="text-lg font-semibold">Audit Analysis</h2>
<div class="text-sm text-base-content/60 mt-0.5">Select an audit to analyze resource distribution</div>
</div>
<div class="flex items-center gap-2">
<span class="text-xs text-base-content/50" id="auditsCount">{% if skeleton %}Loading…{% else %}{{ audits|length }} available{% endif %}</span>
<span class="text-xs text-base-content/60" id="auditsCount">{% if skeleton %}Loading…{% else %}{{ audits|length }} available{% endif %}</span>
<div class="dropdown dropdown-end">
<label tabindex="0" class="btn btn-xs btn-ghost">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
@@ -372,19 +373,23 @@
{% endfor %}
{% endif %}
</select>
<button onclick="loadSelectedAudit()" class="btn btn-primary btn-sm">
<button type="button" onclick="loadSelectedAudit()" class="btn btn-primary btn-sm gap-2">
<svg class="w-4 h-4 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6"/>
</svg>
Load Analysis
</button>
</div>
</div>
</div>
</section>
<!-- ANALYSIS VISUALIZATION -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4">
<section class="grid grid-cols-1 lg:grid-cols-2 gap-4" aria-label="CPU distribution charts">
<!-- Current State -->
<div class="card bg-base-100 shadow-sm">
<div class="card-body p-4">
<h3 class="text-sm font-semibold mb-4">Current CPU Distribution</h3>
<div class="card-body p-5">
<h3 class="text-lg font-semibold mb-4">Current CPU Distribution</h3>
<div class="h-48">
<canvas id="cpuHostChart"></canvas>
</div>
@@ -403,8 +408,8 @@
<!-- Projected State -->
<div class="card bg-base-100 shadow-sm">
<div class="card-body p-4">
<h3 class="text-sm font-semibold mb-4">Projected CPU Distribution</h3>
<div class="card-body p-5">
<h3 class="text-lg font-semibold mb-4">Projected CPU Distribution</h3>
<div class="h-48">
<canvas id="cpuProjectedChart"></canvas>
</div>
@@ -420,13 +425,14 @@
</div>
</div>
</div>
</div>
</section>
<!-- MIGRATION ACTIONS -->
<section aria-label="Migration actions">
<div class="card bg-base-100 shadow-sm">
<div class="card-body p-4">
<div class="card-body p-6">
<div class="flex items-center justify-between mb-4">
<h3 class="text-sm font-semibold">Migration Actions</h3>
<h3 class="text-lg font-semibold">Migration Actions</h3>
<div class="badge badge-neutral badge-sm" id="migrationCount">Select audit</div>
</div>
@@ -442,7 +448,7 @@
</thead>
<tbody id="migrationTableBody" class="text-sm">
<tr>
<td colspan="4" class="text-center py-6 text-base-content/40">
<td colspan="4" class="text-center py-6 text-base-content/60">
No audit selected. Load an audit to view migration recommendations.
</td>
</tr>
@@ -451,6 +457,7 @@
</div>
</div>
</div>
</section>
</div>
{% endblock %}
@@ -607,7 +614,7 @@
if (!data || !data.migrations || data.migrations.length === 0) {
tbody.innerHTML = `
<tr>
<td colspan="4" class="text-center py-6 text-base-content/40">
<td colspan="4" class="text-center py-6 text-base-content/60">
No migration actions recommended
</td>
</tr>
@@ -871,10 +878,10 @@
{% block css %}
<style>
.progress {
@apply h-1.5;
@apply h-2 rounded-full;
}
.table td, .table th {
@apply px-4 py-2;
@apply px-4 py-3;
}
.badge-xs {
@apply px-1.5 py-0.5 text-xs;