Fix: Adminbereich responsiv (Mobile-Drawer-Navigation, scrollbare Tabellen)

This commit is contained in:
2026-07-19 11:20:33 +02:00
parent 986de50963
commit d65897b8e1
6 changed files with 147 additions and 90 deletions

View File

@@ -1,3 +1,4 @@
import { useEffect, useState } from "react";
import { Link, Outlet, useRouterState } from "@tanstack/react-router"; import { Link, Outlet, useRouterState } from "@tanstack/react-router";
const NAV_ITEMS = [ const NAV_ITEMS = [
@@ -13,17 +14,65 @@ const NAV_ITEMS = [
export function AdminLayout() { export function AdminLayout() {
const pathname = useRouterState({ select: (s) => s.location.pathname }); const pathname = useRouterState({ select: (s) => s.location.pathname });
const [mobileOpen, setMobileOpen] = useState(false);
// Drawer schließen, wenn per Navigation die Seite wechselt
useEffect(() => {
setMobileOpen(false);
}, [pathname]);
return ( return (
<div className="flex min-h-screen bg-neutral-50 dark:bg-neutral-950"> <div className="min-h-screen bg-neutral-50 dark:bg-neutral-950 sm:flex">
<aside className="flex w-56 shrink-0 flex-col border-r border-black/10 bg-white/70 dark:border-white/10 dark:bg-white/5"> {/* Mobile-Kopfleiste mit Hamburger-Button, nur unterhalb des sm-Breakpoints sichtbar */}
<div className="flex items-center gap-2 border-b border-black/10 px-5 py-4 dark:border-white/10"> <div
<Link to="/" className="text-lg font-semibold text-black dark:text-white"> className="flex items-center gap-3 border-b border-black/10 bg-white/80 px-4 py-3
backdrop-blur-md dark:border-white/10 dark:bg-neutral-950/80 sm:hidden"
>
<button
onClick={() => setMobileOpen(true)}
aria-label="Menü öffnen"
className="rounded-lg p-1.5 text-black/70 hover:bg-black/5 dark:text-white/70 dark:hover:bg-white/10"
>
<span className="block text-xl leading-none" aria-hidden>
</span>
</button>
<Link to="/" className="text-base font-semibold text-black dark:text-white">
LaunchPad LaunchPad
</Link> </Link>
</div> </div>
<nav className="flex flex-1 flex-col gap-0.5 p-3"> {/* Abdunkelungs-Overlay hinter der aufgeklappten Drawer-Navigation (nur mobil) */}
{mobileOpen ? (
<div
onClick={() => setMobileOpen(false)}
className="fixed inset-0 z-30 bg-black/40 sm:hidden"
aria-hidden
/>
) : null}
<aside
className={`fixed inset-y-0 left-0 z-40 flex w-64 flex-col border-r border-black/10
bg-white transition-transform duration-200 ease-out dark:border-white/10
dark:bg-neutral-950 sm:static sm:z-auto sm:w-56 sm:shrink-0 sm:translate-x-0
sm:bg-white/70 sm:transition-none dark:sm:bg-white/5 ${
mobileOpen ? "translate-x-0" : "-translate-x-full"
}`}
>
<div className="flex items-center justify-between gap-2 border-b border-black/10 px-5 py-4 dark:border-white/10">
<Link to="/" className="text-lg font-semibold text-black dark:text-white">
LaunchPad
</Link>
<button
onClick={() => setMobileOpen(false)}
aria-label="Menü schließen"
className="rounded-lg p-1 text-black/50 hover:bg-black/5 dark:text-white/50 dark:hover:bg-white/10 sm:hidden"
>
</button>
</div>
<nav className="flex flex-1 flex-col gap-0.5 overflow-y-auto p-3">
{NAV_ITEMS.map((item) => { {NAV_ITEMS.map((item) => {
const active = pathname.startsWith(item.to); const active = pathname.startsWith(item.to);
return ( return (
@@ -56,7 +105,7 @@ export function AdminLayout() {
</div> </div>
</aside> </aside>
<main className="flex-1 overflow-y-auto p-8"> <main className="flex-1 overflow-x-hidden overflow-y-auto p-4 sm:p-8">
<Outlet /> <Outlet />
</main> </main>
</div> </div>

View File

@@ -196,7 +196,7 @@ export function CategoriesPage() {
description="Per Drag & Drop sortieren. Favoriten erscheinen in der Suche trotzdem immer zuerst." description="Per Drag & Drop sortieren. Favoriten erscheinen in der Suche trotzdem immer zuerst."
/> />
<form onSubmit={handleSubmit} className="mb-6 flex items-end gap-2"> <form onSubmit={handleSubmit} className="mb-6 flex flex-wrap items-end gap-2">
<div> <div>
<label className="mb-1 block text-xs font-medium text-black/50 dark:text-white/50"> <label className="mb-1 block text-xs font-medium text-black/50 dark:text-white/50">
Neue Kategorie Neue Kategorie

View File

@@ -49,6 +49,7 @@ export function DashboardPage() {
</p> </p>
) : ( ) : (
<div className="overflow-hidden rounded-2xl border border-black/10 dark:border-white/10"> <div className="overflow-hidden rounded-2xl border border-black/10 dark:border-white/10">
<div className="overflow-x-auto">
<table className="w-full text-sm"> <table className="w-full text-sm">
<tbody> <tbody>
{recentlyScanned.map((device) => ( {recentlyScanned.map((device) => (
@@ -56,11 +57,11 @@ export function DashboardPage() {
key={device.id} key={device.id}
className="border-b border-black/5 last:border-0 dark:border-white/5" 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"> <td className="whitespace-nowrap px-4 py-3 font-medium text-black dark:text-white">
{device.hostname} {device.hostname}
</td> </td>
<td className="px-4 py-3 text-black/40 dark:text-white/40">{device.ip}</td> <td className="whitespace-nowrap 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"> <td className="whitespace-nowrap px-4 py-3 text-black/40 dark:text-white/40">
{device.lastScan ? new Date(device.lastScan).toLocaleString("de-DE") : ""} {device.lastScan ? new Date(device.lastScan).toLocaleString("de-DE") : ""}
</td> </td>
</tr> </tr>
@@ -68,6 +69,7 @@ export function DashboardPage() {
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
)} )}
</div> </div>

View File

@@ -134,7 +134,7 @@ function AddDeviceForm() {
} }
return ( return (
<form onSubmit={handleSubmit} className="flex items-end gap-2"> <form onSubmit={handleSubmit} className="flex flex-wrap items-end gap-2">
<div> <div>
<label className="mb-1 block text-xs font-medium text-black/50 dark:text-white/50"> <label className="mb-1 block text-xs font-medium text-black/50 dark:text-white/50">
Hostname Hostname
@@ -191,7 +191,8 @@ export function DevicesPage() {
<p className="text-sm text-red-500">Geräte konnten nicht geladen werden.</p> <p className="text-sm text-red-500">Geräte konnten nicht geladen werden.</p>
) : devices && devices.length > 0 ? ( ) : devices && devices.length > 0 ? (
<div className="overflow-hidden rounded-2xl border border-black/10 dark:border-white/10"> <div className="overflow-hidden rounded-2xl border border-black/10 dark:border-white/10">
<table className="w-full text-sm"> <div className="overflow-x-auto">
<table className="w-full min-w-[640px] text-sm">
<thead> <thead>
<tr className="border-b border-black/10 bg-black/[0.02] text-left text-xs <tr className="border-b border-black/10 bg-black/[0.02] text-left text-xs
uppercase tracking-wide text-black/40 dark:border-white/10 dark:bg-white/5 dark:text-white/40"> uppercase tracking-wide text-black/40 dark:border-white/10 dark:bg-white/5 dark:text-white/40">
@@ -209,6 +210,7 @@ export function DevicesPage() {
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
) : ( ) : (
<p className="text-sm text-black/40 dark:text-white/40"> <p className="text-sm text-black/40 dark:text-white/40">
Noch keine Geräte angelegt. Füge oben ein Gerät hinzu oder nutze den FritzBox-Scan Noch keine Geräte angelegt. Füge oben ein Gerät hinzu oder nutze den FritzBox-Scan

View File

@@ -17,7 +17,8 @@ export function LogsPage() {
<p className="text-sm text-red-500">Logs konnten nicht geladen werden.</p> <p className="text-sm text-red-500">Logs konnten nicht geladen werden.</p>
) : logs && logs.length > 0 ? ( ) : logs && logs.length > 0 ? (
<div className="overflow-hidden rounded-2xl border border-black/10 dark:border-white/10"> <div className="overflow-hidden rounded-2xl border border-black/10 dark:border-white/10">
<table className="w-full text-sm"> <div className="overflow-x-auto">
<table className="w-full min-w-[560px] text-sm">
<thead> <thead>
<tr className="border-b border-black/10 bg-black/[0.02] text-left text-xs <tr className="border-b border-black/10 bg-black/[0.02] text-left text-xs
uppercase tracking-wide text-black/40 dark:border-white/10 dark:bg-white/5 dark:text-white/40"> uppercase tracking-wide text-black/40 dark:border-white/10 dark:bg-white/5 dark:text-white/40">
@@ -32,7 +33,7 @@ export function LogsPage() {
<td className="whitespace-nowrap px-4 py-3 text-xs text-black/40 dark:text-white/40"> <td className="whitespace-nowrap px-4 py-3 text-xs text-black/40 dark:text-white/40">
{new Date(log.createdAt).toLocaleString("de-DE")} {new Date(log.createdAt).toLocaleString("de-DE")}
</td> </td>
<td className="px-4 py-3"> <td className="whitespace-nowrap px-4 py-3">
<span <span
className={`inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-medium ${ className={`inline-flex items-center gap-1.5 rounded-full px-2 py-0.5 text-xs font-medium ${
log.level === "error" log.level === "error"
@@ -49,6 +50,7 @@ export function LogsPage() {
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
) : ( ) : (
<p className="text-sm text-black/40 dark:text-white/40"> <p className="text-sm text-black/40 dark:text-white/40">
Noch keine Scans durchgeführt. Starte einen Scan unter Geräte" oder „Scanner". Noch keine Scans durchgeführt. Starte einen Scan unter Geräte" oder „Scanner".

View File

@@ -188,7 +188,8 @@ export function ServicesPage() {
<p className="text-sm text-red-500">Dienste konnten nicht geladen werden.</p> <p className="text-sm text-red-500">Dienste konnten nicht geladen werden.</p>
) : services && services.length > 0 ? ( ) : services && services.length > 0 ? (
<div className="overflow-hidden rounded-2xl border border-black/10 dark:border-white/10"> <div className="overflow-hidden rounded-2xl border border-black/10 dark:border-white/10">
<table className="w-full text-sm"> <div className="overflow-x-auto">
<table className="w-full min-w-[720px] text-sm">
<thead> <thead>
<tr className="border-b border-black/10 bg-black/[0.02] text-left text-xs <tr className="border-b border-black/10 bg-black/[0.02] text-left text-xs
uppercase tracking-wide text-black/40 dark:border-white/10 dark:bg-white/5 dark:text-white/40"> uppercase tracking-wide text-black/40 dark:border-white/10 dark:bg-white/5 dark:text-white/40">
@@ -207,6 +208,7 @@ export function ServicesPage() {
</tbody> </tbody>
</table> </table>
</div> </div>
</div>
) : ( ) : (
<p className="text-sm text-black/40 dark:text-white/40"> <p className="text-sm text-black/40 dark:text-white/40">
Noch keine Dienste vorhanden. Scanne ein Gerät unter Geräte, um automatisch welche Noch keine Dienste vorhanden. Scanne ein Gerät unter Geräte, um automatisch welche