changed UI layout

new Navigation
Dashboard
This commit is contained in:
Philipp committed 2026-05-21 12:49:32 +02:00
1 parent bef27416bf
commit 7eb9fdef18
5 files changed
+707 -179

No files matched your search

+127 -100
View File
@@ -1,10 +1,23 @@
import { useEffect, useMemo, useState } from 'react';
import { AlertTriangle, CalendarClock, ClipboardList, DatabaseBackup, LogOut, Network, RefreshCw, Settings as SettingsIcon } from 'lucide-react';
import {
AlertTriangle,
CalendarClock,
ClipboardList,
Database,
DatabaseBackup,
LogOut,
Moon,
Network,
RefreshCw,
Settings as SettingsIcon,
Shield,
} from 'lucide-react';
import { api, errorMessage } from './api.js';
import { Dashboard } from './components/Dashboard.jsx';
import { Login } from './components/Login.jsx';
import { Nodes } from './components/Nodes.jsx';
import { Operations } from './components/Operations.jsx';
import { Repositories } from './components/Repositories.jsx';
import { Scheduler } from './components/Scheduler.jsx';
import { Settings } from './components/Settings.jsx';
import { VMDetail } from './components/VMDetail.jsx';
@@ -97,122 +110,136 @@ export default function App() {
return <Login onLogin={loadSession} />;
}
const pageTitle = currentVm
? currentVm.name
: {
dashboard: 'Backups',
nodes: 'Nodes',
repositories: 'Repositories',
operations: 'Operations',
scheduler: 'Scheduler',
settings: 'Settings',
}[view] || 'Backups';
return (
<main className="min-h-screen bg-zinc-950 text-zinc-100">
<header className="border-b border-zinc-800 bg-zinc-950/95">
<div className="mx-auto flex max-w-7xl items-center justify-between gap-4 px-4 py-4 sm:px-6">
<div className="grid min-h-screen lg:grid-cols-[260px_1fr]">
<aside className="flex flex-col border-r border-zinc-800 bg-zinc-900/80">
<button
className="flex items-center gap-3 text-left"
className="flex h-20 w-full items-center gap-3 border-b border-zinc-800 px-6 text-left"
onClick={() => {
setSelectedVm(null);
setView('dashboard');
}}
type="button"
>
<span className="flex h-9 w-9 items-center justify-center rounded-md border border-zinc-800 bg-zinc-900">
<DatabaseBackup className="h-5 w-5 text-cyan-300" />
<span className="flex h-9 w-9 items-center justify-center rounded-md text-red-400">
<DatabaseBackup className="h-6 w-6" />
</span>
<span>
<span className="block text-sm font-semibold tracking-wide text-zinc-100">Incus Backup Control</span>
<span className="block text-xs text-zinc-500">ZFS block-level Restic operations</span>
<span className="block text-base font-semibold tracking-wide text-zinc-100">Incus Backup</span>
<span className="block text-xs text-zinc-500">Management UI</span>
</span>
</button>
<div className="flex items-center gap-2">
<button
className="inline-flex h-9 items-center gap-2 rounded-md border border-zinc-800 bg-zinc-900 px-3 text-sm text-zinc-200 hover:border-zinc-700"
onClick={() => {
setSelectedVm(null);
setView('nodes');
}}
type="button"
>
<Network className="h-4 w-4" />
Nodes
</button>
<button
className="inline-flex h-9 items-center gap-2 rounded-md border border-zinc-800 bg-zinc-900 px-3 text-sm text-zinc-200 hover:border-zinc-700"
onClick={() => {
setSelectedVm(null);
setView('scheduler');
}}
type="button"
>
<CalendarClock className="h-4 w-4" />
Scheduler
</button>
<button
className="inline-flex h-9 items-center gap-2 rounded-md border border-zinc-800 bg-zinc-900 px-3 text-sm text-zinc-200 hover:border-zinc-700"
onClick={() => {
setSelectedVm(null);
setView('operations');
}}
type="button"
>
<ClipboardList className="h-4 w-4" />
Operations
</button>
<button
className="inline-flex h-9 items-center gap-2 rounded-md border border-zinc-800 bg-zinc-900 px-3 text-sm text-zinc-200 hover:border-zinc-700"
onClick={() => {
setSelectedVm(null);
setView('settings');
}}
type="button"
>
<SettingsIcon className="h-4 w-4" />
Settings
</button>
<button
className="inline-flex h-9 items-center gap-2 rounded-md border border-zinc-800 bg-zinc-900 px-3 text-sm text-zinc-200 hover:border-zinc-700"
onClick={refresh}
type="button"
>
<RefreshCw className="h-4 w-4" />
Refresh
</button>
<button
className="inline-flex h-9 items-center gap-2 rounded-md border border-zinc-800 bg-zinc-900 px-3 text-sm text-zinc-200 hover:border-zinc-700"
onClick={logout}
type="button"
>
<LogOut className="h-4 w-4" />
Logout
</button>
<nav className="space-y-1 px-4 py-6">
<NavButton active={view === 'nodes' && !currentVm} icon={Network} label="Nodes" onClick={() => switchView('nodes')} />
<NavButton active={view === 'repositories' && !currentVm} icon={Database} label="Repositories" onClick={() => switchView('repositories')} />
<NavButton active={view === 'dashboard' || Boolean(currentVm)} icon={CalendarClock} label="Backups" onClick={() => switchView('dashboard')} />
<NavButton active={view === 'operations' && !currentVm} icon={ClipboardList} label="Operations" onClick={() => switchView('operations')} />
<NavButton active={view === 'scheduler' && !currentVm} icon={Shield} label="Scheduler" onClick={() => switchView('scheduler')} />
<NavButton active={view === 'settings' && !currentVm} icon={SettingsIcon} label="Settings" onClick={() => switchView('settings')} />
</nav>
<div className="mt-auto hidden border-t border-zinc-800 px-6 py-4 text-xs text-zinc-500 lg:block">
<div>v0.1.0</div>
<div className="mt-2 text-red-400">Management</div>
</div>
</div>
</header>
</aside>
{error ? (
<div className="mx-auto mt-4 flex max-w-7xl items-center gap-2 px-4 text-sm text-amber-300 sm:px-6">
<AlertTriangle className="h-4 w-4" />
{error}
</div>
) : null}
<section className="min-w-0">
<header className="flex h-20 items-center justify-between border-b border-zinc-800 bg-zinc-900/80 px-6">
<div className="flex items-center gap-3">
<button className="hidden h-9 w-9 items-center justify-center rounded-md border border-zinc-800 text-zinc-400 lg:inline-flex" type="button">
<DatabaseBackup className="h-4 w-4" />
</button>
<h1 className="text-lg font-semibold tracking-wide text-zinc-100">{pageTitle}</h1>
</div>
<div className="flex items-center gap-2 rounded-full border border-zinc-800 bg-zinc-950 px-3 py-2">
<span className="px-2 text-sm font-medium text-zinc-200">{session.user?.username || 'admin'}</span>
<button className="flex h-7 w-7 items-center justify-center rounded-full text-zinc-400 hover:text-zinc-100" onClick={refresh} title="Refresh" type="button">
<RefreshCw className="h-4 w-4" />
</button>
<button className="flex h-7 w-7 items-center justify-center rounded-full text-zinc-400 hover:text-zinc-100" title="Theme" type="button">
<Moon className="h-4 w-4" />
</button>
<button className="flex h-7 w-7 items-center justify-center rounded-full text-zinc-400 hover:text-zinc-100" onClick={logout} title="Logout" type="button">
<LogOut className="h-4 w-4" />
</button>
</div>
</header>
<div className="mx-auto max-w-7xl px-4 py-6 sm:px-6">
{view === 'nodes' ? (
<Nodes nodes={nodes} onChanged={refresh} />
) : view === 'operations' ? (
<Operations />
) : view === 'settings' ? (
<Settings nodes={nodes} onChanged={refresh} />
) : view === 'scheduler' ? (
<Scheduler onChanged={refresh} schedules={schedules} vms={vms} />
) : currentVm ? (
<VMDetail vm={currentVm} jobs={jobs} onBack={() => setSelectedVm(null)} onChanged={refresh} />
) : (
<Dashboard
health={health}
jobs={jobs}
loading={loading}
onManage={(vmName) => {
setSelectedVm(vmName);
setView('dashboard');
}}
vms={vms}
/>
)}
<div className="app-grid-bg min-h-[calc(100vh-5rem)] px-5 py-7 sm:px-8">
{error ? (
<div className="mb-5 flex items-center gap-2 text-sm text-amber-300">
<AlertTriangle className="h-4 w-4" />
{error}
</div>
) : null}
<div className="mx-auto max-w-[1500px]">
{view === 'nodes' ? (
<Nodes nodes={nodes} onChanged={refresh} />
) : view === 'repositories' ? (
<Repositories nodes={nodes} onChanged={refresh} />
) : view === 'operations' ? (
<Operations />
) : view === 'settings' ? (
<Settings nodes={nodes} onChanged={refresh} />
) : view === 'scheduler' ? (
<Scheduler onChanged={refresh} schedules={schedules} vms={vms} />
) : currentVm ? (
<VMDetail vm={currentVm} jobs={jobs} onBack={() => setSelectedVm(null)} onChanged={refresh} />
) : (
<Dashboard
health={health}
jobs={jobs}
loading={loading}
onCreateSchedule={() => switchView('scheduler')}
onManage={(vmName) => {
setSelectedVm(vmName);
setView('dashboard');
}}
schedules={schedules}
vms={vms}
/>
)}
</div>
</div>
</section>
</div>
</main>
);
function switchView(nextView) {
setSelectedVm(null);
setView(nextView);
}
}
function NavButton({ active, icon: Icon, label, onClick }) {
return (
<button
className={`flex h-11 w-full items-center gap-3 rounded-md px-3 text-left text-sm font-semibold transition ${
active
? 'border-l-4 border-red-500 bg-red-950/40 text-zinc-100'
: 'text-zinc-400 hover:bg-zinc-800/70 hover:text-zinc-100'
}`}
onClick={onClick}
type="button"
>
<Icon className={`h-4 w-4 ${active ? 'text-red-400' : 'text-zinc-500'}`} />
{label}
</button>
);
}