SignalMod / frontend /src /components /Layout.tsx
Ruperth's picture
feat: rebuild Layout with collapsible sidebar topbar toggles and Supabase link
5d39332
Raw
History Blame Contribute Delete
6.07 kB
import { useState } from "react";
import { NavLink, Outlet } from "react-router-dom";
import { useI18n } from "../i18n/I18nContext";
import { useTheme } from "../context/ThemeContext";
import {
DatabaseIcon,
GlobeIcon,
HistoryIcon,
HomeIcon,
HubIcon,
LibraryIcon,
MenuIcon,
MoonIcon,
SearchIcon,
SettingsIcon,
ShieldIcon,
ShortsIcon,
SubsIcon,
SunIcon,
WatchIcon,
YourVideosIcon,
} from "./icons";
const DATABASE_URL =
"https://supabase.com/dashboard/project/kxzxqlhdzkpcanotdbgl/editor/17547?schema=public&sort=created_at%3Adesc";
export function Layout() {
const { t, toggleLocale } = useI18n();
const { theme, toggleTheme } = useTheme();
const [sidebarOpen, setSidebarOpen] = useState(false);
const [collapsed, setCollapsed] = useState(false);
const navLinkClass = ({ isActive }: { isActive: boolean }) =>
isActive ? "nav-item active" : "nav-item";
const closeMobile = () => setSidebarOpen(false);
const sidebarClass = [
"sidebar",
sidebarOpen ? "open" : "",
collapsed ? "collapsed" : "",
]
.filter(Boolean)
.join(" ");
return (
<div className={collapsed ? "app-shell collapsed-shell" : "app-shell"}>
<nav className={sidebarClass}>
<NavLink to="/" className="brand" onClick={closeMobile} aria-label="SignalMod">
<img src="/signalmod_logo.png" alt="SignalMod" className="brand-logo" />
</NavLink>
<div className="sidebar-section">
<NavLink to="/" end className={navLinkClass} onClick={closeMobile} title={t.nav.home}>
<span className="nav-icon"><HomeIcon /></span>
<span className="nav-label">{t.nav.home}</span>
</NavLink>
<button className="nav-item" type="button" disabled title={t.nav.shorts}>
<span className="nav-icon"><ShortsIcon /></span>
<span className="nav-label">{t.nav.shorts}</span>
</button>
<button className="nav-item" type="button" disabled title={t.nav.subscriptions}>
<span className="nav-icon"><SubsIcon /></span>
<span className="nav-label">{t.nav.subscriptions}</span>
</button>
</div>
<div className="sidebar-section">
<button className="nav-item" type="button" title={t.nav.library}>
<span className="nav-icon"><LibraryIcon /></span>
<span className="nav-label">{t.nav.library}</span>
</button>
<button className="nav-item" type="button" title={t.nav.history}>
<span className="nav-icon"><HistoryIcon /></span>
<span className="nav-label">{t.nav.history}</span>
</button>
<button className="nav-item" type="button" title={t.nav.yourVideos}>
<span className="nav-icon"><YourVideosIcon /></span>
<span className="nav-label">{t.nav.yourVideos}</span>
</button>
</div>
<div className="sidebar-section">
<h3 className="sidebar-heading">
<span className="nav-icon"><ShieldIcon /></span>
<span className="nav-label">{t.nav.moderation}</span>
</h3>
<NavLink to="/" end className={navLinkClass} onClick={closeMobile} title={t.nav.watch}>
<span className="nav-icon"><WatchIcon /></span>
<span className="nav-label">{t.nav.watch}</span>
</NavLink>
<NavLink to="/hub" className={navLinkClass} onClick={closeMobile} title={t.nav.hub}>
<span className="nav-icon"><HubIcon /></span>
<span className="nav-label">{t.nav.hub}</span>
</NavLink>
<NavLink to="/settings" className={navLinkClass} onClick={closeMobile} title={t.nav.settings}>
<span className="nav-icon"><SettingsIcon /></span>
<span className="nav-label">{t.nav.settings}</span>
</NavLink>
<a
className="nav-item"
href={DATABASE_URL}
target="_blank"
rel="noopener noreferrer"
onClick={closeMobile}
title={t.nav.database}
>
<span className="nav-icon"><DatabaseIcon /></span>
<span className="nav-label">{t.nav.database}</span>
</a>
</div>
</nav>
{sidebarOpen && (
<div className="sidebar-backdrop" onClick={closeMobile} />
)}
<main className="main-content">
<header className="topbar">
<div className="topbar-left">
<button
className="icon-btn"
type="button"
onClick={() => {
if (window.matchMedia("(max-width: 820px)").matches) {
setSidebarOpen((v) => !v);
} else {
setCollapsed((v) => !v);
}
}}
aria-label={t.topbar.toggleSidebar}
title={t.topbar.toggleSidebar}
>
<MenuIcon />
</button>
</div>
<div className="topbar-search">
<SearchIcon />
<input
placeholder={t.topbar.searchPlaceholder}
aria-label={t.topbar.searchPlaceholder}
/>
</div>
<div className="topbar-right">
<button
className="lang-toggle"
type="button"
onClick={toggleLocale}
title={t.topbar.toggleLanguage}
aria-label={t.topbar.toggleLanguage}
>
<GlobeIcon /> {t.topbar.languageLabel}
</button>
<button
className="theme-toggle"
type="button"
onClick={toggleTheme}
title={t.topbar.toggleTheme}
aria-label={t.topbar.toggleTheme}
>
{theme === "dark" ? <SunIcon /> : <MoonIcon />}
</button>
<button className="sign-in" type="button">
{t.topbar.signIn}
</button>
</div>
</header>
<Outlet />
</main>
</div>
);
}