mirror of
https://github.com/truewhile/MeBox.git
synced 2026-10-05 04:56:37 +08:00
@
feat: Warm Industrial UI redesign + social footer + code deduplication Design System (Phase A): - New palette: warm charcoal #111110 / amber gold #c9954a / sage green #7a9a8a - Fonts: Cabinet Grotesk (heading) + Geist (body) + JetBrains Mono (data) - SVG grain texture overlay, collapsed sidebar with hover expand - Bento grid dashboard with WideContinueCard + PosterCard + QuickLink - Backward-compatible CSS aliases for glass-panel/neon-button/input-base Social Footer (Phase B): - AppFooter component with GitHub repo / author homepage / TG group links - Embedded in LoginPage, Layout (global), and SettingsPage - TG group badge added to README.md Code Cleanup: - Removed deprecated DownloadClientCard / NotifyChannelCard components - Merged downloadClient/notify API clients into unified modules - Route deduplication: removed authed-group duplicates for admin endpoints - Removed redundant AdminPage tabs (sites/downloads/notify/scheduler) - UI fixes: blank pages (discover/download-clients/notify-channels) nil slice defense - Email channel support in NotifyChannelsPage @
This commit is contained in:
+132
-88
@@ -36,11 +36,11 @@ import {
|
||||
} from 'lucide-react'
|
||||
import clsx from 'clsx'
|
||||
|
||||
import { AppFooter } from './AppFooter'
|
||||
import { libraryAPI } from '../api/library'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
import type { Library } from '../types'
|
||||
|
||||
// Top-level chrome: a fixed left rail + scrollable content area.
|
||||
export function Layout() {
|
||||
const navigate = useNavigate()
|
||||
const user = useAuthStore((s) => s.user)
|
||||
@@ -58,96 +58,132 @@ export function Layout() {
|
||||
|
||||
return (
|
||||
<div className="flex min-h-full">
|
||||
<aside className="hidden w-64 shrink-0 flex-col border-r border-white/5 bg-surface-900/80 px-4 py-6 backdrop-blur md:flex">
|
||||
<Link to="/" className="mb-8 flex items-center gap-2 px-2">
|
||||
<Film className="h-6 w-6 text-primary-400" />
|
||||
<span className="font-display text-xl font-bold tracking-wide text-white">
|
||||
MediaStationGo
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
<nav className="flex-1 space-y-1 overflow-y-auto pr-1">
|
||||
<SidebarLink to="/" icon={<Home size={18} />} label="首页" end />
|
||||
<SidebarLink to="/discover" icon={<Compass size={18} />} label="发现" />
|
||||
<SidebarLink to="/search" icon={<Search size={18} />} label="搜索" />
|
||||
<SidebarLink to="/ai" icon={<Sparkles size={18} />} label="AI 助手" />
|
||||
<SidebarLink to="/favourites" icon={<Heart size={18} />} label="收藏" />
|
||||
<SidebarLink to="/playlists" icon={<ListMusic size={18} />} label="播放列表" />
|
||||
<SidebarLink to="/history" icon={<Clock size={18} />} label="观看历史" />
|
||||
<SidebarLink to="/poster-wall" icon={<GalleryHorizontalEnd size={18} />} label="海报墙" />
|
||||
|
||||
<div className="mt-6 px-2 text-xs uppercase tracking-wider text-slate-500">
|
||||
媒体库
|
||||
</div>
|
||||
{libraries.length === 0 && (
|
||||
<div className="px-2 py-1 text-sm text-slate-500">暂无媒体库</div>
|
||||
{/* ── Sidebar group — collapsed 64px, expands to 240px on hover ── */}
|
||||
<div className="group relative z-30 shrink-0">
|
||||
<aside
|
||||
className={clsx(
|
||||
'flex h-full flex-col border-r border-cream-900/15 bg-surface-600',
|
||||
'w-16 transition-all duration-300 ease-out',
|
||||
'group-hover:w-60',
|
||||
)}
|
||||
{libraries.map((lib) => (
|
||||
<SidebarLink
|
||||
key={lib.id}
|
||||
to={`/library/${lib.id}`}
|
||||
icon={<LibraryIcon size={18} />}
|
||||
label={lib.name}
|
||||
/>
|
||||
))}
|
||||
|
||||
<div className="mt-6 px-2 text-xs uppercase tracking-wider text-slate-500">
|
||||
自动化
|
||||
</div>
|
||||
<SidebarLink to="/downloads" icon={<CloudDownload size={18} />} label="下载" />
|
||||
<SidebarLink to="/subscriptions" icon={<Rss size={18} />} label="RSS 订阅" />
|
||||
<SidebarLink to="/dlna" icon={<Cast size={18} />} label="DLNA 投屏" />
|
||||
<SidebarLink to="/sites" icon={<Globe size={18} />} label="站点管理" />
|
||||
<SidebarLink to="/site-search" icon={<Search size={18} />} label="站点搜索" />
|
||||
|
||||
<div className="mt-6 px-2 text-xs uppercase tracking-wider text-slate-500">
|
||||
账号
|
||||
</div>
|
||||
<SidebarLink to="/profile" icon={<UserIcon size={18} />} label="个人资料" />
|
||||
<SidebarLink to="/play-profiles" icon={<UserCog size={18} />} label="观影 Profile" />
|
||||
|
||||
{user?.role === 'admin' && (
|
||||
<>
|
||||
<div className="mt-6 px-2 text-xs uppercase tracking-wider text-slate-500">
|
||||
管理
|
||||
</div>
|
||||
<SidebarLink to="/tasks" icon={<ListChecks size={18} />} label="实时任务" />
|
||||
<SidebarLink to="/stats" icon={<Activity size={18} />} label="运行状态" />
|
||||
<SidebarLink to="/storage" icon={<HardDrive size={18} />} label="存储" />
|
||||
<SidebarLink to="/files" icon={<FolderTree size={18} />} label="文件浏览" />
|
||||
<SidebarLink to="/duplicates" icon={<Copy size={18} />} label="重复文件" />
|
||||
<SidebarLink to="/scheduler" icon={<Clock size={18} />} label="定时任务" />
|
||||
<SidebarLink to="/sites" icon={<Globe size={18} />} label="站点管理" />
|
||||
<SidebarLink to="/strm" icon={<Link2 size={18} />} label="STRM 管理" />
|
||||
<SidebarLink to="/tools" icon={<Wrench size={18} />} label="运维工具" />
|
||||
<SidebarLink to="/notify-channels" icon={<Bell size={18} />} label="通知渠道" />
|
||||
<SidebarLink to="/download-clients" icon={<Server size={18} />} label="下载器" />
|
||||
<SidebarLink to="/storage-config" icon={<Cloud size={18} />} label="外部存储" />
|
||||
<SidebarLink to="/license" icon={<KeySquare size={18} />} label="许可证" />
|
||||
<SidebarLink to="/assistant" icon={<MessageSquare size={18} />} label="AI 对话" />
|
||||
<SidebarLink to="/settings" icon={<Sliders size={18} />} label="系统设置" />
|
||||
<SidebarLink to="/recycle" icon={<Trash2 size={18} />} label="回收站" />
|
||||
<SidebarLink to="/admin" icon={<Settings size={18} />} label="管理后台" />
|
||||
</>
|
||||
)}
|
||||
</nav>
|
||||
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="mt-4 flex items-center gap-2 rounded-lg px-3 py-2 text-sm text-slate-400 transition hover:bg-white/5 hover:text-white"
|
||||
>
|
||||
<LogOut size={16} />
|
||||
退出登录 ({user?.username})
|
||||
</button>
|
||||
</aside>
|
||||
{/* Logo */}
|
||||
<Link to="/" className="flex items-center gap-3 px-4 py-5">
|
||||
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md bg-brand-500/15">
|
||||
<Film className="h-4 w-4 text-brand-400" />
|
||||
</div>
|
||||
<span
|
||||
className={clsx(
|
||||
'font-display text-lg font-bold tracking-tight text-cream-100',
|
||||
'overflow-hidden whitespace-nowrap opacity-0 transition-opacity duration-200',
|
||||
'group-hover:opacity-100',
|
||||
)}
|
||||
>
|
||||
MediaStation
|
||||
</span>
|
||||
</Link>
|
||||
|
||||
<main className="flex-1 overflow-y-auto px-6 py-8">
|
||||
<Outlet />
|
||||
{/* Navigation */}
|
||||
<nav className="flex-1 space-y-0.5 overflow-y-auto px-2 pb-4">
|
||||
<SidebarLink to="/" icon={<Home size={18} />} label="首页" end />
|
||||
|
||||
<SectionHeader label="浏览" />
|
||||
<SidebarLink to="/discover" icon={<Compass size={18} />} label="发现" />
|
||||
<SidebarLink to="/search" icon={<Search size={18} />} label="搜索" />
|
||||
<SidebarLink to="/favourites" icon={<Heart size={18} />} label="收藏" />
|
||||
<SidebarLink to="/playlists" icon={<ListMusic size={18} />} label="播放列表" />
|
||||
<SidebarLink to="/history" icon={<Clock size={18} />} label="观看历史" />
|
||||
<SidebarLink to="/poster-wall" icon={<GalleryHorizontalEnd size={18} />} label="海报墙" />
|
||||
<SidebarLink to="/ai" icon={<Sparkles size={18} />} label="AI 助手" />
|
||||
|
||||
<SectionHeader label="媒体库" />
|
||||
{libraries.length === 0 && (
|
||||
<div className="hidden px-3 py-1.5 text-xs text-cream-500/50 group-hover:block">
|
||||
暂无媒体库
|
||||
</div>
|
||||
)}
|
||||
{libraries.map((lib) => (
|
||||
<SidebarLink
|
||||
key={lib.id}
|
||||
to={`/library/${lib.id}`}
|
||||
icon={<LibraryIcon size={18} />}
|
||||
label={lib.name}
|
||||
/>
|
||||
))}
|
||||
|
||||
<SectionHeader label="自动化" />
|
||||
<SidebarLink to="/downloads" icon={<CloudDownload size={18} />} label="下载" />
|
||||
<SidebarLink to="/subscriptions" icon={<Rss size={18} />} label="RSS 订阅" />
|
||||
<SidebarLink to="/dlna" icon={<Cast size={18} />} label="DLNA 投屏" />
|
||||
<SidebarLink to="/site-search" icon={<Search size={18} />} label="站点搜索" />
|
||||
|
||||
<SectionHeader label="账号" />
|
||||
<SidebarLink to="/profile" icon={<UserIcon size={18} />} label="个人资料" />
|
||||
<SidebarLink to="/play-profiles" icon={<UserCog size={18} />} label="观影 Profile" />
|
||||
|
||||
{user?.role === 'admin' && (
|
||||
<>
|
||||
<SectionHeader label="管理" />
|
||||
<SidebarLink to="/admin" icon={<Settings size={18} />} label="管理后台" />
|
||||
<SidebarLink to="/tasks" icon={<ListChecks size={18} />} label="实时任务" />
|
||||
<SidebarLink to="/stats" icon={<Activity size={18} />} label="运行状态" />
|
||||
<SidebarLink to="/sites" icon={<Globe size={18} />} label="站点管理" />
|
||||
<SidebarLink to="/notify-channels" icon={<Bell size={18} />} label="通知渠道" />
|
||||
<SidebarLink to="/download-clients" icon={<Server size={18} />} label="下载器" />
|
||||
<SidebarLink to="/scheduler" icon={<Clock size={18} />} label="定时任务" />
|
||||
<SidebarLink to="/storage" icon={<HardDrive size={18} />} label="存储" />
|
||||
<SidebarLink to="/storage-config" icon={<Cloud size={18} />} label="外部存储" />
|
||||
<SidebarLink to="/files" icon={<FolderTree size={18} />} label="文件浏览" />
|
||||
<SidebarLink to="/duplicates" icon={<Copy size={18} />} label="重复文件" />
|
||||
<SidebarLink to="/strm" icon={<Link2 size={18} />} label="STRM 管理" />
|
||||
<SidebarLink to="/tools" icon={<Wrench size={18} />} label="运维工具" />
|
||||
<SidebarLink to="/license" icon={<KeySquare size={18} />} label="许可证" />
|
||||
<SidebarLink to="/assistant" icon={<MessageSquare size={18} />} label="AI 对话" />
|
||||
<SidebarLink to="/settings" icon={<Sliders size={18} />} label="系统设置" />
|
||||
<SidebarLink to="/recycle" icon={<Trash2 size={18} />} label="回收站" />
|
||||
</>
|
||||
)}
|
||||
</nav>
|
||||
|
||||
{/* Bottom: logout */}
|
||||
<div className="border-t border-cream-900/15 px-3 py-3">
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="flex w-full items-center gap-3 rounded-lg px-2 py-2 text-sm text-cream-400 transition hover:bg-cream-900/10 hover:text-cream-200"
|
||||
title={`退出登录 (${user?.username})`}
|
||||
>
|
||||
<LogOut size={16} />
|
||||
<span className="overflow-hidden whitespace-nowrap opacity-0 transition-opacity duration-200 group-hover:opacity-100">
|
||||
退出
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
{/* ── Main content ── */}
|
||||
<main className="flex flex-1 flex-col overflow-y-auto">
|
||||
<div className="flex-1 px-6 py-8">
|
||||
<Outlet />
|
||||
</div>
|
||||
<AppFooter className="py-4" />
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** Section divider — visible only when sidebar is expanded */
|
||||
function SectionHeader({ label }: { label: string }) {
|
||||
return (
|
||||
<div className="mt-4 mb-1 overflow-hidden whitespace-nowrap px-3 opacity-0 transition-opacity duration-200 group-hover:opacity-100">
|
||||
<span className="text-[10px] font-medium uppercase tracking-[0.15em] text-cream-500/50">
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
/** Single nav link — icon always visible, label fades in on expand */
|
||||
function SidebarLink({
|
||||
to,
|
||||
icon,
|
||||
@@ -165,15 +201,23 @@ function SidebarLink({
|
||||
end={end}
|
||||
className={({ isActive }) =>
|
||||
clsx(
|
||||
'flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition',
|
||||
'flex items-center gap-3 rounded-lg px-3 py-2 text-sm transition-colors',
|
||||
isActive
|
||||
? 'bg-primary-400/10 text-primary-400'
|
||||
: 'text-slate-300 hover:bg-white/5 hover:text-white',
|
||||
? 'bg-brand-500/10 text-brand-400'
|
||||
: 'text-cream-400 hover:bg-cream-900/10 hover:text-cream-200',
|
||||
)
|
||||
}
|
||||
>
|
||||
{icon}
|
||||
<span className="truncate">{label}</span>
|
||||
<span className="flex shrink-0 items-center justify-center w-5 h-5">{icon}</span>
|
||||
<span
|
||||
className={clsx(
|
||||
'overflow-hidden whitespace-nowrap truncate',
|
||||
'opacity-0 transition-opacity duration-200',
|
||||
'group-hover:opacity-100',
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</span>
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user