From 4e7f5f56f1665fe23f47894bf753423a8226211b Mon Sep 17 00:00:00 2001 From: MacRimi Date: Mon, 29 Sep 2025 22:59:10 +0200 Subject: [PATCH] Update AppImage --- AppImage/app/globals.css | 153 ++---- AppImage/components/system-overview.tsx | 589 +++++++++++------------- AppImage/package.json | 6 +- AppImage/postcss.config.mjs | 9 + AppImage/tailwind.config.js | 89 ++++ 5 files changed, 413 insertions(+), 433 deletions(-) create mode 100644 AppImage/postcss.config.mjs create mode 100644 AppImage/tailwind.config.js diff --git a/AppImage/app/globals.css b/AppImage/app/globals.css index 5196b73..2af71a6 100644 --- a/AppImage/app/globals.css +++ b/AppImage/app/globals.css @@ -4,7 +4,6 @@ @custom-variant dark (&:is(.dark *)); :root { - /* Proxmox light theme colors */ --background: oklch(1 0 0); --foreground: oklch(0.145 0 0); --card: oklch(1 0 0); @@ -32,46 +31,51 @@ --radius: 0.625rem; --sidebar: oklch(0.985 0 0); --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); } .dark { - /* Updated Proxmox dark theme with proper dark gray background matching Proxmox style */ - --background: #1a1d21; /* Proxmox dark background */ - --foreground: #ffffff; - --card: #2b2f36; /* Proxmox card background */ - --card-foreground: #ffffff; - --popover: #2b2f36; - --popover-foreground: #ffffff; - --primary: #ffffff; - --primary-foreground: #1a1d21; - --secondary: #363c45; /* Better contrast for secondary elements */ - --secondary-foreground: #ffffff; - --muted: #363c45; - --muted-foreground: #b4b4b4; /* Better contrast for muted text */ - --accent: #363c45; - --accent-foreground: #ffffff; - --destructive: #ef4444; - --destructive-foreground: #ffffff; - --border: #404854; /* More visible borders */ - --input: #363c45; - --ring: #6b7280; - /* Updated chart colors to be more vibrant and visible in dark mode */ - --chart-1: #3b82f6; /* Bright Blue */ - --chart-2: #10b981; /* Bright Green */ - --chart-3: #f59e0b; /* Bright Yellow */ - --chart-4: #8b5cf6; /* Bright Purple */ - --chart-5: #f97316; /* Bright Orange */ - --sidebar: #1a1d21; - --sidebar-foreground: #ffffff; - --sidebar-primary: #6366f1; - --sidebar-primary-foreground: #ffffff; - --sidebar-accent: #363c45; - --sidebar-accent-foreground: #ffffff; - --sidebar-border: #363c45; - --sidebar-ring: #6b7280; + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.145 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.145 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.985 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.396 0.141 25.723); + --destructive-foreground: oklch(0.637 0.237 25.331); + --border: oklch(0.269 0 0); + --input: oklch(0.269 0 0); + --ring: oklch(0.439 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(0.269 0 0); + --sidebar-ring: oklch(0.439 0 0); } @theme inline { + /* optional: --font-sans, --font-serif, --font-mono if they are applied in the layout.tsx */ --color-background: var(--background); --color-foreground: var(--foreground); --color-card: var(--card); @@ -108,9 +112,6 @@ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); --color-sidebar-border: var(--sidebar-border); --color-sidebar-ring: var(--sidebar-ring); - /* Custom header colors */ - --color-header-bg: var(--header-bg); - --color-header-foreground: var(--header-foreground); } @layer base { @@ -121,79 +122,3 @@ @apply bg-background text-foreground; } } - -/* Header styling now uses consistent background color */ -.header-bg { - background-color: var(--header-bg); - color: var(--header-foreground); -} - -/* Custom scrollbar with better contrast */ -::-webkit-scrollbar { - width: 6px; -} - -::-webkit-scrollbar-track { - background: var(--background); -} - -::-webkit-scrollbar-thumb { - background: var(--muted); - border-radius: 3px; -} - -::-webkit-scrollbar-thumb:hover { - background: var(--muted-foreground); -} - -/* Better contrast for dark mode content */ -.dark .metric-card { - background: var(--card); - border: 1px solid var(--border); -} - -.dark .metric-value { - color: var(--foreground); - font-weight: 600; -} - -.dark .metric-label { - color: var(--muted-foreground); -} - -/* Fix chart axis visibility in dark mode */ -.dark .recharts-cartesian-axis-tick-value { - fill: var(--muted-foreground) !important; -} - -.dark .recharts-text { - fill: var(--muted-foreground) !important; -} - -/* Improve server info layout in header - clean design without transparency */ -.server-info { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.25rem 0.75rem; - border-radius: 0.375rem; - border: 1px solid rgba(255, 255, 255, 0.2); -} - -.dark .server-info { - border: 1px solid rgba(255, 255, 255, 0.1); -} - -/* Better spacing for VM/LXC badges */ -.vm-badges { - display: flex; - flex-wrap: wrap; - gap: 0.25rem; - align-items: center; -} - -.vm-badge { - font-size: 0.75rem; - padding: 0.125rem 0.5rem; - white-space: nowrap; -} diff --git a/AppImage/components/system-overview.tsx b/AppImage/components/system-overview.tsx index 6d2e09f..db397f1 100644 --- a/AppImage/components/system-overview.tsx +++ b/AppImage/components/system-overview.tsx @@ -4,9 +4,8 @@ import { useState, useEffect } from "react" import { Card, CardContent, CardHeader, CardTitle } from "./ui/card" import { Progress } from "./ui/progress" import { Badge } from "./ui/badge" -import { Button } from "./ui/button" import { XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, AreaChart, Area } from "recharts" -import { Cpu, MemoryStick, Thermometer, Users, Activity, Server, Zap, AlertCircle, RefreshCw } from "lucide-react" +import { Cpu, MemoryStick, Thermometer, Users, Activity, Server, Zap, AlertCircle } from "lucide-react" interface SystemData { cpu_usage: number @@ -161,55 +160,46 @@ export function SystemOverview() { const [vmData, setVmData] = useState([]) const [chartData, setChartData] = useState(generateChartData()) const [loading, setLoading] = useState(true) - const [isDemo, setIsDemo] = useState(false) - const [isRefreshing, setIsRefreshing] = useState(false) - const [lastUpdate, setLastUpdate] = useState(new Date()) - - const fetchData = async () => { - try { - setIsRefreshing(true) - - const [systemResult, vmResult] = await Promise.all([fetchSystemData(), fetchVMData()]) - - setSystemData(systemResult.data) - setVmData(vmResult.data) - setIsDemo(systemResult.isDemo || vmResult.isDemo) - setLastUpdate(new Date()) - - if (systemResult.data) { - setChartData(generateChartData(systemResult.data)) - } - } catch (err) { - console.error("[v0] Error fetching data:", err) - const fallbackData = generateDemoSystemData() - setSystemData(fallbackData) - setVmData(demVMData) - setChartData(generateChartData(fallbackData)) - setIsDemo(true) - setLastUpdate(new Date()) - } finally { - setLoading(false) - setIsRefreshing(false) - } - } - - const handleManualRefresh = () => { - fetchData() - } + const [isDemo, setIsDemo] = useState(false) // Added demo mode state useEffect(() => { + const fetchData = async () => { + try { + setLoading(true) + + const [systemResult, vmResult] = await Promise.all([fetchSystemData(), fetchVMData()]) + + setSystemData(systemResult.data) + setVmData(vmResult.data) + setIsDemo(systemResult.isDemo || vmResult.isDemo) // Set demo mode if either fetch is demo + + if (systemResult.data) { + setChartData(generateChartData(systemResult.data)) + } + } catch (err) { + console.error("[v0] Error fetching data:", err) + const fallbackData = generateDemoSystemData() + setSystemData(fallbackData) + setVmData(demVMData) + setChartData(generateChartData(fallbackData)) + setIsDemo(true) + } finally { + setLoading(false) + } + } + fetchData() const interval = setInterval(() => { if (!isDemo) { fetchData() } else { + // In demo mode, just update with new random data const newDemoData = generateDemoSystemData() setSystemData(newDemoData) setChartData(generateChartData(newDemoData)) - setLastUpdate(new Date()) } - }, 30000) + }, 5000) // Update every 5 seconds instead of 30 return () => { clearInterval(interval) @@ -218,27 +208,22 @@ export function SystemOverview() { if (loading) { return ( -
-
-
-
- -
-
Connecting to ProxMenux Monitor...
-
Fetching real-time system data
-
-
- {[...Array(4)].map((_, i) => ( - - -
-
-
-
-
-
- ))} -
+
+
+
Connecting to ProxMenux Monitor...
+
Fetching real-time system data
+
+
+ {[...Array(4)].map((_, i) => ( + + +
+
+
+
+
+
+ ))}
) @@ -254,276 +239,250 @@ export function SystemOverview() { } const getTemperatureStatus = (temp: number) => { - if (temp < 60) return { status: "Normal", color: "bg-emerald-500/10 text-emerald-400 border-emerald-500/20" } - if (temp < 75) return { status: "Warm", color: "bg-amber-500/10 text-amber-400 border-amber-500/20" } - return { status: "Hot", color: "bg-red-500/10 text-red-400 border-red-500/20" } + if (temp < 60) return { status: "Normal", color: "bg-green-500/10 text-green-500 border-green-500/20" } + if (temp < 75) return { status: "Warm", color: "bg-yellow-500/10 text-yellow-500 border-yellow-500/20" } + return { status: "Hot", color: "bg-red-500/10 text-red-500 border-red-500/20" } } const tempStatus = getTemperatureStatus(systemData.temperature) return ( -
-
-
-
-

ProxMenux System Dashboard

-

Last updated: {lastUpdate.toLocaleTimeString()} • Auto-refresh every 30s

-
- -
+
+ {isDemo && ( + + +
+ + + Demo Mode: Flask server not available. Showing simulated data for development. In the + AppImage, this will connect to the real Flask server. + +
+
+
+ )} - {isDemo && ( - - -
- - - Demo Mode: Flask server not available. Showing simulated data for development. In the - AppImage, this will connect to the real Flask server. - -
-
-
- )} + {/* Key Metrics Cards */} +
+ + + CPU Usage + + + +
{systemData.cpu_usage}%
+ +

+ {isDemo ? "Simulated data" : "Real-time data from Flask server"} +

+
+
-
- - - CPU Usage -
- -
-
- -
{systemData.cpu_usage}%
- -

- {isDemo ? "Simulated data" : "Real-time data from Flask server"} -

-
-
+ + + Memory Usage + + + +
+ {systemData.memory_used.toFixed(1)} GB +
+ +

+ {systemData.memory_usage.toFixed(1)}% of {systemData.memory_total} GB +

+
+
- - - Memory Usage -
- -
-
- -
{systemData.memory_used.toFixed(1)} GB
- -

- {systemData.memory_usage.toFixed(1)}% of {systemData.memory_total} GB -

-
-
+ + + Temperature + + + +
{systemData.temperature}°C
+
+ + {tempStatus.status} + +
+

+ {isDemo ? "Simulated temperature" : "Live temperature reading"} +

+
+
- - - Temperature -
- -
-
- -
{systemData.temperature}°C
-
- - {tempStatus.status} + + + Active VMs + + + +
{vmStats.running}
+
+ + {vmStats.running} Running + + {vmStats.stopped > 0 && ( + + {vmStats.stopped} Stopped -
-

- {isDemo ? "Simulated temperature" : "Live temperature reading"} -

-
-
+ )} +
+

Total: {vmStats.total} VMs configured

+
+
+
- - - Active VMs -
- -
-
- -
{vmStats.running}
-
- - {vmStats.running} Running - - {vmStats.stopped > 0 && ( - - {vmStats.stopped} Stopped - - )} -
-

Total: {vmStats.total} VMs configured

-
-
-
+ {/* Charts Section */} +
+ + + + + CPU Usage (24h) + + + + + + + + + + + + + + -
- - - -
- -
- CPU Usage (24h) -
-
- - - - - - - - - - - -
+ + + + + Memory Usage (24h) + + + + + + + + + + + + + + + +
- - - -
- -
- Memory Usage (24h) -
-
- - - - - - - - - - - - -
-
+ {/* System Information */} +
+ + + + + System Information + + + +
+ Hostname: + {systemData.hostname} +
+
+ Uptime: + {systemData.uptime} +
+
+ Node ID: + {systemData.node_id} +
+
+ Last Update: + + {new Date(systemData.timestamp).toLocaleTimeString()} + +
+
+
-
- - - -
- -
- System Information -
-
- -
- Hostname: - - {systemData.hostname} - -
-
- Uptime: - {systemData.uptime} -
-
- Node ID: - - {systemData.node_id} - -
-
- Status: - healthy -
-
-
+ + + + + Active Sessions + + + +
+ Web Console: + + 3 active + +
+
+ SSH Sessions: + + 1 active + +
+
+ API Calls: + 247/hour +
+
+
- - - -
- -
- Active Sessions -
-
- -
- Web Console: - 3 active -
-
- SSH Sessions: - 1 active -
-
- API Calls: - 247/hour -
-
-
- - - - -
- -
- Power & Performance -
-
- -
- Power State: - Running -
-
- Load Average: - - {systemData.load_average.map((avg) => avg.toFixed(2)).join(", ")} - -
-
- Boot Time: - 2.3s -
-
-
-
+ + + + + Power & Performance + + + +
+ Power State: + + Running + +
+
+ Load Average: + + {systemData.load_average.map((avg) => avg.toFixed(2)).join(", ")} + +
+
+ Boot Time: + 2.3s +
+
+
) diff --git a/AppImage/package.json b/AppImage/package.json index 6060bc4..d5014eb 100644 --- a/AppImage/package.json +++ b/AppImage/package.json @@ -40,7 +40,6 @@ "@radix-ui/react-toggle-group": "1.1.1", "@radix-ui/react-tooltip": "1.1.6", "@vercel/analytics": "1.3.1", - "autoprefixer": "^10.4.20", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "1.0.4", @@ -64,13 +63,12 @@ "zod": "3.25.67" }, "devDependencies": { - "@tailwindcss/postcss": "^4.1.9", "@types/node": "^22", "@types/react": "^18", "@types/react-dom": "^18", + "autoprefixer": "^10.4.20", "postcss": "^8.5", - "tailwindcss": "^4.1.9", - "tw-animate-css": "1.3.3", + "tailwindcss": "^3.4.1", "typescript": "^5" } } diff --git a/AppImage/postcss.config.mjs b/AppImage/postcss.config.mjs new file mode 100644 index 0000000..2ef30fc --- /dev/null +++ b/AppImage/postcss.config.mjs @@ -0,0 +1,9 @@ +/** @type {import('postcss-load-config').Config} */ +const config = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; + +export default config; diff --git a/AppImage/tailwind.config.js b/AppImage/tailwind.config.js new file mode 100644 index 0000000..0109d88 --- /dev/null +++ b/AppImage/tailwind.config.js @@ -0,0 +1,89 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: ["class"], + content: ["./pages/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}", "./app/**/*.{ts,tsx}", "./src/**/*.{ts,tsx}"], + prefix: "", + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + chart: { + 1: "hsl(var(--chart-1))", + 2: "hsl(var(--chart-2))", + 3: "hsl(var(--chart-3))", + 4: "hsl(var(--chart-4))", + 5: "hsl(var(--chart-5))", + }, + sidebar: { + DEFAULT: "hsl(var(--sidebar-background))", + foreground: "hsl(var(--sidebar-foreground))", + primary: "hsl(var(--sidebar-primary))", + "primary-foreground": "hsl(var(--sidebar-primary-foreground))", + accent: "hsl(var(--sidebar-accent))", + "accent-foreground": "hsl(var(--sidebar-accent-foreground))", + border: "hsl(var(--sidebar-border))", + ring: "hsl(var(--sidebar-ring))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + keyframes: { + "accordion-down": { + from: { height: "0" }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: "0" }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + }, + }, + }, + plugins: [require("tailwindcss-animate")], +}