+ {
+ setChannelFilter("needs_full");
+ setPage("channels");
+ }}
+ />
+ openReleaseNotes(CURRENT_VERSION)} />
+
{settingsOpen && (
+ {page === "feed" && (
+
+ )}
+
{page === "channels" ? (
setWizardOpen(true)}
/>
)}
-
+
+
-
-
-
-
- {open && (
-
- );
-}
diff --git a/frontend/src/components/NavSidebar.tsx b/frontend/src/components/NavSidebar.tsx
new file mode 100644
index 0000000..1ea7c09
--- /dev/null
+++ b/frontend/src/components/NavSidebar.tsx
@@ -0,0 +1,187 @@
+import { useState } from "react";
+import { useTranslation } from "react-i18next";
+import {
+ BarChart3,
+ ChevronLeft,
+ ChevronRight,
+ Home,
+ Info,
+ ListVideo,
+ LogOut,
+ Settings,
+ Shield,
+ Tv,
+} from "lucide-react";
+import type { Me } from "../lib/api";
+import type { Page } from "../lib/urlState";
+import AvatarImg from "./Avatar";
+
+// Primary app navigation: a collapsible left rail with icon+label entries (Design C). The
+// modules used to hide under the avatar dropdown; they now live here. Collapsed, it becomes
+// a thin icon-only rail. Account actions sit at the bottom in a popover.
+export default function NavSidebar({
+ me,
+ page,
+ setPage,
+ onOpenSettings,
+ onOpenAbout,
+}: {
+ me: Me;
+ page: Page;
+ setPage: (p: Page) => void;
+ onOpenSettings: () => void;
+ onOpenAbout: () => void;
+}) {
+ const { t } = useTranslation();
+ const [collapsed, setCollapsed] = useState
-
- )}
-
-
-
-
- {me.role === "admin" && (
-
-
-
- {me.display_name ?? me.email.split("@")[0]}
-
- {me.email}
-
-
- {t("header.account.admin")}
-
- )}
-
-
- {page !== "feed" && (
-
- )}
- {page !== "channels" && (
-
- )}
- {page !== "playlists" && (
-
- )}
- {me.role === "admin" && page !== "stats" && (
-
- )}
-
-
-
-
-