diff --git a/docker/ui/src/components/HeroCinemaBanner.tsx b/docker/ui/src/components/HeroCinemaBanner.tsx index 1a90236..711287a 100644 --- a/docker/ui/src/components/HeroCinemaBanner.tsx +++ b/docker/ui/src/components/HeroCinemaBanner.tsx @@ -1,78 +1,201 @@ -import { Disc } from 'lucide-react' +import { Disc, Film, Sparkles, CheckCircle, Shield, Zap } from 'lucide-react' +import { RadialProgress } from './ui/RadialProgress' +import { TypeBadge, StatusBadge } from './ui/Badge' +import { Button } from './ui/Button' -export function HeroCinemaBanner() { - return ( -
- {/* Top Ambient Glow */} -
+interface JobMeta { + year?: number + overview?: string + poster_path?: string + genres?: string[] + runtime?: number + type?: string + source?: string +} - {/* 5-Panel Movie Carousel Wall (Matching SC1 Mockup 1:1) */} -
- - {/* Panel 1 */} -
- Sci-Fi -
-
- Sci-Fi Cinema -
-
+interface Job { + id: string + type: 'cd' | 'dvd' | 'bluray' | 'uhd' + status: 'pending' | 'processing' | 'transcoding' | 'canceling' | 'completed' | 'failed' + device: string + startTime: string + endTime?: string + progress: number + title?: string + can_retry?: boolean + meta?: JobMeta | null +} - {/* Panel 2 */} -
- Blade Runner 2049 -
-
- Blade Runner 2049 -
-
+function posterUrl(meta?: JobMeta | null): string | null { + const p = meta?.poster_path + if (!p) return null + return p.startsWith('http') ? p : `https://image.tmdb.org/t/p/w500${p}` +} - {/* Center Panel 3: Spinning Optical Disc Showcase */} -
-
-
- +interface HeroCinemaBannerProps { + aktiverJob?: Job | null + completedJobs?: Job[] + onDetailClick?: (jobId: string) => void + onCancelClick?: (jobId: string) => void +} + +export function HeroCinemaBanner({ + aktiverJob, + completedJobs = [], + onDetailClick, + onCancelClick, +}: HeroCinemaBannerProps) { + // Fall 1: Aktiver Job (Rippt oder Komprimiert) + if (aktiverJob) { + const isTranscoding = aktiverJob.status === 'transcoding' + const statusText = isTranscoding ? 'HandBrake Kompression' : 'MakeMKV Extraktion' + const poster = posterUrl(aktiverJob.meta) + + return ( +
+
+
+
+ {poster ? ( +
+ +
+ +
+
+ ) : ( +
+
+ +
+
+ )} +
+ +
+
+ + NOW RIPPING + + + +
+ +

+ {aktiverJob.title || 'Aktiver Ripping-Prozess'} +

+ +

+ Laufwerk: {aktiverJob.device} · {statusText} +

-
- DISC ENGINE + +
+ + +
+ + +
+
+ ) + } - {/* Panel 4 */} -
- Dune -
-
- Dune Part II + // Fall 2: Keine echten gerippten Medien vorhanden -> Standby Hero Banner (Keine Fake-Filme!) + if (completedJobs.length === 0) { + return ( +
+
+
+
+
+ +
+
+ +
+
+ + READY FOR DISC + + System Status: Bereit +
+

+ Rippy Cinema Studio +

+

+ Automatische Disc-Erkennung aktiv. Lege eine 4K UHD, Blu-ray, DVD oder CD in dein Laufwerk ein. +

+
+
+ +
+
+ + Gerippte Discs: 0 +
+
+
+ + MakeMKV 1:1 Lossless +
+
+ ) + } - {/* Panel 5 */} -
- Interstellar -
-
- Interstellar -
-
+ // Fall 3: Echte gerippte Titel vorhanden -> Zeige ECHTE gerippte Discs in der Galerie (Maximal 5 echte Jobs) + const displayJobs = completedJobs.slice(0, 5) + return ( +
+
+

+ ECHTE GERIPPTE MEDIEN ({completedJobs.length}) +

+ Aus deiner Rippy-Datenbank +
+ +
+ {displayJobs.map((job) => { + const poster = posterUrl(job.meta) + return ( +
onDetailClick && onDetailClick(job.id)} + className="relative rounded-2xl overflow-hidden group cursor-pointer border border-slate-800 hover:border-amber-500/50 transition-all bg-slate-900 aspect-[2/3] flex flex-col justify-end p-3" + > + {poster ? ( + {job.title + ) : ( +
+ + {job.type} +
+ )} +
+ +
+ +

+ {job.title || 'Unbekannt'} +

+
+
+ ) + })}
) diff --git a/docker/ui/src/pages/Anleitung.tsx b/docker/ui/src/pages/Anleitung.tsx index f52f282..84d2d7a 100644 --- a/docker/ui/src/pages/Anleitung.tsx +++ b/docker/ui/src/pages/Anleitung.tsx @@ -23,7 +23,7 @@ export default function AnleitungPage() { const fett = "font-semibold text-slate-900 dark:text-slate-100" return ( -
+
clearInterval(interval) }, []) - const aktiverJob = jobs.find(j => j.status === 'processing' || j.status === 'transcoding') || jobs[0] || null - const queueJobs = jobs.filter(j => j.id !== aktiverJob?.id && j.status === 'pending') + const aktiverJob = jobs.find(j => j.status === 'processing' || j.status === 'transcoding') + const queueJobs = jobs.filter(j => j.status === 'pending') const completedJobs = jobs.filter(j => j.status === 'completed') - // Sample data fallback for Recently Ripped if list is short to match SC1 visual richness - const recentlyRippedList = completedJobs.length > 0 ? completedJobs.slice(0, 3) : [ - { id: 'sample-1', title: 'Everything Everywhere All At Once', year: 2021, poster: 'https://images.unsplash.com/photo-1536440136628-849c177e76a1?q=80&w=400&auto=format&fit=crop' }, - { id: 'sample-2', title: 'Top Gun: Maverick', year: 2023, poster: 'https://images.unsplash.com/photo-1517604931442-7e0c8ed2963c?q=80&w=400&auto=format&fit=crop' }, - { id: 'sample-3', title: 'Black Panther: Wakanda Forever', year: 2022, poster: 'https://images.unsplash.com/photo-1626814026160-2237a95fc5a0?q=80&w=400&auto=format&fit=crop' }, - ] - if (loading) { return (
@@ -115,10 +108,15 @@ export default function Dashboard() { return (
- {/* 1. Top Hero Movie Wall Carousel (Identisch zu SC1 Top Banner) */} - + {/* 1. Top Hero Cinema Showcase (Nur echte API Daten!) */} + setDetailJobId(id)} + onCancelClick={id => api.post(`/jobs/${id}/cancel`).catch(() => {})} + /> - {/* 2. Main 2-Column Grid Layout (Matching SC1 1:1) */} + {/* 2. Main 2-Column Grid Layout */}
{/* LEFT COLUMN (lg:col-span-7) */} @@ -128,14 +126,13 @@ export default function Dashboard() { - + Current Job {aktiverJob ? (
- {/* Poster Thumbnail */} {posterUrl(aktiverJob.meta) ? (
- {/* Amber Progress Bar */}
Status: {aktiverJob.status === 'transcoding' ? 'Komprimieren' : 'Extraktion'} @@ -171,8 +167,8 @@ export default function Dashboard() {
-

Ripping status: {aktiverJob.status === 'transcoding' ? 'HandBrake H.265' : 'MakeMKV Lossless Extraktion'}

-

Disc type: {aktiverJob.type.toUpperCase()} Blu-ray

+

Status: {aktiverJob.status === 'transcoding' ? 'HandBrake H.265' : 'MakeMKV Lossless Extraktion'}

+

Disc typ: {aktiverJob.type.toUpperCase()}

Gerät: {aktiverJob.device} · Gestartet: {new Date(aktiverJob.startTime).toLocaleTimeString('de-DE')}

@@ -185,51 +181,25 @@ export default function Dashboard() {
- {/* Ripping Queue Card */} + {/* Ripping Queue Card (NUR ECHTE QUEUED JOBS) */} - Ripping Queue + Ripping Queue ({queueJobs.length}) {queueJobs.length === 0 ? ( -
-
-
- 2. - The Menu -
- Queue 1 -
-
-
- 3. - Glass Onion -
- Queue 2 -
-
-
- 4. - Babylon -
- Queue 3 -
-
-
- 5. - Avatar: The Way of Water -
- Queue 4 -
+
+ Warteschlange ist leer (0 Jobs eingereiht)
) : ( queueJobs.map((j, idx) => (
- {idx + 2}. + {idx + 1}. {j.title || 'Unbekannt'} +
- Queue {idx + 1} + Wartend
)) )} @@ -241,48 +211,45 @@ export default function Dashboard() { {/* RIGHT COLUMN (lg:col-span-5) */}
- {/* Server Status Card with Live Sparkline Graph */} + {/* Server Status Card mit Live Sparkline Graph */} Server Status
- - {/* Left: Usage Bars */}
CPU Usage: - 42% + Aktiv
-
+
- RAM Usage: - 6.1GB / 18GB + Status: + Bereit
-
+
- Storage Used: - 2.1TB / 8TB + Speichermounts: + {plaetze.length ? `${plaetze[0].frei_gb} GB frei` : 'Aktiv'}
-
+
- {/* Right: Live Amber Sparkline Graph */}
- {/* Recently Ripped Card (3 Poster Grid matching SC1) */} + {/* Recently Ripped Card (NUR ECHTE GERIPPTE JOBS) */} - - Recently Ripped + + Zuletzt gerippt ({completedJobs.length}) + {completedJobs.length > 0 && ( + + )} -
- {recentlyRippedList.map((item: any) => { - const poster = item.poster || posterUrl(item.meta) - return ( -
-
- {poster ? ( - {item.title} - ) : ( -
- -
- )} -
+ {completedJobs.length === 0 ? ( +
+ Noch keine gerippten Medien in der Datenbank +
+ ) : ( +
+ {completedJobs.slice(0, 3).map((item) => { + const poster = posterUrl(item.meta) + return ( +
+
+ {poster ? ( + {item.title + ) : ( +
+ + {item.type} +
+ )} +
-
-

{item.title}

-

{item.year || 2023}

- +
+

{item.title || 'Unbekannt'}

+

{new Date(item.startTime).toLocaleDateString('de-DE')}

+ +
-
- ) - })} -
+ ) + })} +
+ )} diff --git a/docker/ui/src/pages/Logs.tsx b/docker/ui/src/pages/Logs.tsx index 22f4789..a3bdb4e 100644 --- a/docker/ui/src/pages/Logs.tsx +++ b/docker/ui/src/pages/Logs.tsx @@ -82,7 +82,7 @@ export default function LogsPage() { } return ( -
+
+