Files
tabatago/admin-web/components/sidebar.tsx
Millian Lamiaux 3d8d9efd70 feat: YouTube music download system with admin dashboard
Sidecar architecture: edge functions (auth + DB) → youtube-worker container
(youtubei.js for playlist metadata, yt-dlp for audio downloads).

- Edge functions: youtube-playlist, youtube-process, youtube-status (CRUD)
- youtube-worker sidecar: Node.js + yt-dlp, downloads M4A to Supabase Storage
- Admin music page: import playlists, process queue, inline genre editing
- 12 music genres with per-track assignment and import-time defaults
- DB migrations: download_jobs, download_items tables with genre column
- Deploy script and CI workflow for edge functions + sidecar
2026-03-26 10:47:05 +01:00

80 lines
2.3 KiB
TypeScript

"use client";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import { supabase } from "@/lib/supabase";
import { Button } from "@/components/ui/button";
import {
LayoutDashboard,
Dumbbell,
Users,
FolderOpen,
ImageIcon,
Music,
LogOut,
Flame,
} from "lucide-react";
const navItems = [
{ href: "/", label: "Dashboard", icon: LayoutDashboard },
{ href: "/workouts", label: "Workouts", icon: Dumbbell },
{ href: "/trainers", label: "Trainers", icon: Users },
{ href: "/collections", label: "Collections", icon: FolderOpen },
{ href: "/media", label: "Media", icon: ImageIcon },
{ href: "/music", label: "Music", icon: Music },
];
export function Sidebar() {
const pathname = usePathname();
const router = useRouter();
const handleLogout = async () => {
await supabase.auth.signOut();
router.push("/login");
};
return (
<aside className="w-64 bg-neutral-950 border-r border-neutral-800 flex flex-col h-screen sticky top-0">
<div className="p-6 border-b border-neutral-800">
<div className="flex items-center gap-2">
<Flame className="w-6 h-6 text-orange-500" />
<span className="text-lg font-bold text-white">TabataFit</span>
</div>
<p className="text-neutral-500 text-sm mt-1">Admin Dashboard</p>
</div>
<nav className="flex-1 p-4 space-y-1">
{navItems.map((item) => {
const Icon = item.icon;
const isActive = pathname === item.href;
return (
<Link
key={item.href}
href={item.href}
className={`flex items-center gap-3 px-4 py-3 rounded-lg text-sm font-medium transition-colors ${
isActive
? "bg-orange-500/10 text-orange-500"
: "text-neutral-400 hover:text-white hover:bg-neutral-900"
}`}
>
<Icon className="w-5 h-5" />
{item.label}
</Link>
);
})}
</nav>
<div className="p-4 border-t border-neutral-800">
<Button
variant="ghost"
className="w-full justify-start text-neutral-400 hover:text-white"
onClick={handleLogout}
>
<LogOut className="w-5 h-5 mr-3" />
Logout
</Button>
</div>
</aside>
);
}