+ {/* Header */}
+
+
+
Music
+
+ Download audio from YouTube playlists for workout tracks
+
+
+
+
+
+ {/* Import section */}
+
+
+
+
+ {/* Jobs + Detail layout */}
+
+ {/* Jobs list */}
+
+
+ Jobs ({jobs.length})
+
+ {jobs.length === 0 ? (
+
+ No jobs yet. Import a playlist to get started.
+
+ ) : (
+ jobs.map((job) => (
+
handleSelectJob(job.id)}
+ onStart={() => handleStart(job.id)}
+ onStop={stopProcessing}
+ onDelete={() => setDeleteTarget(job)}
+ />
+ ))
+ )}
+
+
+ {/* Detail panel */}
+
+ {activeJob ? (
+
+
+
+ {activeJob.playlist_title || "Untitled Playlist"}
+
+
+
+
+
+
+ ) : (
+
+
+
+
+ Select a job to view its tracks
+
+
+
+ )}
+
+
+
+ {/* Delete confirmation dialog */}
+
+
+ );
+}
diff --git a/admin-web/components/sidebar.tsx b/admin-web/components/sidebar.tsx
index af4f3e0..1c88158 100644
--- a/admin-web/components/sidebar.tsx
+++ b/admin-web/components/sidebar.tsx
@@ -10,6 +10,7 @@ import {
Users,
FolderOpen,
ImageIcon,
+ Music,
LogOut,
Flame,
} from "lucide-react";
@@ -20,6 +21,7 @@ const navItems = [
{ 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() {
diff --git a/admin-web/lib/supabase.ts b/admin-web/lib/supabase.ts
index a2701c7..b2a80cf 100644
--- a/admin-web/lib/supabase.ts
+++ b/admin-web/lib/supabase.ts
@@ -34,6 +34,28 @@ export type Json =
| { [key: string]: Json | undefined }
| Json[]
+export const MUSIC_GENRES = [
+ 'edm', 'hip-hop', 'pop', 'rock', 'latin', 'house',
+ 'drum-and-bass', 'dubstep', 'r-and-b', 'country', 'metal', 'ambient',
+] as const
+
+export type MusicGenre = typeof MUSIC_GENRES[number]
+
+export const GENRE_LABELS: Record