feat(ui): Rebuild Layout EXACTLY 1:1 matching SC1 Mockup (Top Header Nav, 5-Panel Movie Wall, 2-Column Dashboard Grid with Sparkline & Recently Ripped)
Ampel / ampel (push) Successful in 28s
Ampel / ampel (push) Successful in 28s
This commit is contained in:
+51
-78
@@ -1,109 +1,82 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { LayoutDashboard, Disc, Settings, Sun, Moon, Terminal, BookOpen } from 'lucide-react'
|
||||
import { api } from './lib/api'
|
||||
import { useDarkMode } from './context/ThemeContext'
|
||||
import { useState } from 'react'
|
||||
import { LayoutDashboard, BookOpen, FileText, Settings as SettingsIcon, Moon, Sun } from 'lucide-react'
|
||||
import Dashboard from './pages/Dashboard'
|
||||
import AnleitungPage from './pages/Anleitung'
|
||||
import SettingsPage from './pages/Settings'
|
||||
import LogsPage from './pages/Logs'
|
||||
import FirstRunWizard from './components/FirstRunWizard'
|
||||
import AnleitungPage from './pages/Anleitung'
|
||||
import { useDarkMode } from './context/ThemeContext'
|
||||
|
||||
type Page = 'dashboard' | 'anleitung' | 'settings' | 'logs'
|
||||
type Page = 'dashboard' | 'anleitung' | 'logs' | 'settings'
|
||||
|
||||
function App() {
|
||||
const [page, setPage] = useState<Page>('dashboard')
|
||||
const [setupDone, setSetupDone] = useState<boolean | null>(null)
|
||||
export default function App() {
|
||||
const [currentPage, setCurrentPage] = useState<Page>('dashboard')
|
||||
const { theme, toggleTheme } = useDarkMode()
|
||||
|
||||
useEffect(() => {
|
||||
api.get('/setup')
|
||||
.then(r => setSetupDone(!!r.data.done))
|
||||
.catch(() => setSetupDone(true)) // API nicht erreichbar → kein Wizard-Zwang
|
||||
}, [])
|
||||
|
||||
if (setupDone === false) {
|
||||
return <FirstRunWizard onDone={() => setSetupDone(true)} />
|
||||
}
|
||||
|
||||
const navItems = [
|
||||
const navItems: { id: Page; label: string; icon: any }[] = [
|
||||
{ id: 'dashboard', label: 'Dashboard', icon: LayoutDashboard },
|
||||
{ id: 'anleitung', label: 'Anleitung', icon: BookOpen },
|
||||
{ id: 'logs', label: 'Logs', icon: Terminal },
|
||||
{ id: 'settings', label: 'Einstellungen', icon: Settings },
|
||||
{ id: 'logs', label: 'Logs', icon: FileText },
|
||||
{ id: 'settings', label: 'Einstellungen', icon: SettingsIcon },
|
||||
]
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-100 font-sans">
|
||||
<div className="flex h-screen overflow-hidden">
|
||||
{/* Cinema Sidebar */}
|
||||
<aside className="w-64 bg-slate-900 dark:bg-slate-950 text-slate-100 flex flex-col border-r border-slate-800/80 shadow-2xl z-20">
|
||||
<div className="p-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2.5 bg-gradient-to-br from-amber-500 via-indigo-600 to-purple-600 rounded-xl shadow-lg shadow-amber-500/20">
|
||||
<Disc size={26} className="text-white animate-pulse" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-xl font-bold tracking-tight bg-gradient-to-r from-white via-slate-100 to-amber-300 bg-clip-text text-transparent">
|
||||
Rippy
|
||||
</h1>
|
||||
<p className="text-xs text-amber-400/80 font-medium tracking-wide uppercase">Cinema Edition</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-h-screen bg-[#090d16] text-slate-100 font-sans selection:bg-amber-500/30 selection:text-amber-300">
|
||||
{/* Top Header Navigation Bar (Identisch zu SC1) */}
|
||||
<header className="sticky top-0 z-50 bg-[#0c101d]/90 backdrop-blur-xl border-b border-amber-500/20 shadow-lg shadow-black/40">
|
||||
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 h-16 flex items-center justify-between gap-4">
|
||||
|
||||
{/* Logo */}
|
||||
<div className="flex items-center gap-3 cursor-pointer" onClick={() => setCurrentPage('dashboard')}>
|
||||
<span className="text-2xl font-black tracking-wider bg-gradient-to-r from-amber-400 via-amber-300 to-amber-500 bg-clip-text text-transparent drop-shadow-[0_0_12px_rgba(245,158,11,0.4)] font-mono">
|
||||
RIPPY
|
||||
</span>
|
||||
<span className="px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-widest bg-amber-500/15 text-amber-400 border border-amber-500/30 hidden sm:inline-block">
|
||||
CINEMA OS
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="px-3 mb-2">
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
className="w-full flex items-center justify-between px-3 py-2 rounded-xl bg-slate-800/50 hover:bg-slate-800 text-slate-400 hover:text-slate-200 border border-slate-800 transition-all duration-200 text-xs font-medium"
|
||||
title={theme === 'dark' ? 'Light Mode aktivieren' : 'Dark Mode aktivieren'}
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
{theme === 'dark' ? <Moon size={15} className="text-amber-400" /> : <Sun size={15} className="text-amber-500" />}
|
||||
<span>{theme === 'dark' ? 'Dark Mode' : 'Light Mode'}</span>
|
||||
</span>
|
||||
<span className="text-[10px] uppercase tracking-wider px-1.5 py-0.5 rounded bg-slate-700/60 text-slate-300">
|
||||
{theme}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<nav className="flex-1 px-3 py-2 space-y-1.5">
|
||||
{/* Central Nav Tabs */}
|
||||
<nav className="flex items-center gap-1.5 bg-[#121827] p-1.5 rounded-2xl border border-slate-800 shadow-inner">
|
||||
{navItems.map((item) => {
|
||||
const isActive = page === item.id
|
||||
const Icon = item.icon
|
||||
const isActive = currentPage === item.id
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => setPage(item.id as Page)}
|
||||
className={`w-full flex items-center gap-3 px-3.5 py-2.5 rounded-xl transition-all duration-200 font-medium text-sm ${
|
||||
onClick={() => setCurrentPage(item.id)}
|
||||
className={`flex items-center gap-2 px-4 py-2 rounded-xl text-xs font-semibold transition-all duration-200 ${
|
||||
isActive
|
||||
? 'bg-gradient-to-r from-amber-500/20 via-slate-800 to-slate-800/80 text-amber-300 border-l-4 border-amber-500 shadow-md shadow-amber-500/10'
|
||||
: 'text-slate-400 hover:bg-slate-800/50 hover:text-slate-200 border-l-4 border-transparent'
|
||||
? 'bg-amber-500 text-slate-950 shadow-md shadow-amber-500/20 font-bold scale-[1.02]'
|
||||
: 'text-slate-400 hover:text-slate-100 hover:bg-slate-800/60'
|
||||
}`}
|
||||
>
|
||||
<item.icon size={19} className={isActive ? 'text-amber-400' : 'text-slate-400'} />
|
||||
<Icon size={15} className={isActive ? 'text-slate-950' : 'text-slate-400'} />
|
||||
<span>{item.label}</span>
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</nav>
|
||||
|
||||
<footer className="px-4 py-4 text-center text-[11px] text-slate-500 border-t border-slate-800/80">
|
||||
Created with <span className="text-rose-400">❤️</span> by LucyAI, Claude & KrBrZ
|
||||
</footer>
|
||||
</aside>
|
||||
|
||||
{/* Main Content Area */}
|
||||
<main className="flex-1 overflow-auto bg-slate-50 dark:bg-slate-950">
|
||||
<div className="max-w-7xl mx-auto p-6 md:p-8">
|
||||
{page === 'dashboard' && <Dashboard />}
|
||||
{page === 'anleitung' && <AnleitungPage />}
|
||||
{page === 'logs' && <LogsPage />}
|
||||
{page === 'settings' && <SettingsPage />}
|
||||
{/* Right Action: Theme Toggle */}
|
||||
<div className="flex items-center gap-3">
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
className="p-2 rounded-xl border border-slate-800 bg-[#121827] text-amber-400 hover:border-amber-500/40 hover:bg-slate-800 transition-all"
|
||||
title={theme === 'dark' ? 'Light Mode aktivieren' : 'Dark Mode aktivieren'}
|
||||
>
|
||||
{theme === 'dark' ? <Moon size={16} /> : <Sun size={16} />}
|
||||
</button>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{/* Main Content Viewport */}
|
||||
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
||||
{currentPage === 'dashboard' && <Dashboard />}
|
||||
{currentPage === 'anleitung' && <AnleitungPage />}
|
||||
{currentPage === 'logs' && <LogsPage />}
|
||||
{currentPage === 'settings' && <SettingsPage />}
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
|
||||
Reference in New Issue
Block a user