From c2eb23aa5bf3eb6a02f8d27c93be9faa4c45247a Mon Sep 17 00:00:00 2001 From: m Date: Mon, 27 Apr 2026 15:13:46 +0200 Subject: [PATCH] feat(t-paliad-054): /admin landing page indexing admin sub-pages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `/admin` was 404 — only `/admin/team` existed. Add a browseable index so the admin area has a root, with the existing Team-Verwaltung tile alongside greyed-out roadmap placeholders (Departments, Audit-Log, Email-Templates, Feature-Flags) so admins see what's coming. - internal/handlers/admin_users.go: handleAdminIndexPage serves dist/admin.html. Same RequireAdminFunc gate as /admin/team — non-admins get the standard 302 to /dashboard?forbidden=admin. - internal/handlers/handlers.go: register GET /admin under the existing admin-conditional block. - frontend/src/admin.tsx + client/admin.ts: card grid built from the shared .grid + .card landing-page pattern. .admin-card-soon dims the placeholders + adds a "Kommt bald" badge so they read as roadmap, not broken links. - frontend/src/components/Sidebar.tsx: add Admin-Bereich (/admin) above Team-Verwaltung in the existing admin group. Both items live in the same display:none group that sidebar.ts reveals after /api/me confirms global_role='global_admin'. - frontend/src/client/i18n.ts: nav.admin.bereich + admin.title / .heading / .subtitle / .section.{available,planned} / .coming_soon plus per-card title+desc, DE+EN. - frontend/src/styles/global.css: .admin-section-planned spacing, .admin-card-soon dimming, .admin-soon-badge pill. - frontend/build.ts: register the renderAdmin entrypoint and admin.ts client bundle. --- frontend/build.ts | 3 + frontend/src/admin.tsx | 108 ++++++++++++++++++++++++++++ frontend/src/client/admin.ts | 7 ++ frontend/src/client/i18n.ts | 34 +++++++++ frontend/src/components/Sidebar.tsx | 3 +- frontend/src/styles/global.css | 35 +++++++++ internal/handlers/admin_users.go | 9 +++ internal/handlers/handlers.go | 1 + 8 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 frontend/src/admin.tsx create mode 100644 frontend/src/client/admin.ts diff --git a/frontend/build.ts b/frontend/build.ts index fe34d1d..973580b 100644 --- a/frontend/build.ts +++ b/frontend/build.ts @@ -29,6 +29,7 @@ import { renderAgenda } from "./src/agenda"; import { renderOnboarding } from "./src/onboarding"; import { renderChangelog } from "./src/changelog"; import { renderTeam } from "./src/team"; +import { renderAdmin } from "./src/admin"; import { renderAdminTeam } from "./src/admin-team"; import { renderNotFound } from "./src/notfound"; @@ -75,6 +76,7 @@ async function build() { join(import.meta.dir, "src/client/onboarding.ts"), join(import.meta.dir, "src/client/changelog.ts"), join(import.meta.dir, "src/client/team.ts"), + join(import.meta.dir, "src/client/admin.ts"), join(import.meta.dir, "src/client/admin-team.ts"), join(import.meta.dir, "src/client/notfound.ts"), ], @@ -153,6 +155,7 @@ async function build() { await Bun.write(join(DIST, "onboarding.html"), renderOnboarding()); await Bun.write(join(DIST, "changelog.html"), renderChangelog()); await Bun.write(join(DIST, "team.html"), renderTeam()); + await Bun.write(join(DIST, "admin.html"), renderAdmin()); await Bun.write(join(DIST, "admin-team.html"), renderAdminTeam()); await Bun.write(join(DIST, "notfound.html"), renderNotFound()); diff --git a/frontend/src/admin.tsx b/frontend/src/admin.tsx new file mode 100644 index 0000000..6669932 --- /dev/null +++ b/frontend/src/admin.tsx @@ -0,0 +1,108 @@ +import { h } from "./jsx"; +import { Sidebar } from "./components/Sidebar"; +import { BottomNav } from "./components/BottomNav"; +import { Footer } from "./components/Footer"; +import { PWAHead } from "./components/PWAHead"; + +const ICON_USERS = ''; +const ICON_BUILDING = ''; +const ICON_LOG = ''; +const ICON_MAIL = ''; +const ICON_FLAG = ''; + +interface PlannedCard { + icon: string; + i18nTitle: string; + i18nDesc: string; + fallbackTitle: string; + fallbackDesc: string; +} + +const PLANNED: PlannedCard[] = [ + { + icon: ICON_BUILDING, + i18nTitle: "admin.card.departments.title", + i18nDesc: "admin.card.departments.desc", + fallbackTitle: "Departments / Dezernate", + fallbackDesc: "Dezernate anlegen und Mitglieder verwalten.", + }, + { + icon: ICON_LOG, + i18nTitle: "admin.card.audit.title", + i18nDesc: "admin.card.audit.desc", + fallbackTitle: "Audit-Log", + fallbackDesc: "Wer hat wann was geändert? Nachvollziehbarkeit für sicherheitsrelevante Aktionen.", + }, + { + icon: ICON_MAIL, + i18nTitle: "admin.card.email_templates.title", + i18nDesc: "admin.card.email_templates.desc", + fallbackTitle: "Email-Templates", + fallbackDesc: "Vorlagen für Einladungen, Erinnerungen und Benachrichtigungen anpassen.", + }, + { + icon: ICON_FLAG, + i18nTitle: "admin.card.feature_flags.title", + i18nDesc: "admin.card.feature_flags.desc", + fallbackTitle: "Feature-Flags", + fallbackDesc: "Funktionen pro Standort, Dezernat oder Rolle aktivieren.", + }, +]; + +export function renderAdmin(): string { + return "" + ( + + + + + + + + + Admin-Bereich — Paliad + + + + + + +
+
+
+
+

Admin-Bereich

+

+ Werkzeuge zur Verwaltung von Paliad. Nur für Administrator:innen sichtbar. +

+
+ +

Verfügbar

+
+ + + +

Geplant

+
+ {PLANNED.map((c) => ( +
+
+

{c.fallbackTitle}

+

{c.fallbackDesc}

+ Kommt bald +
+ ))} +
+
+
+
+ +