58 lines
3.0 KiB
TypeScript
58 lines
3.0 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { usePathname } from "next/navigation";
|
|
import { CloudSun, Droplets, TriangleAlert } from "lucide-react";
|
|
import { NAV_ITEMS } from "@/lib/constants";
|
|
import { cn } from "@/lib/utils";
|
|
import { InstallPWAButton } from "@/components/ui/install-pwa-button";
|
|
import { ThemeToggle } from "@/components/ui/theme-toggle";
|
|
import { LanguageToggle } from "@/components/ui/language-toggle";
|
|
import { useI18n } from "@/lib/i18n";
|
|
|
|
const icons = [CloudSun, TriangleAlert, Droplets];
|
|
|
|
export function AppShell({ children }: { children: React.ReactNode }) {
|
|
const pathname = usePathname();
|
|
const { t } = useI18n();
|
|
return (
|
|
<div className="min-h-screen overflow-x-hidden bg-background">
|
|
<header className="sticky top-0 z-40 border-b border-border/70 bg-surface/80 backdrop-blur-xl dark:bg-background/80">
|
|
<div className="mx-auto flex max-w-7xl items-center justify-between px-4 py-3 sm:px-6 lg:px-8">
|
|
<Link href="/" className="text-2xl font-semibold tracking-[-0.09em] text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent">
|
|
wtr<span className="text-accent">.</span>
|
|
</Link>
|
|
<nav aria-label={t("nav.main")} className="hidden items-center gap-1 md:flex">
|
|
{NAV_ITEMS.map((item) => {
|
|
const active = item.href === "/" ? pathname === "/" : pathname.startsWith(item.href);
|
|
return (
|
|
<Link key={item.href} href={item.href} className={cn("rounded-control px-4 py-2 text-sm font-medium transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent", active ? "bg-foreground text-background shadow-soft" : "text-muted hover:bg-surface-muted/70")}>
|
|
{t(item.labelKey)}
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
<div className="flex items-center gap-2">
|
|
<InstallPWAButton />
|
|
<LanguageToggle />
|
|
<ThemeToggle />
|
|
</div>
|
|
</div>
|
|
</header>
|
|
<main className="mx-auto max-w-7xl px-4 pb-28 pt-5 sm:px-6 sm:pt-8 lg:px-8">{children}</main>
|
|
<nav aria-label={t("nav.mobile")} className="fixed inset-x-3 bottom-3 z-50 flex justify-around rounded-panel border border-border/70 bg-surface/90 p-1.5 shadow-card backdrop-blur-xl dark:bg-surface/90 md:hidden">
|
|
{NAV_ITEMS.map((item, index) => {
|
|
const Icon = icons[index];
|
|
const active = item.href === "/" ? pathname === "/" : pathname.startsWith(item.href);
|
|
return (
|
|
<Link key={item.href} href={item.href} className={cn("flex min-w-[5rem] flex-col items-center gap-1 rounded-card px-3 py-2 text-[0.68rem] font-medium transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-accent", active ? "bg-foreground text-background" : "text-muted")}>
|
|
<Icon className="size-4" />
|
|
{t(item.labelKey)}
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
</div>
|
|
);
|
|
}
|