File size: 4,562 Bytes
243dc36
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
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>
  )
}