diff --git a/CHANGELOG.md b/CHANGELOG.md index 5cb6b72..6c375fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,15 @@ und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/). ### Sicherheit +## [0.20.2] – 2026-10-03 + +### Behoben +- **Das Seitenmenü ließ sich auf dem Handy nicht schließen.** Es gab weder eine Fläche zum Antippen noch ein Schließen beim Navigieren – das Markup stammt von AdminLTE, dessen JavaScript wir bewusst nicht einbinden (#034) +- Die Dashboard-Tabelle war auf dem Telefon rechts abgeschnitten; sie zeigt dort jetzt Host und Ablauf, den Kunden klein darunter (#034) + +### Hinzugefügt +- Schließen-Knopf im Menükopf, Schließen per Escape, und der Inhalt hinter dem offenen Menü scrollt nicht mehr mit (#034) + ## [0.20.1] – 2026-10-03 ### Behoben diff --git a/DEVLOG.md b/DEVLOG.md index 0948c73..abd0a3c 100644 --- a/DEVLOG.md +++ b/DEVLOG.md @@ -2,7 +2,7 @@ ## Zeitübersicht -**Gesamt:** 16 h 58 min +**Gesamt:** 17 h 38 min | Issue | Zeit | |---|---| @@ -39,6 +39,7 @@ | #031 Update für beide Varianten | 0 h 45 min | | #032 LXC-Installation korrigiert | 0 h 20 min | | #033 Weiterleitung auf Lausch-Adresse | 0 h 30 min | +| #034 Bedienung auf dem Handy | 0 h 40 min | | Meilenstein | Zeit | |---|---| @@ -61,7 +62,7 @@ ## 2026-09-17 · Sitzung 1 · 19:36–laufend **Issue:** #001–#007 (Meilensteine v0.1.0 bis v0.4.0) -**Version:** 0.20.1 +**Version:** 0.20.2 ### Erledigt - Auftrag gelesen, Rückfragen gestellt @@ -179,6 +180,9 @@ - #033 war hausgemacht: Mein `HOSTNAME=0.0.0.0` aus #031 hat den Healthcheck repariert und dafür alle Weiterleitungen aus Server Actions auf `0.0.0.0` gelenkt. Vorher stand dort der Containername – derselbe Fehler, nur unauffälliger - Lehre: Weiterleitungen gehören aus der konfigurierten Adresse gebaut, nie aus dem, was der Server über sich selbst weiß +- Mobile Bedienung (#034): Das Menü ließ sich auf dem Telefon nicht schließen – AdminLTE-Markup ohne AdminLTE-JavaScript. Eigener Zustand, abdunkelnde Fläche, Schließen beim Navigieren und mit Escape +- Lehre: Die Mobilansicht war mit Screenshots geprüft, nicht mit den Fingern. Ein Bild zeigt nicht, dass etwas nicht wieder weggeht + ### Nächster Schritt - #016 Benutzerhandbuch mit Screenshots, danach #017 Favicons und #019 CI-Pipeline - Für 1.0.0 fehlt noch: PMG (kein Host verfügbar) und der Fall „aktive Subscription auf PBS“ (#023) diff --git a/docs/anleitung/README.md b/docs/anleitung/README.md index de21064..f2885dd 100644 --- a/docs/anleitung/README.md +++ b/docs/anleitung/README.md @@ -473,6 +473,11 @@ Server-ID stehen in der Detailansicht unter *Subscription-Historie*. ## Anhang: dunkles Design und Mobilansicht +Auf dem Telefon öffnet das Symbol links oben das Menü; es schließt sich durch Antippen der +abgedunkelten Fläche, über das Kreuz im Menükopf, mit Escape – und von selbst, sobald ein Eintrag +gewählt wurde. Die Tabellen zeigen auf schmalen Bildschirmen nur die wichtigsten Spalten; die +übrigen Angaben stehen auf der jeweiligen Detailseite. + Das Dashboard folgt der Einstellung des Betriebssystems und lässt sich über das Mondsymbol in der Kopfleiste umschalten. diff --git a/docs/anleitung/bilder/audit.png b/docs/anleitung/bilder/audit.png index 4d07f1f..109509c 100644 Binary files a/docs/anleitung/bilder/audit.png and b/docs/anleitung/bilder/audit.png differ diff --git a/docs/anleitung/bilder/benachrichtigungen.png b/docs/anleitung/bilder/benachrichtigungen.png index 20df2c1..f5be048 100644 Binary files a/docs/anleitung/bilder/benachrichtigungen.png and b/docs/anleitung/bilder/benachrichtigungen.png differ diff --git a/docs/anleitung/bilder/benutzer.png b/docs/anleitung/bilder/benutzer.png index d4ae55f..d92e958 100644 Binary files a/docs/anleitung/bilder/benutzer.png and b/docs/anleitung/bilder/benutzer.png differ diff --git a/docs/anleitung/bilder/dashboard-dunkel.png b/docs/anleitung/bilder/dashboard-dunkel.png index eecbe44..0c434ac 100644 Binary files a/docs/anleitung/bilder/dashboard-dunkel.png and b/docs/anleitung/bilder/dashboard-dunkel.png differ diff --git a/docs/anleitung/bilder/dashboard.png b/docs/anleitung/bilder/dashboard.png index 94b55a1..0c434ac 100644 Binary files a/docs/anleitung/bilder/dashboard.png and b/docs/anleitung/bilder/dashboard.png differ diff --git a/docs/anleitung/bilder/host-detail.png b/docs/anleitung/bilder/host-detail.png index 11ea2ca..b3cf4f9 100644 Binary files a/docs/anleitung/bilder/host-detail.png and b/docs/anleitung/bilder/host-detail.png differ diff --git a/docs/anleitung/bilder/hosts-dunkel.png b/docs/anleitung/bilder/hosts-dunkel.png index bea3d23..4281f89 100644 Binary files a/docs/anleitung/bilder/hosts-dunkel.png and b/docs/anleitung/bilder/hosts-dunkel.png differ diff --git a/docs/anleitung/bilder/hosts.png b/docs/anleitung/bilder/hosts.png index 5061f71..4281f89 100644 Binary files a/docs/anleitung/bilder/hosts.png and b/docs/anleitung/bilder/hosts.png differ diff --git a/docs/anleitung/bilder/jahresbahn.png b/docs/anleitung/bilder/jahresbahn.png index d937539..9ce7f6d 100644 Binary files a/docs/anleitung/bilder/jahresbahn.png and b/docs/anleitung/bilder/jahresbahn.png differ diff --git a/docs/anleitung/bilder/kunden.png b/docs/anleitung/bilder/kunden.png index 8715931..5acaa5d 100644 Binary files a/docs/anleitung/bilder/kunden.png and b/docs/anleitung/bilder/kunden.png differ diff --git a/docs/anleitung/bilder/login.png b/docs/anleitung/bilder/login.png index 7304be2..46dbca5 100644 Binary files a/docs/anleitung/bilder/login.png and b/docs/anleitung/bilder/login.png differ diff --git a/docs/anleitung/bilder/mobil-dashboard.png b/docs/anleitung/bilder/mobil-dashboard.png index aaa9a39..b3a3c15 100644 Binary files a/docs/anleitung/bilder/mobil-dashboard.png and b/docs/anleitung/bilder/mobil-dashboard.png differ diff --git a/docs/anleitung/bilder/mobil-hosts.png b/docs/anleitung/bilder/mobil-hosts.png index c288581..7823faf 100644 Binary files a/docs/anleitung/bilder/mobil-hosts.png and b/docs/anleitung/bilder/mobil-hosts.png differ diff --git a/docs/anleitung/bilder/nicht-zugeordnet.png b/docs/anleitung/bilder/nicht-zugeordnet.png index 8319785..7350f3c 100644 Binary files a/docs/anleitung/bilder/nicht-zugeordnet.png and b/docs/anleitung/bilder/nicht-zugeordnet.png differ diff --git a/docs/anleitung/bilder/passkeys.png b/docs/anleitung/bilder/passkeys.png index caff728..4be1541 100644 Binary files a/docs/anleitung/bilder/passkeys.png and b/docs/anleitung/bilder/passkeys.png differ diff --git a/docs/anleitung/bilder/rechnungen-dunkel.png b/docs/anleitung/bilder/rechnungen-dunkel.png index 848f13b..cea5491 100644 Binary files a/docs/anleitung/bilder/rechnungen-dunkel.png and b/docs/anleitung/bilder/rechnungen-dunkel.png differ diff --git a/docs/anleitung/bilder/rechnungen.png b/docs/anleitung/bilder/rechnungen.png index 8416164..cea5491 100644 Binary files a/docs/anleitung/bilder/rechnungen.png and b/docs/anleitung/bilder/rechnungen.png differ diff --git a/docs/anleitung/bilder/system.png b/docs/anleitung/bilder/system.png index cfe41ab..d823aa9 100644 Binary files a/docs/anleitung/bilder/system.png and b/docs/anleitung/bilder/system.png differ diff --git a/docs/anleitung/bilder/tokens.png b/docs/anleitung/bilder/tokens.png index 9bcf910..974555d 100644 Binary files a/docs/anleitung/bilder/tokens.png and b/docs/anleitung/bilder/tokens.png differ diff --git a/docs/anleitung/bilder/warnregeln.png b/docs/anleitung/bilder/warnregeln.png index b8cb1c1..08d1c3e 100644 Binary files a/docs/anleitung/bilder/warnregeln.png and b/docs/anleitung/bilder/warnregeln.png differ diff --git a/docs/anleitung/bilder/warnungen-dunkel.png b/docs/anleitung/bilder/warnungen-dunkel.png index 82e7662..82a6d94 100644 Binary files a/docs/anleitung/bilder/warnungen-dunkel.png and b/docs/anleitung/bilder/warnungen-dunkel.png differ diff --git a/docs/anleitung/bilder/warnungen.png b/docs/anleitung/bilder/warnungen.png index 6ce2938..82a6d94 100644 Binary files a/docs/anleitung/bilder/warnungen.png and b/docs/anleitung/bilder/warnungen.png differ diff --git a/docs/issues/034-mobile-bedienung.md b/docs/issues/034-mobile-bedienung.md new file mode 100644 index 0000000..f426519 --- /dev/null +++ b/docs/issues/034-mobile-bedienung.md @@ -0,0 +1,28 @@ +# #034 Bedienung auf dem Handy +**Status:** erledigt +**Meilenstein:** v0.20.2 +**Labels:** ui, bug +**Geschätzt:** 2 h **Tatsächlich:** 0 h 40 min + +## Auslöser +Florian im produktiven Einsatz auf dem Telefon: „Das Menü geht nicht weg.“ Dazu eine Tabelle, von der +rechts die Hälfte abgeschnitten war. + +## Ursache +Das Markup stammt von AdminLTE, dessen JavaScript binden wir bewusst nicht ein. Die Klasse +`sidebar-open` ließ sich damit setzen – aber nichts nahm sie wieder weg: keine Fläche zum Antippen, +kein Schließen beim Navigieren, kein Escape. Auf dem Telefon blieb das Menü über dem Inhalt stehen. + +## Behebung +- Eigener, winziger Zustand (`menuZustand.ts`), auf den Kopfzeile und Menü gemeinsam hören +- Abdunkelnde Fläche hinter dem Menü – antippen schließt (ein `button`, damit auch per Tastatur erreichbar) +- Schließen-Knopf im Menükopf, nur auf schmalen Bildschirmen +- Schließt beim Navigieren und mit Escape +- Der Inhalt dahinter scrollt nicht mehr mit, solange das Menü offen ist +- Dashboard-Tabelle: auf dem Telefon nur Host und Ablauf, der Kunde steht klein unter dem Hostnamen. + Key und Rechnungsstände erscheinen ab Tablet-Breite – die Host-Tabelle machte das schon vorher so. +- Fünf Tests für die Zustandslogik + +## Nicht geändert +Die abdunkelnde Fläche gilt nur unter 992 px. Auf breiten Bildschirmen bleibt es beim bisherigen +Verhalten: Das Menü klappt zu Symbolen zusammen, statt sich über den Inhalt zu legen. diff --git a/docs/issues/README.md b/docs/issues/README.md index 631bcec..0e7a3d6 100644 --- a/docs/issues/README.md +++ b/docs/issues/README.md @@ -37,4 +37,5 @@ Details und Architektur: [../architektur.md](../architektur.md). Zeiten werden a | [#031](031-update-skript-beide-varianten.md) | Update- und Sicherungsskript für beide Varianten | erledigt | v0.20.0 | 2 h | 0 h 45 min | | [#032](032-lxc-installation-korrekturen.md) | LXC-Installation: Herkunft der Skripte und Speicher | erledigt | v0.20.1 | 1 h | 0 h 20 min | | [#033](033-weiterleitung-lauschadresse.md) | Abmelden führte auf die Lausch-Adresse | erledigt | v0.20.1 | 1 h | 0 h 30 min | -| | **Summe** | | | **151 h** | **16 h 58 min** | +| [#034](034-mobile-bedienung.md) | Bedienung auf dem Handy | erledigt | v0.20.2 | 2 h | 0 h 40 min | +| | **Summe** | | | **153 h** | **17 h 38 min** | diff --git a/package.json b/package.json index 98b2a49..beca736 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "proxledger", - "version": "0.20.1", + "version": "0.20.2", "private": true, "scripts": { "dev": "next dev", diff --git a/src/app/(dashboard)/page.tsx b/src/app/(dashboard)/page.tsx index 7dfff78..7f344a6 100644 --- a/src/app/(dashboard)/page.tsx +++ b/src/app/(dashboard)/page.tsx @@ -121,12 +121,22 @@ export default async function DashboardSeite() {
| Host | -Kunde | -Key | ++ Kunde + | ++ Key + | Ablauf | -Proxmox | -Kundenrechnung | ++ Proxmox + | ++ Kundenrechnung + | + | {host.kunde ? ( {host.kunde.name} @@ -152,16 +166,16 @@ export default async function DashboardSeite() { )} | -+ |
|
|
- + |
|
- + |
|
diff --git a/src/app/globals.css b/src/app/globals.css
index 27b6b80..e587ac5 100644
--- a/src/app/globals.css
+++ b/src/app/globals.css
@@ -186,6 +186,19 @@ body {
display: none;
}
+/*
+ * Fläche hinter dem geöffneten Menü auf schmalen Bildschirmen. Ein Knopf statt eines div,
+ * damit sie auch per Tastatur erreichbar ist.
+ */
+.pl-menu-hintergrund {
+ position: fixed;
+ inset: 0;
+ z-index: 1037; /* unter dem Menü (1038), über allem anderen */
+ padding: 0;
+ background-color: rgba(0, 0, 0, 0.5);
+ border: 0;
+}
+
/* ---------- Inhaltsbereich ---------- */
.app-content-header {
diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx
index df7c3b4..99a895c 100644
--- a/src/components/layout/Header.tsx
+++ b/src/components/layout/Header.tsx
@@ -5,6 +5,7 @@ import { useCallback, useEffect, useState, useSyncExternalStore } from "react";
import { SearchDialog, type SuchEintrag } from "./SearchDialog";
import type { Rolle } from "@/generated/prisma/enums";
import { ROLLEN_TEXT } from "@/server/auth/rollen";
+import { menuUmschalten } from "./menuZustand";
export interface KopfleistenAlert {
id: string;
@@ -58,16 +59,9 @@ export function Header({
return () => window.removeEventListener("keydown", tasten);
}, []);
- /** Sidebar umschalten: auf schmalen Bildschirmen als Overlay, sonst eingeklappt. */
+ /** Seitenmenü umschalten – der Zustand liegt in menuZustand.ts, damit das Menü selbst ihn kennt. */
const sidebarUmschalten = useCallback(() => {
- const body = document.body;
- if (window.innerWidth < 992) {
- body.classList.toggle("sidebar-open");
- body.classList.remove("sidebar-collapse");
- } else {
- body.classList.toggle("sidebar-collapse");
- body.classList.remove("sidebar-open");
- }
+ menuUmschalten();
}, []);
function themaUmschalten() {
diff --git a/src/components/layout/Sidebar.tsx b/src/components/layout/Sidebar.tsx
index d91fb85..bd6d0d8 100644
--- a/src/components/layout/Sidebar.tsx
+++ b/src/components/layout/Sidebar.tsx
@@ -2,10 +2,11 @@
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
-import { useState } from "react";
+import { useEffect, useState } from "react";
import { LogoMitSchriftzug } from "@/components/Logo";
import type { Rolle } from "@/generated/prisma/enums";
import { darf } from "@/server/auth/rollen";
+import { menuSchliessen, useMenuOffen } from "./menuZustand";
interface MenuEintrag {
href: string;
@@ -30,6 +31,21 @@ export function Sidebar({
const pathname = usePathname();
const router = useRouter();
const [suche, setSuche] = useState("");
+ const menuOffen = useMenuOffen();
+
+ // Nach dem Navigieren hat das Menü seinen Zweck erfüllt – auf dem Handy verdeckt es sonst alles.
+ useEffect(() => {
+ menuSchliessen();
+ }, [pathname]);
+
+ // Escape schließt, wie bei jedem anderen Overlay auch.
+ useEffect(() => {
+ function taste(ereignis: KeyboardEvent) {
+ if (ereignis.key === "Escape") menuSchliessen();
+ }
+ window.addEventListener("keydown", taste);
+ return () => window.removeEventListener("keydown", taste);
+ }, []);
const haupt: MenuEintrag[] = [
{ href: "/", titel: "Dashboard", icon: "bi-speedometer2" },
@@ -105,11 +121,30 @@ export function Sidebar({
}
return (
-
|---|