From 2578bf6a099539beeccd56b3e6c0928342baed5a Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sat, 5 Sep 2026 12:50:52 +0200 Subject: [PATCH] Improve HK sidebar and restore nested content scrolling --- src/app/admin/layout.tsx | 2 +- src/components/admin/admin-mobile-wrapper.tsx | 15 ++- src/components/admin/admin-nav-link.tsx | 7 +- src/components/admin/admin-sidebar-nav.tsx | 108 ++++++++++++++++-- src/components/smooth-scroll.tsx | 6 +- src/messages/bg.json | 3 + src/messages/cs.json | 3 + src/messages/da.json | 3 + src/messages/de.json | 3 + src/messages/el.json | 3 + src/messages/en.json | 3 + src/messages/es.json | 3 + src/messages/fr.json | 3 + src/messages/hr.json | 3 + src/messages/hu.json | 3 + src/messages/it.json | 3 + src/messages/nl.json | 3 + src/messages/no.json | 3 + src/messages/pl.json | 3 + src/messages/pt.json | 3 + src/messages/ro.json | 3 + src/messages/ru.json | 3 + src/messages/sk.json | 3 + src/messages/sr.json | 3 + src/messages/sv.json | 3 + src/messages/tr.json | 3 + src/messages/uk.json | 3 + 27 files changed, 184 insertions(+), 20 deletions(-) diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx index 8cc04e21..47c5163f 100644 --- a/src/app/admin/layout.tsx +++ b/src/app/admin/layout.tsx @@ -49,7 +49,7 @@ export default async function AdminLayout({ }>
diff --git a/src/components/admin/admin-mobile-wrapper.tsx b/src/components/admin/admin-mobile-wrapper.tsx index d091414e..03a46e7b 100644 --- a/src/components/admin/admin-mobile-wrapper.tsx +++ b/src/components/admin/admin-mobile-wrapper.tsx @@ -42,11 +42,11 @@ export function AdminMobileWrapper({ return ( {/* Wider than public max-w-7xl (80rem), capped so it never goes full-bleed. */} -
-
+
+
{/* Mobile header */}
{/* Content */} -
{children}
+
+ {children} +
diff --git a/src/components/admin/admin-nav-link.tsx b/src/components/admin/admin-nav-link.tsx index 2f49b84d..3d7348fd 100644 --- a/src/components/admin/admin-nav-link.tsx +++ b/src/components/admin/admin-nav-link.tsx @@ -46,11 +46,12 @@ export function AdminNavLink({ @@ -65,7 +66,7 @@ export function AdminNavLink({ {icon} ) : null} - {children} + {children} ); } diff --git a/src/components/admin/admin-sidebar-nav.tsx b/src/components/admin/admin-sidebar-nav.tsx index 0fda05e0..1cb5829e 100644 --- a/src/components/admin/admin-sidebar-nav.tsx +++ b/src/components/admin/admin-sidebar-nav.tsx @@ -1,6 +1,12 @@ "use client"; -import { ChevronDown, ChevronsDownUp, ChevronsUpDown } from "lucide-react"; +import { + ChevronDown, + ChevronsDownUp, + ChevronsUpDown, + Search, + X, +} from "lucide-react"; import { usePathname } from "next/navigation"; import { useTranslations } from "next-intl"; import { useCallback, useEffect, useMemo, useState } from "react"; @@ -75,6 +81,7 @@ export function AdminSidebarNav({ const pathname = usePathname() ?? ""; const [collapsed, setCollapsed] = useState>({}); const [hydrated, setHydrated] = useState(false); + const [query, setQuery] = useState(""); const allowed = useMemo( () => new Set(allowedPermissions), @@ -94,12 +101,39 @@ export function AdminSidebarNav({ .filter((group) => group.items.length > 0); }, [allowed, isSuperAdmin, navConfig]); + const search = query.trim().toLocaleLowerCase(); + const visibleGroups = useMemo( + () => + groups + .map((group) => ({ + ...group, + items: group.items.filter( + (item) => + !search || + t(group.labelKey).toLocaleLowerCase().includes(search) || + t(item.labelKey).toLocaleLowerCase().includes(search), + ), + })) + .filter((group) => group.items.length > 0), + [groups, search, t], + ); + const allKeys = useMemo(() => groups.map((g) => g.labelKey), [groups]); useEffect(() => { - setCollapsed(readCollapsed()); + const saved = readCollapsed(); + setCollapsed( + Object.fromEntries( + groups.map((group) => [ + group.labelKey, + typeof saved[group.labelKey] === "boolean" + ? saved[group.labelKey] + : true, + ]), + ), + ); setHydrated(true); - }, []); + }, [groups]); // Keep the group that contains the current page open. useEffect(() => { @@ -163,11 +197,50 @@ export function AdminSidebarNav({ className="flex min-h-0 flex-1 flex-col overflow-hidden" aria-label={t("overview")} > +
+
+ + setQuery(event.target.value)} + onKeyDown={(event) => { + if (event.key === "Escape" && query) { + event.stopPropagation(); + setQuery(""); + } + }} + aria-label={t("searchMenu")} + placeholder={t("searchMenu")} + style={{ + paddingLeft: 36, + paddingRight: 36, + backgroundColor: "var(--admin-sidebar-background)", + color: "var(--admin-sidebar-text-readable)", + }} + className="h-10 w-full min-w-0 rounded-lg border border-[var(--admin-border)] text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--admin-accent)]" + /> + {query && ( + + )} +
+