"use client"; import { AnimatePresence, motion, useReducedMotion } from "framer-motion"; import { Settings, ShieldCheck } from "lucide-react"; import Image from "next/image"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { useLocale, useTranslations } from "next-intl"; import { useState, type ReactNode } from "react"; import { LocaleToggle } from "@/components/layout/locale-toggle"; import { SoundToggle } from "@/components/sound-toggle"; import { ThemeToggle } from "@/components/theme-toggle"; import { Dock, DockIcon } from "@/components/ui/dock"; import { buildAdminUrl } from "@/lib/admin-routing"; import { getLocalizedPath, stripLocalePrefix, type AppLocale } from "@/lib/locale"; import { cn } from "@/lib/utils"; type SiteDockProps = { defaultLocale: AppLocale; /** * Server-computed via `isSuperAdmin()` in the parent layout. This client * component only decides whether to *render* the Admin shortcut — it performs * no auth check and grants no access; the real guard lives server-side on * each admin page/action. */ isSuperAdmin?: boolean; }; type NavKey = "home" | "about" | "portfolio" | "products" | "contact"; type NavItem = { key: NavKey; path: string; disabled?: boolean; }; const navItems: NavItem[] = [ { key: "home", path: "" }, { key: "about", path: "/about" }, { key: "portfolio", path: "/portfolio" }, { key: "products", path: "/products", disabled: true }, { key: "contact", path: "/contact" }, ]; // Graphite squircle matching the SVG page icons, used for the settings tile. const controlTile = "group relative overflow-visible rounded-[26%] bg-gradient-to-b from-[#3f4551] to-[#191d24] shadow-[0_6px_14px_-6px_rgba(0,0,0,0.5)] ring-1 ring-white/10 transition-shadow duration-200 hover:shadow-[0_10px_22px_-8px_rgba(0,0,0,0.6)]"; // Glass pill used for each control that fans out along the arc. const fanPill = "flex h-11 w-11 items-center justify-center rounded-full border border-transparent bg-background/90 text-foreground/85 shadow-panel backdrop-blur-chrome transition-colors hover:bg-accent hover:text-foreground"; // Radius (px) and opening angles of the arc the controls fan out along. const ARC_RADIUS = 74; const ARC_START = 46; const ARC_END = 134; function isNavItemActive(currentPath: string, itemPath: string) { if (itemPath === "/") { return currentPath === "/"; } return currentPath === itemPath || currentPath.startsWith(`${itemPath}/`); } export function SiteDock({ defaultLocale, isSuperAdmin = false }: SiteDockProps) { const locale = useLocale(); const pathname = usePathname(); const t = useTranslations("navigation"); const reducedMotion = useReducedMotion(); const [settingsOpen, setSettingsOpen] = useState(false); const currentPath = stripLocalePrefix(pathname); const homeHref = getLocalizedPath(locale, "/", defaultLocale); const fanItems: ReactNode[] = [ , , , ]; if (isSuperAdmin) { fanItems.push( , ); } const arcPosition = (index: number) => { const count = fanItems.length; const angle = count === 1 ? 90 : ARC_START + ((ARC_END - ARC_START) * index) / (count - 1); const radians = (angle * Math.PI) / 180; return { x: Math.cos(radians) * ARC_RADIUS, y: -Math.sin(radians) * ARC_RADIUS }; }; return ( <> {/* click-away scrim (below the dock, above the page) */} {settingsOpen ? ( {settingsOpen ? null : } ); } function DockSeparator() { return
; } function TileGloss() { return ( ); } function DockTip({ label, soon }: { label: string; soon?: string }) { return ( {label} {soon ? {soon} : null} ); }