103 lines
2.8 KiB
TypeScript
103 lines
2.8 KiB
TypeScript
|
|
import { useStore } from '@nanostores/react'
|
||
|
|
import { $router } from '../../stores/router'
|
||
|
|
import { Icons, type IconComponent } from '../shared/Icons'
|
||
|
|
|
||
|
|
interface NavItem {
|
||
|
|
route: string
|
||
|
|
label: string
|
||
|
|
Icon: IconComponent
|
||
|
|
}
|
||
|
|
|
||
|
|
interface NavSection {
|
||
|
|
title: string
|
||
|
|
items: NavItem[]
|
||
|
|
}
|
||
|
|
|
||
|
|
const navigation: NavSection[] = [
|
||
|
|
{
|
||
|
|
title: '',
|
||
|
|
items: [
|
||
|
|
{ route: 'home', label: 'Home', Icon: Icons.Home },
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: 'Content',
|
||
|
|
items: [
|
||
|
|
{ route: 'posts', label: 'Posts', Icon: Icons.Posts },
|
||
|
|
{ route: 'analytics', label: 'Analytics', Icon: Icons.Analytics },
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: 'Site',
|
||
|
|
items: [
|
||
|
|
{ route: 'general', label: 'General', Icon: Icons.Settings },
|
||
|
|
{ route: 'design', label: 'Design', Icon: Icons.Design },
|
||
|
|
{ route: 'domain', label: 'Domain', Icon: Icons.Domain },
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: 'Readers',
|
||
|
|
items: [
|
||
|
|
{ route: 'engagement', label: 'Engagement', Icon: Icons.Engagement },
|
||
|
|
{ route: 'monetization', label: 'Monetization', Icon: Icons.Monetization },
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: 'Developer',
|
||
|
|
items: [
|
||
|
|
{ route: 'plugins', label: 'Plugins', Icon: Icons.Code },
|
||
|
|
{ route: 'api', label: 'API Keys', Icon: Icons.ApiKeys },
|
||
|
|
{ route: 'data', label: 'Data', Icon: Icons.Data },
|
||
|
|
],
|
||
|
|
},
|
||
|
|
{
|
||
|
|
title: 'Account',
|
||
|
|
items: [
|
||
|
|
{ route: 'billing', label: 'Billing', Icon: Icons.Billing },
|
||
|
|
],
|
||
|
|
},
|
||
|
|
]
|
||
|
|
|
||
|
|
export function Sidebar() {
|
||
|
|
const page = useStore($router)
|
||
|
|
const currentRoute = page?.route ?? 'posts'
|
||
|
|
|
||
|
|
return (
|
||
|
|
<aside className="w-56 h-screen bg-bg border-r border-border flex flex-col">
|
||
|
|
<div className="px-4 py-6">
|
||
|
|
<a href="/" className="block group">
|
||
|
|
<div className="text-[15px] font-bold tracking-tight text-text">
|
||
|
|
WriteKit
|
||
|
|
</div>
|
||
|
|
<div className="text-[11px] font-medium text-muted tracking-wide">
|
||
|
|
Studio
|
||
|
|
</div>
|
||
|
|
</a>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<nav className="flex-1 overflow-y-auto px-3">
|
||
|
|
{navigation.map((section, idx) => (
|
||
|
|
<div key={section.title || idx} className="mb-1">
|
||
|
|
{section.title && <div className="nav-section">{section.title}</div>}
|
||
|
|
<div className="space-y-0.5">
|
||
|
|
{section.items.map(item => {
|
||
|
|
const href = item.route === 'home' ? '/studio' : `/studio/${item.route}`
|
||
|
|
return (
|
||
|
|
<a
|
||
|
|
key={item.route}
|
||
|
|
href={href}
|
||
|
|
className={currentRoute === item.route ? 'nav-item-active' : 'nav-item'}
|
||
|
|
>
|
||
|
|
<item.Icon className={`text-sm ${currentRoute === item.route ? 'text-accent opacity-100' : 'opacity-50'}`} />
|
||
|
|
<span>{item.label}</span>
|
||
|
|
</a>
|
||
|
|
)
|
||
|
|
})}
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</nav>
|
||
|
|
</aside>
|
||
|
|
)
|
||
|
|
}
|