growth-activities / src /components /ConsoleLayout.tsx
3v324v23's picture
init: growth-activities for hf spaces
243dc36
Raw
History Blame Contribute Delete
4.56 kB
import type { ReactNode } from "react"
import { Link, NavLink, Outlet, useLocation } from "react-router-dom"
import { BarChart3, Blocks, LayoutDashboard, RotateCcw, Sparkles } from "lucide-react"
import { cn } from "@/lib/utils"
import { useTheme } from "@/hooks/useTheme"
import { useAppStore } from "@/store/useAppStore"
function ShellNavItem(props: { to: string; icon: ReactNode; label: string }) {
return (
<NavLink
to={props.to}
end
className={({ isActive }) =>
cn(
"group flex items-center gap-3 rounded-xl px-3 py-2 text-sm transition",
isActive
? "bg-[color:var(--panel)] shadow-[var(--glow)]"
: "text-[color:var(--muted)] hover:bg-[color:var(--panel2)] hover:text-[color:var(--text)]",
)
}
>
<span className="grid h-9 w-9 place-items-center rounded-lg border border-[color:var(--stroke)] bg-[color:var(--panel2)] text-[color:var(--text)] transition group-hover:border-[color:var(--accentSoft)]">
{props.icon}
</span>
<span className="font-medium tracking-wide">{props.label}</span>
</NavLink>
)
}
export default function ConsoleLayout() {
const { toggleTheme, isDark } = useTheme()
const resetDemo = useAppStore((s) => s.resetDemo)
const location = useLocation()
const showDataLink = location.pathname.startsWith("/campaign/")
return (
<div className="relative min-h-screen bg-[color:var(--bg)] text-[color:var(--text)]">
<div className="pointer-events-none absolute inset-0 opacity-60 [background:var(--bgFx)]" />
<div className="pointer-events-none absolute inset-0 opacity-[0.06] [background-image:radial-gradient(circle_at_1px_1px,rgba(255,255,255,1)_1px,transparent_0)] [background-size:22px_22px]" />
<div className="relative mx-auto grid max-w-[1220px] grid-cols-1 gap-6 px-4 py-6 md:grid-cols-[280px_1fr]">
<aside className="sticky top-6 hidden h-[calc(100vh-48px)] flex-col justify-between md:flex">
<div className="rounded-3xl border border-[color:var(--stroke)] bg-[color:var(--panel2)] p-4 shadow-[var(--glow)] backdrop-blur">
<Link to="/" className="group flex items-center gap-3">
<span className="grid h-11 w-11 place-items-center rounded-2xl border border-[color:var(--stroke)] bg-[color:var(--panel)] shadow-[0_0_0_1px_rgba(255,255,255,0.06)]">
<Sparkles className="h-5 w-5 text-[color:var(--accent)]" />
</span>
<div className="leading-tight">
<div className="font-display text-lg tracking-[0.08em]">GROWTH LOOP</div>
<div className="text-xs text-[color:var(--muted)]">活动搭建 · 留资 · 核销 · 复盘</div>
</div>
</Link>
<div className="mt-5 space-y-2">
<ShellNavItem to="/" icon={<LayoutDashboard className="h-4 w-4" />} label="运营控制台" />
<ShellNavItem to="/campaign/new" icon={<Blocks className="h-4 w-4" />} label="新建活动" />
{showDataLink ? (
<ShellNavItem to={location.pathname.replace(/\/edit$|\/leads$/, "/leads")} icon={<BarChart3 className="h-4 w-4" />} label="线索与导出" />
) : null}
</div>
</div>
<div className="mt-4 space-y-3 rounded-3xl border border-[color:var(--stroke)] bg-[color:var(--panel2)] p-4 backdrop-blur">
<button
type="button"
onClick={toggleTheme}
className="flex w-full items-center justify-between rounded-2xl border border-[color:var(--stroke)] bg-[color:var(--panel)] px-4 py-3 text-sm text-[color:var(--muted)] transition hover:text-[color:var(--text)]"
>
<span className="font-medium">模式</span>
<span className="font-display text-xs tracking-[0.12em]">{isDark ? "DARK" : "LIGHT"}</span>
</button>
<button
type="button"
onClick={resetDemo}
className="flex w-full items-center justify-between rounded-2xl border border-[color:var(--stroke)] bg-[color:var(--panel)] px-4 py-3 text-sm text-[color:var(--muted)] transition hover:border-[color:var(--accentSoft)] hover:text-[color:var(--text)]"
>
<span className="font-medium">重置演示数据</span>
<RotateCcw className="h-4 w-4" />
</button>
</div>
</aside>
<main className="min-w-0">
<Outlet />
</main>
</div>
</div>
)
}