From 795d96f8d59f6f096cf9a91f98e7a7dd9f18dbbd Mon Sep 17 00:00:00 2001 From: MacRimi Date: Sun, 28 Sep 2025 23:09:31 +0200 Subject: [PATCH] Update AppImage --- AppImage/README.md | 59 ++ AppImage/app/api/flask/route.ts | 78 +++ AppImage/app/globals.css | 233 +++---- AppImage/app/layout.tsx | 21 +- AppImage/app/page.tsx | 4 +- AppImage/components.json | 21 - AppImage/components/network-metrics.tsx | 267 ++++++++ AppImage/components/proxmox-dashboard.tsx | 246 +++++++ AppImage/components/storage-metrics.tsx | 243 +++++++ AppImage/components/system-logs.tsx | 275 ++++++++ AppImage/components/system-overview.tsx | 249 +++++++ AppImage/components/theme-toggle.tsx | 22 + AppImage/components/ui/accordion.tsx | 66 -- AppImage/components/ui/alert-dialog.tsx | 157 ----- AppImage/components/ui/alert.tsx | 66 -- AppImage/components/ui/aspect-ratio.tsx | 11 - AppImage/components/ui/avatar.tsx | 53 -- AppImage/components/ui/badge.tsx | 42 +- AppImage/components/ui/breadcrumb.tsx | 109 ---- AppImage/components/ui/button.tsx | 71 +- AppImage/components/ui/calendar.tsx | 213 ------ AppImage/components/ui/card.tsx | 120 +--- AppImage/components/ui/carousel.tsx | 241 ------- AppImage/components/ui/chart.tsx | 353 ---------- AppImage/components/ui/checkbox.tsx | 32 - AppImage/components/ui/collapsible.tsx | 33 - AppImage/components/ui/command.tsx | 184 ------ AppImage/components/ui/context-menu.tsx | 252 ------- AppImage/components/ui/dialog.tsx | 143 ---- AppImage/components/ui/drawer.tsx | 135 ---- AppImage/components/ui/dropdown-menu.tsx | 257 -------- AppImage/components/ui/form.tsx | 167 ----- AppImage/components/ui/hover-card.tsx | 44 -- AppImage/components/ui/input-otp.tsx | 77 --- AppImage/components/ui/input.tsx | 17 +- AppImage/components/ui/label.tsx | 24 - AppImage/components/ui/menubar.tsx | 276 -------- AppImage/components/ui/navigation-menu.tsx | 166 ----- AppImage/components/ui/pagination.tsx | 127 ---- AppImage/components/ui/popover.tsx | 48 -- AppImage/components/ui/progress.tsx | 46 +- AppImage/components/ui/radio-group.tsx | 45 -- AppImage/components/ui/resizable.tsx | 56 -- AppImage/components/ui/scroll-area.tsx | 86 +-- AppImage/components/ui/select.tsx | 283 ++++---- AppImage/components/ui/separator.tsx | 28 - AppImage/components/ui/sheet.tsx | 139 ---- AppImage/components/ui/sidebar.tsx | 726 --------------------- AppImage/components/ui/skeleton.tsx | 13 - AppImage/components/ui/slider.tsx | 63 -- AppImage/components/ui/sonner.tsx | 25 - AppImage/components/ui/switch.tsx | 31 - AppImage/components/ui/table.tsx | 116 ---- AppImage/components/ui/tabs.tsx | 106 ++- AppImage/components/ui/textarea.tsx | 18 - AppImage/components/ui/toast.tsx | 129 ---- AppImage/components/ui/toaster.tsx | 35 - AppImage/components/ui/toggle-group.tsx | 73 --- AppImage/components/ui/toggle.tsx | 47 -- AppImage/components/ui/tooltip.tsx | 61 -- AppImage/components/ui/use-mobile.tsx | 19 - AppImage/components/ui/use-toast.ts | 191 ------ AppImage/components/virtual-machines.tsx | 317 +++++++++ AppImage/hooks/use-mobile.ts | 19 - AppImage/hooks/use-toast.ts | 191 ------ AppImage/lib/utils.ts | 2 +- AppImage/next.config.mjs | 32 +- AppImage/package.json | 39 +- AppImage/pnpm-lock.yaml | 5 - AppImage/postcss.config.mjs | 8 - AppImage/public/android-chrome-192x192.jpg | Bin 23533 -> 0 bytes AppImage/public/android-chrome-512x512.jpg | Bin 36470 -> 0 bytes AppImage/public/apple-touch-icon.jpg | Bin 33807 -> 0 bytes AppImage/public/favicon-16x16.jpg | Bin 24780 -> 0 bytes AppImage/public/favicon-32x32.jpg | Bin 20699 -> 0 bytes AppImage/public/images/proxmenux-logo.png | Bin 10338 -> 0 bytes AppImage/public/manifest.json | 40 -- AppImage/public/placeholder-logo.png | Bin 568 -> 0 bytes AppImage/public/placeholder-logo.svg | 1 - AppImage/public/placeholder-user.jpg | Bin 1635 -> 0 bytes AppImage/public/placeholder.jpg | Bin 1064 -> 0 bytes AppImage/public/placeholder.svg | 1 - AppImage/styles/globals.css | 125 ---- AppImage/tsconfig.json | 4 +- 84 files changed, 2251 insertions(+), 6071 deletions(-) create mode 100644 AppImage/README.md create mode 100644 AppImage/app/api/flask/route.ts delete mode 100644 AppImage/components.json create mode 100644 AppImage/components/network-metrics.tsx create mode 100644 AppImage/components/proxmox-dashboard.tsx create mode 100644 AppImage/components/storage-metrics.tsx create mode 100644 AppImage/components/system-logs.tsx create mode 100644 AppImage/components/system-overview.tsx create mode 100644 AppImage/components/theme-toggle.tsx delete mode 100644 AppImage/components/ui/accordion.tsx delete mode 100644 AppImage/components/ui/alert-dialog.tsx delete mode 100644 AppImage/components/ui/alert.tsx delete mode 100644 AppImage/components/ui/aspect-ratio.tsx delete mode 100644 AppImage/components/ui/avatar.tsx delete mode 100644 AppImage/components/ui/breadcrumb.tsx delete mode 100644 AppImage/components/ui/calendar.tsx delete mode 100644 AppImage/components/ui/carousel.tsx delete mode 100644 AppImage/components/ui/chart.tsx delete mode 100644 AppImage/components/ui/checkbox.tsx delete mode 100644 AppImage/components/ui/collapsible.tsx delete mode 100644 AppImage/components/ui/command.tsx delete mode 100644 AppImage/components/ui/context-menu.tsx delete mode 100644 AppImage/components/ui/dialog.tsx delete mode 100644 AppImage/components/ui/drawer.tsx delete mode 100644 AppImage/components/ui/dropdown-menu.tsx delete mode 100644 AppImage/components/ui/form.tsx delete mode 100644 AppImage/components/ui/hover-card.tsx delete mode 100644 AppImage/components/ui/input-otp.tsx delete mode 100644 AppImage/components/ui/label.tsx delete mode 100644 AppImage/components/ui/menubar.tsx delete mode 100644 AppImage/components/ui/navigation-menu.tsx delete mode 100644 AppImage/components/ui/pagination.tsx delete mode 100644 AppImage/components/ui/popover.tsx delete mode 100644 AppImage/components/ui/radio-group.tsx delete mode 100644 AppImage/components/ui/resizable.tsx delete mode 100644 AppImage/components/ui/separator.tsx delete mode 100644 AppImage/components/ui/sheet.tsx delete mode 100644 AppImage/components/ui/sidebar.tsx delete mode 100644 AppImage/components/ui/skeleton.tsx delete mode 100644 AppImage/components/ui/slider.tsx delete mode 100644 AppImage/components/ui/sonner.tsx delete mode 100644 AppImage/components/ui/switch.tsx delete mode 100644 AppImage/components/ui/table.tsx delete mode 100644 AppImage/components/ui/textarea.tsx delete mode 100644 AppImage/components/ui/toast.tsx delete mode 100644 AppImage/components/ui/toaster.tsx delete mode 100644 AppImage/components/ui/toggle-group.tsx delete mode 100644 AppImage/components/ui/toggle.tsx delete mode 100644 AppImage/components/ui/tooltip.tsx delete mode 100644 AppImage/components/ui/use-mobile.tsx delete mode 100644 AppImage/components/ui/use-toast.ts create mode 100644 AppImage/components/virtual-machines.tsx delete mode 100644 AppImage/hooks/use-mobile.ts delete mode 100644 AppImage/hooks/use-toast.ts delete mode 100644 AppImage/pnpm-lock.yaml delete mode 100644 AppImage/postcss.config.mjs delete mode 100644 AppImage/public/android-chrome-192x192.jpg delete mode 100644 AppImage/public/android-chrome-512x512.jpg delete mode 100644 AppImage/public/apple-touch-icon.jpg delete mode 100644 AppImage/public/favicon-16x16.jpg delete mode 100644 AppImage/public/favicon-32x32.jpg delete mode 100644 AppImage/public/images/proxmenux-logo.png delete mode 100644 AppImage/public/manifest.json delete mode 100644 AppImage/public/placeholder-logo.png delete mode 100644 AppImage/public/placeholder-logo.svg delete mode 100644 AppImage/public/placeholder-user.jpg delete mode 100644 AppImage/public/placeholder.jpg delete mode 100644 AppImage/public/placeholder.svg delete mode 100644 AppImage/styles/globals.css diff --git a/AppImage/README.md b/AppImage/README.md new file mode 100644 index 0000000..b9a3f52 --- /dev/null +++ b/AppImage/README.md @@ -0,0 +1,59 @@ +# ProxMenux Monitor + +A modern, responsive dashboard for monitoring Proxmox VE systems built with Next.js and React. + +## Features + +- **System Overview**: Real-time monitoring of CPU, memory, temperature, and active VMs/LXC containers +- **Storage Management**: Visual representation of storage distribution and disk performance metrics +- **Network Monitoring**: Network interface statistics and performance graphs +- **Virtual Machines**: Comprehensive view of VMs and LXC containers with resource usage +- **System Logs**: Real-time system log monitoring and filtering +- **Dark/Light Theme**: Toggle between themes with Proxmox-inspired design +- **Responsive Design**: Works seamlessly on desktop and mobile devices + +## Technology Stack + +- **Frontend**: Next.js 15, React 19, TypeScript +- **Styling**: Tailwind CSS with custom Proxmox-inspired theme +- **Charts**: Recharts for data visualization +- **UI Components**: Radix UI primitives with shadcn/ui +- **Backend**: Flask server for system data collection +- **Packaging**: AppImage for easy distribution + +## Development + +\`\`\`bash +# Install dependencies +npm install + +# Start development server +npm run dev + +# Build for production +npm run build +\`\`\` + +## Building AppImage + +\`\`\`bash +# Make build script executable +chmod +x scripts/build_appimage.sh + +# Build AppImage +./scripts/build_appimage.sh +\`\`\` + +## Translation Support + +The project includes a translation system for multi-language support: + +\`\`\`bash +# Build translation AppImage +chmod +x scripts/build_translate_appimage.sh +./scripts/build_translate_appimage.sh +\`\`\` + +## License + +MIT License - see LICENSE file for details. diff --git a/AppImage/app/api/flask/route.ts b/AppImage/app/api/flask/route.ts new file mode 100644 index 0000000..a5264ec --- /dev/null +++ b/AppImage/app/api/flask/route.ts @@ -0,0 +1,78 @@ +import { type NextRequest, NextResponse } from "next/server" + +// This will be the bridge between Next.js and the Flask server +// For now, we'll return mock data that simulates what the Flask server would provide + +export async function GET(request: NextRequest) { + const { searchParams } = new URL(request.url) + const endpoint = searchParams.get("endpoint") + + // Mock data that would come from the Flask server running on port 8008 + const mockData = { + system: { + cpu_usage: 67.3, + memory_usage: 49.4, + temperature: 52, + uptime: "15d 7h 23m", + load_average: [1.23, 1.45, 1.67], + }, + storage: { + total: 2000, + used: 1250, + available: 750, + disks: [ + { name: "/dev/sda", type: "HDD", size: 1000, used: 650, health: "healthy", temp: 42 }, + { name: "/dev/sdb", type: "HDD", size: 1000, used: 480, health: "healthy", temp: 38 }, + { name: "/dev/sdc", type: "SSD", size: 500, used: 120, health: "healthy", temp: 35 }, + { name: "/dev/nvme0n1", type: "NVMe", size: 1000, used: 340, health: "warning", temp: 55 }, + ], + }, + network: { + interfaces: [ + { name: "vmbr0", type: "Bridge", status: "up", ip: "192.168.1.100/24", speed: "1000 Mbps" }, + { name: "enp1s0", type: "Physical", status: "up", ip: "192.168.1.101/24", speed: "1000 Mbps" }, + ], + traffic: { + incoming: 89, + outgoing: 67, + }, + }, + vms: [ + { + id: 100, + name: "web-server-01", + status: "running", + os: "Ubuntu 22.04", + cpu: 4, + memory: 8192, + disk: 50, + uptime: "15d 7h 23m", + cpu_usage: 45, + memory_usage: 62, + disk_usage: 78, + }, + ], + } + + try { + // In the real implementation, this would make a request to the Flask server + // const response = await fetch(`http://localhost:8008/api/${endpoint}`) + // const data = await response.json() + + // For now, return mock data based on the endpoint + switch (endpoint) { + case "system": + return NextResponse.json(mockData.system) + case "storage": + return NextResponse.json(mockData.storage) + case "network": + return NextResponse.json(mockData.network) + case "vms": + return NextResponse.json(mockData.vms) + default: + return NextResponse.json(mockData) + } + } catch (error) { + return NextResponse.json({ error: "Failed to fetch data from Flask server" }, { status: 500 }) + } +} diff --git a/AppImage/app/globals.css b/AppImage/app/globals.css index f08e712..c21cb4d 100644 --- a/AppImage/app/globals.css +++ b/AppImage/app/globals.css @@ -4,6 +4,7 @@ @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); @@ -31,54 +32,49 @@ --radius: 0.625rem; --sidebar: oklch(0.985 0 0); --sidebar-foreground: oklch(0.145 0 0); - --header-bg: oklch(1 0 0); - --header-foreground: oklch(0.145 0 0); } .dark { - --background: oklch(0.205 0.005 240); /* Proxmox dark gray #2b2f36 */ - --foreground: oklch(0.985 0 0); - --card: oklch(0.235 0.005 240); /* Slightly lighter gray for cards #363c45 */ - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.235 0.005 240); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.985 0 0); - --primary-foreground: oklch(0.205 0 0); - --secondary: oklch(0.285 0.005 240); /* Better contrast for secondary elements */ - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.285 0.005 240); - --muted-foreground: oklch(0.708 0 0); /* Better contrast for muted text */ - --accent: oklch(0.285 0.005 240); - --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.335 0.005 240); /* More visible borders */ - --input: oklch(0.285 0.005 240); - --ring: oklch(0.439 0 0); - --chart-1: oklch(0.65 0.2 220); /* Bright Blue */ - --chart-2: oklch(0.65 0.2 140); /* Bright Green */ - --chart-3: oklch(0.7 0.2 50); /* Bright Yellow */ - --chart-4: oklch(0.65 0.2 300); /* Bright Purple */ - --chart-5: oklch(0.65 0.2 20); /* Bright Orange */ - --radius-sm: calc(var(--radius) - 4px); - --radius-md: calc(var(--radius) - 2px); - --radius-lg: var(--radius); - --radius-xl: calc(var(--radius) + 4px); - --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.285 0.005 240); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(0.285 0.005 240); - --sidebar-ring: oklch(0.439 0 0); - --header-bg: oklch(0 0 0); - --header-foreground: oklch(1 0 0); + /* Proxmox dark theme with proper gray background (#2b2f36) */ + --background: #2b2f36; /* Proxmox dark gray */ + --foreground: #ffffff; + --card: #363c45; /* Slightly lighter gray for cards */ + --card-foreground: #ffffff; + --popover: #363c45; + --popover-foreground: #ffffff; + --primary: #ffffff; + --primary-foreground: #2b2f36; + --secondary: #4a5058; /* Better contrast for secondary elements */ + --secondary-foreground: #ffffff; + --muted: #4a5058; + --muted-foreground: #b4b4b4; /* Better contrast for muted text */ + --accent: #4a5058; + --accent-foreground: #ffffff; + --destructive: #ef4444; + --destructive-foreground: #ffffff; + --border: #525862; /* More visible borders */ + --input: #4a5058; + --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: #2b2f36; + --sidebar-foreground: #ffffff; + --sidebar-primary: #6366f1; + --sidebar-primary-foreground: #ffffff; + --sidebar-accent: #4a5058; + --sidebar-accent-foreground: #ffffff; + --sidebar-border: #4a5058; + --sidebar-ring: #6b7280; + /* Header is black only in dark mode */ + --header-bg: #000000; + --header-foreground: #ffffff; } @theme inline { - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); --color-background: var(--background); --color-foreground: var(--foreground); --color-card: var(--card); @@ -115,6 +111,9 @@ --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 { @@ -124,78 +123,80 @@ body { @apply bg-background text-foreground; } - .header-bg { - background-color: var(--header-bg); - color: var(--header-foreground); - backdrop-filter: none; /* Remove any blur effects */ - } - /* 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; - } +} + +/* Header styling that adapts to theme */ +.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/app/layout.tsx b/AppImage/app/layout.tsx index f4009cc..27a5ad7 100644 --- a/AppImage/app/layout.tsx +++ b/AppImage/app/layout.tsx @@ -9,8 +9,21 @@ import "./globals.css" export const metadata: Metadata = { title: "ProxMenux Monitor", - description: "Proxmox System Dashboard", + description: "Proxmox System Dashboard and Monitor", generator: "v0.app", + manifest: "/manifest.json", + icons: { + icon: [ + { url: "/favicon-16x16.png", sizes: "16x16", type: "image/png" }, + { url: "/favicon-32x32.png", sizes: "32x32", type: "image/png" }, + ], + apple: [{ url: "/apple-touch-icon.png", sizes: "180x180", type: "image/png" }], + }, + viewport: "width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no", + themeColor: [ + { media: "(prefers-color-scheme: light)", color: "#ffffff" }, + { media: "(prefers-color-scheme: dark)", color: "#1a1a1a" }, + ], } export default function RootLayout({ @@ -20,13 +33,13 @@ export default function RootLayout({ }>) { return ( - - + + Loading...}> {children} + - ) diff --git a/AppImage/app/page.tsx b/AppImage/app/page.tsx index c6d59e1..47629db 100644 --- a/AppImage/app/page.tsx +++ b/AppImage/app/page.tsx @@ -1,6 +1,6 @@ -import { ProxmoxDashboard } from "../AppImage/components/proxmox-dashboard" +import { ProxmoxDashboard } from "../components/proxmox-dashboard" -export default function Page() { +export default function Home() { return (
diff --git a/AppImage/components.json b/AppImage/components.json deleted file mode 100644 index 4ee62ee..0000000 --- a/AppImage/components.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "new-york", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "", - "css": "app/globals.css", - "baseColor": "neutral", - "cssVariables": true, - "prefix": "" - }, - "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" - }, - "iconLibrary": "lucide" -} diff --git a/AppImage/components/network-metrics.tsx b/AppImage/components/network-metrics.tsx new file mode 100644 index 0000000..8d3a714 --- /dev/null +++ b/AppImage/components/network-metrics.tsx @@ -0,0 +1,267 @@ +"use client" + +import { Card, CardContent, CardHeader, CardTitle } from "./ui/card" +import { Badge } from "./ui/badge" +import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, AreaChart, Area } from "recharts" +import { Wifi, Globe, Shield, Activity, Network, Router } from "lucide-react" + +const networkTraffic = [ + { time: "00:00", incoming: 45, outgoing: 32 }, + { time: "04:00", incoming: 52, outgoing: 28 }, + { time: "08:00", incoming: 78, outgoing: 65 }, + { time: "12:00", incoming: 65, outgoing: 45 }, + { time: "16:00", incoming: 82, outgoing: 58 }, + { time: "20:00", incoming: 58, outgoing: 42 }, + { time: "24:00", incoming: 43, outgoing: 35 }, +] + +const connectionData = [ + { time: "00:00", connections: 1250 }, + { time: "04:00", connections: 980 }, + { time: "08:00", connections: 1850 }, + { time: "12:00", connections: 1650 }, + { time: "16:00", connections: 2100 }, + { time: "20:00", connections: 1580 }, + { time: "24:00", connections: 1320 }, +] + +export function NetworkMetrics() { + return ( +
+ {/* Network Overview Cards */} +
+ + + Network Traffic + + + +
156 MB/s
+
+ ↓ 89 MB/s + ↑ 67 MB/s +
+

Peak: 245 MB/s at 16:30

+
+
+ + + + Active Connections + + + +
1,847
+
+ + Normal + +
+

+ ↑ 12% from last hour +

+
+
+ + + + Firewall Status + + + +
Active
+
+ + Protected + +
+

247 blocked attempts today

+
+
+ + + + + + Latency + + + +
12ms
+
+ + Excellent + +
+

Avg response time

+
+
+
+ + {/* Network Charts */} +
+ + + + + Network Traffic (24h) + + + + + + + + + [`${value} MB/s`, name === "incoming" ? "Incoming" : "Outgoing"]} + /> + + + + + + + + + + + + Active Connections (24h) + + + + + + + + + [`${value}`, "Connections"]} + /> + + + + + +
+ + {/* Network Interfaces */} + + + + + Network Interfaces + + + +
+ {[ + { + name: "vmbr0", + type: "Bridge", + status: "up", + ip: "192.168.1.100/24", + speed: "1000 Mbps", + rx: "2.3 GB", + tx: "1.8 GB", + }, + { + name: "enp1s0", + type: "Physical", + status: "up", + ip: "192.168.1.101/24", + speed: "1000 Mbps", + rx: "1.2 GB", + tx: "890 MB", + }, + { + name: "vmbr1", + type: "Bridge", + status: "up", + ip: "10.0.0.1/24", + speed: "1000 Mbps", + rx: "456 MB", + tx: "234 MB", + }, + { + name: "tap101i0", + type: "TAP", + status: "up", + ip: "10.0.0.101/24", + speed: "1000 Mbps", + rx: "123 MB", + tx: "89 MB", + }, + ].map((interface_, index) => ( +
+
+ +
+
{interface_.name}
+
+ {interface_.type} • {interface_.speed} +
+
+
+ +
+
+
IP Address
+
{interface_.ip}
+
+ +
+
RX / TX
+
+ {interface_.rx} / {interface_.tx} +
+
+ + + {interface_.status.toUpperCase()} + +
+
+ ))} +
+
+
+
+ ) +} diff --git a/AppImage/components/proxmox-dashboard.tsx b/AppImage/components/proxmox-dashboard.tsx new file mode 100644 index 0000000..626a28a --- /dev/null +++ b/AppImage/components/proxmox-dashboard.tsx @@ -0,0 +1,246 @@ +"use client" + +import { useState, useEffect } from "react" +import { Badge } from "./ui/badge" +import { Button } from "./ui/button" +import { Tabs, TabsContent, TabsList, TabsTrigger } from "./ui/tabs" +import { SystemOverview } from "./system-overview" +import { StorageMetrics } from "./storage-metrics" +import { NetworkMetrics } from "./network-metrics" +import { VirtualMachines } from "./virtual-machines" +import { SystemLogs } from "./system-logs" +import { RefreshCw, AlertTriangle, CheckCircle, XCircle, Languages, Server } from "lucide-react" +import Image from "next/image" +import { ThemeToggle } from "./theme-toggle" + +interface SystemStatus { + status: "healthy" | "warning" | "critical" + uptime: string + lastUpdate: string + serverName: string + nodeId: string +} + +export function ProxmoxDashboard() { + const [systemStatus, setSystemStatus] = useState({ + status: "healthy", + uptime: "15d 7h 23m", + lastUpdate: new Date().toLocaleTimeString(), + serverName: "proxmox-01", + nodeId: "pve-node-01", + }) + const [isRefreshing, setIsRefreshing] = useState(false) + const [isTranslating, setIsTranslating] = useState(false) + + useEffect(() => { + const fetchServerInfo = async () => { + try { + const response = await fetch("/api/flask/system-info") + if (response.ok) { + const data = await response.json() + setSystemStatus((prev) => ({ + ...prev, + serverName: data.hostname || "proxmox-01", + nodeId: data.node_id || "pve-node-01", + })) + } + } catch (error) { + console.log("[v0] Using default server name due to API error:", error) + } + } + + fetchServerInfo() + }, []) + + const refreshData = async () => { + setIsRefreshing(true) + await new Promise((resolve) => setTimeout(resolve, 1000)) + setSystemStatus((prev) => ({ + ...prev, + lastUpdate: new Date().toLocaleTimeString(), + })) + setIsRefreshing(false) + } + + const translatePage = async () => { + setIsTranslating(true) + try { + if ("translate" in document.documentElement.dataset) { + const currentLang = document.documentElement.dataset.translate + document.documentElement.dataset.translate = currentLang === "yes" ? "no" : "yes" + } else { + const googleTranslateScript = document.createElement("script") + googleTranslateScript.src = "https://translate.google.com/translate_a/element.js?cb=googleTranslateElementInit" + document.head.appendChild(googleTranslateScript) + + window.googleTranslateElementInit = () => { + new window.google.translate.TranslateElement( + { + pageLanguage: "en", + includedLanguages: "es,en,fr,de,it,pt,ru,zh,ja,ko", + layout: window.google.translate.TranslateElement.InlineLayout.SIMPLE, + }, + "google_translate_element", + ) + } + } + } catch (error) { + console.error("Translation error:", error) + } + setIsTranslating(false) + } + + const getStatusIcon = () => { + switch (systemStatus.status) { + case "healthy": + return + case "warning": + return + case "critical": + return + } + } + + const getStatusColor = () => { + switch (systemStatus.status) { + case "healthy": + return "bg-green-500/10 text-green-500 border-green-500/20" + case "warning": + return "bg-yellow-500/10 text-yellow-500 border-yellow-500/20" + case "critical": + return "bg-red-500/10 text-red-500 border-red-500/20" + } + } + + return ( +
+
+
+
+
+
+
+ ProxMenux Logo +
+
+

ProxMenux Monitor

+

Proxmox System Dashboard

+
+
+
+
+ +
+
{systemStatus.nodeId}
+
+
+
+
+ +
+ + {getStatusIcon()} + {systemStatus.status} + + +
Uptime: {systemStatus.uptime}
+ + + + + + +
+
+
+
+
+ +
+ + + + Overview + + + Storage + + + Network + + + Virtual Machines + + + System Logs + + + + + + + + + + + + + + + + + + + + + + + + +
+

Last updated: {systemStatus.lastUpdate} • ProxMenux Monitor v1.0.0

+
+
+
+ ) +} diff --git a/AppImage/components/storage-metrics.tsx b/AppImage/components/storage-metrics.tsx new file mode 100644 index 0000000..b22a03f --- /dev/null +++ b/AppImage/components/storage-metrics.tsx @@ -0,0 +1,243 @@ +"use client" + +import { Card, CardContent, CardHeader, CardTitle } from "./ui/card" +import { Progress } from "./ui/progress" +import { Badge } from "./ui/badge" +import { ResponsiveContainer, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip } from "recharts" +import { HardDrive, Database, Archive, AlertTriangle, CheckCircle, Activity } from "lucide-react" + +const storageData = [ + { name: "Used", value: 1250, color: "#3b82f6" }, // Blue + { name: "Available", value: 750, color: "#10b981" }, // Green +] + +const diskPerformance = [ + { disk: "sda", read: 45, write: 32, iops: 1250 }, + { disk: "sdb", read: 67, write: 28, iops: 980 }, + { disk: "sdc", read: 23, write: 45, iops: 1100 }, + { disk: "nvme0n1", read: 156, write: 89, iops: 3400 }, +] + +export function StorageMetrics() { + return ( +
+ {/* Storage Overview Cards */} +
+ + + Total Storage + + + +
2.0 TB
+ +

1.25 TB used • 750 GB available

+
+
+ + + + VM & LXC Storage + + + +
890 GB
+ +

71.2% of allocated space

+
+
+ + + + + + Backups + + + +
245 GB
+
+ + 12 Backups + +
+

Last backup: 2h ago

+
+
+ + + + + + IOPS + + + +
6.7K
+
+ Read: 4.2K + Write: 2.5K +
+

Average operations/sec

+
+
+
+ + {/* Storage Distribution and Performance */} +
+ + + + + Storage Distribution + + + +
+
+
+ Used Storage + 1.25 TB (62.5%) +
+
+
+
+
+ +
+
+ Available Storage + 750 GB (37.5%) +
+
+
+
+
+ +
+
+
+
+
+ Used +
+
1.25 TB
+
+
+
+
+ Available +
+
750 GB
+
+
+
+
+
+
+ + + + + + Disk Performance + + + + + + + + + + + + + + + +
+ + {/* Disk Details */} + + + + + Storage Devices + + + +
+ {[ + { name: "/dev/sda", type: "HDD", size: "1TB", used: "650GB", health: "healthy", temp: "42°C" }, + { name: "/dev/sdb", type: "HDD", size: "1TB", used: "480GB", health: "healthy", temp: "38°C" }, + { name: "/dev/sdc", type: "SSD", size: "500GB", used: "120GB", health: "healthy", temp: "35°C" }, + { name: "/dev/nvme0n1", type: "NVMe", size: "1TB", used: "340GB", health: "warning", temp: "55°C" }, + ].map((disk, index) => ( +
+
+ +
+
{disk.name}
+
+ {disk.type} • {disk.size} +
+
+
+ +
+
+
+ {disk.used} / {disk.size} +
+ +
+ +
+
Temp
+
{disk.temp}
+
+ + + {disk.health === "healthy" ? ( + + ) : ( + + )} + {disk.health} + +
+
+ ))} +
+
+
+
+ ) +} diff --git a/AppImage/components/system-logs.tsx b/AppImage/components/system-logs.tsx new file mode 100644 index 0000000..5ef052b --- /dev/null +++ b/AppImage/components/system-logs.tsx @@ -0,0 +1,275 @@ +"use client" + +import { Card, CardContent, CardHeader, CardTitle } from "./ui/card" +import { Badge } from "./ui/badge" +import { Button } from "./ui/button" +import { Input } from "./ui/input" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select" +import { ScrollArea } from "./ui/scroll-area" +import { FileText, Search, Download, AlertTriangle, Info, CheckCircle, XCircle } from "lucide-react" +import { useState } from "react" + +const systemLogs = [ + { + timestamp: "2024-01-15 14:32:15", + level: "info", + service: "pveproxy", + message: "User root@pam authenticated successfully", + source: "auth.log", + }, + { + timestamp: "2024-01-15 14:31:45", + level: "warning", + service: "pvedaemon", + message: "VM 101 high memory usage detected (85%)", + source: "syslog", + }, + { + timestamp: "2024-01-15 14:30:22", + level: "error", + service: "pve-cluster", + message: "Failed to connect to cluster node pve-02", + source: "cluster.log", + }, + { + timestamp: "2024-01-15 14:29:18", + level: "info", + service: "pvestatd", + message: "Storage local: 1.25TB used, 750GB available", + source: "syslog", + }, + { + timestamp: "2024-01-15 14:28:33", + level: "info", + service: "pve-firewall", + message: "Blocked connection attempt from 192.168.1.50", + source: "firewall.log", + }, + { + timestamp: "2024-01-15 14:27:45", + level: "warning", + service: "smartd", + message: "SMART warning: /dev/nvme0n1 temperature high (55°C)", + source: "smart.log", + }, + { + timestamp: "2024-01-15 14:26:12", + level: "info", + service: "pveproxy", + message: "Started backup job for VM 100", + source: "backup.log", + }, + { + timestamp: "2024-01-15 14:25:38", + level: "error", + service: "qemu-server", + message: "VM 102 failed to start: insufficient memory", + source: "qemu.log", + }, + { + timestamp: "2024-01-15 14:24:55", + level: "info", + service: "pvedaemon", + message: "VM 103 migrated successfully to node pve-01", + source: "migration.log", + }, + { + timestamp: "2024-01-15 14:23:17", + level: "warning", + service: "pve-ha-lrm", + message: "Resource VM:104 state changed to error", + source: "ha.log", + }, +] + +export function SystemLogs() { + const [searchTerm, setSearchTerm] = useState("") + const [levelFilter, setLevelFilter] = useState("all") + const [serviceFilter, setServiceFilter] = useState("all") + + const filteredLogs = systemLogs.filter((log) => { + const matchesSearch = + log.message.toLowerCase().includes(searchTerm.toLowerCase()) || + log.service.toLowerCase().includes(searchTerm.toLowerCase()) + const matchesLevel = levelFilter === "all" || log.level === levelFilter + const matchesService = serviceFilter === "all" || log.service === serviceFilter + + return matchesSearch && matchesLevel && matchesService + }) + + const getLevelColor = (level: string) => { + switch (level) { + case "error": + return "bg-red-500/10 text-red-500 border-red-500/20" + case "warning": + return "bg-yellow-500/10 text-yellow-500 border-yellow-500/20" + case "info": + return "bg-blue-500/10 text-blue-500 border-blue-500/20" + default: + return "bg-gray-500/10 text-gray-500 border-gray-500/20" + } + } + + const getLevelIcon = (level: string) => { + switch (level) { + case "error": + return + case "warning": + return + case "info": + return + default: + return + } + } + + const logCounts = { + total: systemLogs.length, + error: systemLogs.filter((log) => log.level === "error").length, + warning: systemLogs.filter((log) => log.level === "warning").length, + info: systemLogs.filter((log) => log.level === "info").length, + } + + const uniqueServices = [...new Set(systemLogs.map((log) => log.service))] + + return ( +
+ {/* Log Statistics */} +
+ + + Total Logs + + + +
{logCounts.total}
+

Last 24 hours

+
+
+ + + + Errors + + + +
{logCounts.error}
+

Requires attention

+
+
+ + + + Warnings + + + +
{logCounts.warning}
+

Monitor closely

+
+
+ + + + Info + + + +
{logCounts.info}
+

Normal operations

+
+
+
+ + {/* Log Filters and Search */} + + + + + System Logs + + + +
+
+
+ + setSearchTerm(e.target.value)} + className="pl-10 bg-background border-border" + /> +
+
+ + + + + + +
+ + +
+ {filteredLogs.map((log, index) => ( +
+
+ + {getLevelIcon(log.level)} + {log.level.toUpperCase()} + +
+ +
+
+
{log.service}
+
{log.timestamp}
+
+
{log.message}
+
Source: {log.source}
+
+
+ ))} + + {filteredLogs.length === 0 && ( +
+ +

No logs found matching your criteria

+
+ )} +
+
+
+
+
+ ) +} diff --git a/AppImage/components/system-overview.tsx b/AppImage/components/system-overview.tsx new file mode 100644 index 0000000..5c9b319 --- /dev/null +++ b/AppImage/components/system-overview.tsx @@ -0,0 +1,249 @@ +"use client" + +import { Card, CardContent, CardHeader, CardTitle } from "./ui/card" +import { Progress } from "./ui/progress" +import { Badge } from "./ui/badge" +import { XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, AreaChart, Area } from "recharts" +import { Cpu, MemoryStick, Thermometer, Users, Activity, Server, Zap } from "lucide-react" + +const cpuData = [ + { time: "00:00", value: 45 }, + { time: "04:00", value: 52 }, + { time: "08:00", value: 78 }, + { time: "12:00", value: 65 }, + { time: "16:00", value: 82 }, + { time: "20:00", value: 58 }, + { time: "24:00", value: 43 }, +] + +const memoryData = [ + { time: "00:00", used: 12.5, available: 19.5 }, + { time: "04:00", used: 14.2, available: 17.8 }, + { time: "08:00", used: 18.7, available: 13.3 }, + { time: "12:00", used: 16.3, available: 15.7 }, + { time: "16:00", used: 21.1, available: 10.9 }, + { time: "20:00", used: 15.8, available: 16.2 }, + { time: "24:00", used: 13.2, available: 18.8 }, +] + +export function SystemOverview() { + return ( +
+ {/* Key Metrics Cards */} +
+ + + CPU Usage + + + +
67.3%
+ +

+ ↓ 2.1% from last hour +

+
+
+ + + + Memory Usage + + + +
15.8 GB
+ +

+ 49.4% of 32 GB • ↑ 1.2 GB +

+
+
+ + + + Temperature + + + +
52°C
+
+ + Normal + +
+

Max: 78°C • Avg: 48°C

+
+
+ + + + Active VMs & LXC + + + +
15
+
+ + 8 Running VMs + + + 3 Running LXC + + + 4 Stopped + +
+

Total: 12 VMs • 6 LXC configured

+
+
+
+ + {/* Charts Section */} +
+ + + + + CPU Usage (24h) + + + + + + + + + + + + + + + + + + + + Memory Usage (24h) + + + + + + + + + + + + + + + +
+ + {/* System Information */} +
+ + + + + System Information + + + +
+ Hostname: + proxmox-01 +
+
+ Version: + PVE 8.1.3 +
+
+ Kernel: + 6.5.11-7-pve +
+
+ Architecture: + x86_64 +
+
+
+ + + + + + Active Sessions + + + +
+ Web Console: + + 3 active + +
+
+ SSH Sessions: + + 1 active + +
+
+ API Calls: + 247/hour +
+
+
+ + + + + + Power & Performance + + + +
+ Power State: + + Running + +
+
+ Load Average: + 1.23, 1.45, 1.67 +
+
+ Boot Time: + 2.3s +
+
+
+
+
+ ) +} diff --git a/AppImage/components/theme-toggle.tsx b/AppImage/components/theme-toggle.tsx new file mode 100644 index 0000000..eb4c91c --- /dev/null +++ b/AppImage/components/theme-toggle.tsx @@ -0,0 +1,22 @@ +"use client" +import { Moon, Sun } from "lucide-react" +import { useTheme } from "next-themes" + +import { Button } from "./ui/button" + +export function ThemeToggle() { + const { theme, setTheme } = useTheme() + + return ( + + ) +} diff --git a/AppImage/components/ui/accordion.tsx b/AppImage/components/ui/accordion.tsx deleted file mode 100644 index e538a33..0000000 --- a/AppImage/components/ui/accordion.tsx +++ /dev/null @@ -1,66 +0,0 @@ -'use client' - -import * as React from 'react' -import * as AccordionPrimitive from '@radix-ui/react-accordion' -import { ChevronDownIcon } from 'lucide-react' - -import { cn } from '@/lib/utils' - -function Accordion({ - ...props -}: React.ComponentProps) { - return -} - -function AccordionItem({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function AccordionTrigger({ - className, - children, - ...props -}: React.ComponentProps) { - return ( - - svg]:rotate-180', - className, - )} - {...props} - > - {children} - - - - ) -} - -function AccordionContent({ - className, - children, - ...props -}: React.ComponentProps) { - return ( - -
{children}
-
- ) -} - -export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/AppImage/components/ui/alert-dialog.tsx b/AppImage/components/ui/alert-dialog.tsx deleted file mode 100644 index 9704452..0000000 --- a/AppImage/components/ui/alert-dialog.tsx +++ /dev/null @@ -1,157 +0,0 @@ -'use client' - -import * as React from 'react' -import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog' - -import { cn } from '@/lib/utils' -import { buttonVariants } from '@/components/ui/button' - -function AlertDialog({ - ...props -}: React.ComponentProps) { - return -} - -function AlertDialogTrigger({ - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function AlertDialogPortal({ - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function AlertDialogOverlay({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function AlertDialogContent({ - className, - ...props -}: React.ComponentProps) { - return ( - - - - - ) -} - -function AlertDialogHeader({ - className, - ...props -}: React.ComponentProps<'div'>) { - return ( -
- ) -} - -function AlertDialogFooter({ - className, - ...props -}: React.ComponentProps<'div'>) { - return ( -
- ) -} - -function AlertDialogTitle({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function AlertDialogDescription({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function AlertDialogAction({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function AlertDialogCancel({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) -} - -export { - AlertDialog, - AlertDialogPortal, - AlertDialogOverlay, - AlertDialogTrigger, - AlertDialogContent, - AlertDialogHeader, - AlertDialogFooter, - AlertDialogTitle, - AlertDialogDescription, - AlertDialogAction, - AlertDialogCancel, -} diff --git a/AppImage/components/ui/alert.tsx b/AppImage/components/ui/alert.tsx deleted file mode 100644 index e6751ab..0000000 --- a/AppImage/components/ui/alert.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import * as React from 'react' -import { cva, type VariantProps } from 'class-variance-authority' - -import { cn } from '@/lib/utils' - -const alertVariants = cva( - 'relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current', - { - variants: { - variant: { - default: 'bg-card text-card-foreground', - destructive: - 'text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90', - }, - }, - defaultVariants: { - variant: 'default', - }, - }, -) - -function Alert({ - className, - variant, - ...props -}: React.ComponentProps<'div'> & VariantProps) { - return ( -
- ) -} - -function AlertTitle({ className, ...props }: React.ComponentProps<'div'>) { - return ( -
- ) -} - -function AlertDescription({ - className, - ...props -}: React.ComponentProps<'div'>) { - return ( -
- ) -} - -export { Alert, AlertTitle, AlertDescription } diff --git a/AppImage/components/ui/aspect-ratio.tsx b/AppImage/components/ui/aspect-ratio.tsx deleted file mode 100644 index 40bb120..0000000 --- a/AppImage/components/ui/aspect-ratio.tsx +++ /dev/null @@ -1,11 +0,0 @@ -'use client' - -import * as AspectRatioPrimitive from '@radix-ui/react-aspect-ratio' - -function AspectRatio({ - ...props -}: React.ComponentProps) { - return -} - -export { AspectRatio } diff --git a/AppImage/components/ui/avatar.tsx b/AppImage/components/ui/avatar.tsx deleted file mode 100644 index aa98465..0000000 --- a/AppImage/components/ui/avatar.tsx +++ /dev/null @@ -1,53 +0,0 @@ -'use client' - -import * as React from 'react' -import * as AvatarPrimitive from '@radix-ui/react-avatar' - -import { cn } from '@/lib/utils' - -function Avatar({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function AvatarImage({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) -} - -function AvatarFallback({ - className, - ...props -}: React.ComponentProps) { - return ( - - ) -} - -export { Avatar, AvatarImage, AvatarFallback } diff --git a/AppImage/components/ui/badge.tsx b/AppImage/components/ui/badge.tsx index fc4126b..78b3863 100644 --- a/AppImage/components/ui/badge.tsx +++ b/AppImage/components/ui/badge.tsx @@ -1,46 +1,28 @@ -import * as React from 'react' -import { Slot } from '@radix-ui/react-slot' -import { cva, type VariantProps } from 'class-variance-authority' - -import { cn } from '@/lib/utils' +import type * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" +import { cn } from "@/lib/utils" const badgeVariants = cva( - 'inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden', + "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", { variants: { variant: { - default: - 'border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90', - secondary: - 'border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90', - destructive: - 'border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', - outline: - 'text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground', + default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", + secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", + outline: "text-foreground", }, }, defaultVariants: { - variant: 'default', + variant: "default", }, }, ) -function Badge({ - className, - variant, - asChild = false, - ...props -}: React.ComponentProps<'span'> & - VariantProps & { asChild?: boolean }) { - const Comp = asChild ? Slot : 'span' +export interface BadgeProps extends React.HTMLAttributes, VariantProps {} - return ( - - ) +function Badge({ className, variant, ...props }: BadgeProps) { + return
} export { Badge, badgeVariants } diff --git a/AppImage/components/ui/breadcrumb.tsx b/AppImage/components/ui/breadcrumb.tsx deleted file mode 100644 index 1750ff2..0000000 --- a/AppImage/components/ui/breadcrumb.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import * as React from 'react' -import { Slot } from '@radix-ui/react-slot' -import { ChevronRight, MoreHorizontal } from 'lucide-react' - -import { cn } from '@/lib/utils' - -function Breadcrumb({ ...props }: React.ComponentProps<'nav'>) { - return