Commit 6: Adminbereich (TanStack Router, 8 Menüpunkte, Scan-Logs)

This commit is contained in:
2026-07-19 02:53:04 +02:00
parent b0ff551ca0
commit 2f31996bbb
31 changed files with 1693 additions and 112 deletions

View File

@@ -0,0 +1,84 @@
import { useServices } from "../../hooks/useServices.js";
import { useDevices } from "../../hooks/useDevices.js";
import { useCategories } from "../../hooks/useCategories.js";
import { AdminPageHeader } from "./AdminPageHeader.js";
function StatCard({ label, value }: { label: string; value: number | string }) {
return (
<div className="rounded-2xl border border-black/10 bg-white p-5 dark:border-white/10 dark:bg-white/5">
<div className="text-3xl font-semibold text-black dark:text-white">{value}</div>
<div className="mt-1 text-sm text-black/50 dark:text-white/50">{label}</div>
</div>
);
}
export function DashboardPage() {
const { data: services } = useServices();
const { data: devices } = useDevices();
const { data: categories } = useCategories();
const onlineDevices = devices?.filter((d) => d.online).length ?? 0;
const favoriteServices = services?.filter((s) => s.favorite).length ?? 0;
const recentlyScanned = [...(devices ?? [])]
.filter((d) => d.lastScan)
.sort((a, b) => (b.lastScan ?? "").localeCompare(a.lastScan ?? ""))
.slice(0, 5);
return (
<div>
<AdminPageHeader
title="Dashboard"
description="Überblick über dein Homelab."
/>
<div className="grid grid-cols-2 gap-4 sm:grid-cols-4">
<StatCard label="Geräte" value={devices?.length ?? 0} />
<StatCard label="davon online" value={onlineDevices} />
<StatCard label="Dienste" value={services?.length ?? 0} />
<StatCard label="Favoriten" value={favoriteServices} />
</div>
<div className="mt-8">
<h2 className="mb-3 text-sm font-semibold text-black/60 dark:text-white/60">
Zuletzt gescannte Geräte
</h2>
{recentlyScanned.length === 0 ? (
<p className="text-sm text-black/40 dark:text-white/40">
Noch keine Scans durchgeführt. Starte einen Scan unter Geräte oder Scanner.
</p>
) : (
<div className="overflow-hidden rounded-2xl border border-black/10 dark:border-white/10">
<table className="w-full text-sm">
<tbody>
{recentlyScanned.map((device) => (
<tr
key={device.id}
className="border-b border-black/5 last:border-0 dark:border-white/5"
>
<td className="px-4 py-3 font-medium text-black dark:text-white">
{device.hostname}
</td>
<td className="px-4 py-3 text-black/40 dark:text-white/40">{device.ip}</td>
<td className="px-4 py-3 text-black/40 dark:text-white/40">
{device.lastScan ? new Date(device.lastScan).toLocaleString("de-DE") : ""}
</td>
</tr>
))}
</tbody>
</table>
</div>
)}
</div>
<div className="mt-8">
<h2 className="mb-3 text-sm font-semibold text-black/60 dark:text-white/60">
Kategorien
</h2>
<p className="text-sm text-black/40 dark:text-white/40">
{categories?.length ?? 0} Kategorie(n) angelegt.
</p>
</div>
</div>
);
}