mirror of
https://github.com/nihonbuzz/nihonbuzz-academy.git
synced 2026-01-26 05:25:37 +07:00
feat: Implement a new course learning system with dedicated layouts, lesson playback, and Spaced Repetition System (SRS) functionality.
This commit is contained in:
169
resources/js/Layouts/DashboardLayout.tsx
Normal file
169
resources/js/Layouts/DashboardLayout.tsx
Normal file
@@ -0,0 +1,169 @@
|
||||
import { Link, usePage } from '@inertiajs/react';
|
||||
import { PropsWithChildren, ReactNode, useState } from 'react';
|
||||
import {
|
||||
LayoutDashboard,
|
||||
BookOpen,
|
||||
Layers,
|
||||
Trophy,
|
||||
User,
|
||||
Settings,
|
||||
LogOut,
|
||||
Menu,
|
||||
Search,
|
||||
Bell,
|
||||
Plus,
|
||||
Play
|
||||
} from 'lucide-react';
|
||||
import { Sheet, SheetContent, SheetTrigger } from '@/Components/ui/sheet';
|
||||
import { Button } from '@/Components/ui/button';
|
||||
import { Avatar, AvatarFallback, AvatarImage } from '@/Components/ui/avatar';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { ScrollArea } from '@/Components/ui/scroll-area';
|
||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/Components/ui/dropdown-menu';
|
||||
import { ModeToggle } from '@/Components/ModeToggle';
|
||||
|
||||
export default function DashboardLayout({
|
||||
header,
|
||||
children,
|
||||
}: PropsWithChildren<{ header?: ReactNode }>) {
|
||||
const user = usePage().props.auth.user;
|
||||
const { url } = usePage();
|
||||
|
||||
const navItems = [
|
||||
{ label: 'Dashboard', icon: LayoutDashboard, href: route('dashboard'), active: route().current('dashboard') },
|
||||
{ label: 'Study (SRS)', icon: BookOpen, href: route('srs.index'), active: route().current('srs.*') },
|
||||
{ label: 'Decks', icon: Layers, href: route('courses.index'), active: route().current('courses.index') },
|
||||
{ label: 'Leaderboard', icon: Trophy, href: '#', active: false },
|
||||
{ label: 'Profile', icon: User, href: route('profile.edit'), active: route().current('profile.edit') },
|
||||
];
|
||||
|
||||
const SidebarContent = () => (
|
||||
<div className="flex flex-col h-full bg-white dark:bg-[#0a0a0b] border-r border-gray-200 dark:border-white/5 p-6 transition-colors duration-300">
|
||||
{/* Logo */}
|
||||
<div className="flex items-center gap-3 mb-8">
|
||||
<div className="size-10 bg-[#FF4500] rounded flex items-center justify-center text-white font-bold text-xl shadow-[0_0_15px_rgba(255,68,0,0.3)]">
|
||||
N
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<h1 className="text-gray-900 dark:text-white text-base font-bold leading-none tracking-tight">NIHONBUZZ</h1>
|
||||
<p className="text-[#FF4500] text-[10px] font-bold tracking-[0.2em] uppercase mt-0.5">Academy</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Nav Links */}
|
||||
<nav className="space-y-2 flex-1">
|
||||
{navItems.map((item, index) => (
|
||||
<Link
|
||||
key={index}
|
||||
href={item.href}
|
||||
className={cn(
|
||||
"flex items-center gap-3 px-3 py-2.5 rounded-lg text-sm font-medium transition-all duration-200",
|
||||
item.active
|
||||
? "bg-[#FF4500]/10 text-[#FF4500] border border-[#FF4500]/20"
|
||||
: "text-gray-500 dark:text-white/60 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-white/5"
|
||||
)}
|
||||
>
|
||||
<item.icon size={20} className={cn(item.active ? "text-[#FF4500]" : "text-gray-400 dark:text-white/60")} />
|
||||
<span>{item.label}</span>
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
{/* Profile Section (Bottom) */}
|
||||
<div className="space-y-4 pt-6 border-t border-gray-200 dark:border-white/5">
|
||||
<div className="flex items-center gap-3">
|
||||
<Avatar className="h-10 w-10 border border-gray-200 dark:border-white/10 rounded">
|
||||
<AvatarImage src={user.avatar} />
|
||||
<AvatarFallback className="bg-[#FF4500] text-white">
|
||||
{user.name.charAt(0)}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-bold text-gray-900 dark:text-white truncate">{user.name}</p>
|
||||
<p className="text-xs text-gray-500 dark:text-white/40 truncate">Level 12 Scholar</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mini Progress Bar */}
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex justify-between text-[10px] font-bold uppercase tracking-wider text-gray-500 dark:text-white/40">
|
||||
<span>XP Progress</span>
|
||||
<span>85%</span>
|
||||
</div>
|
||||
<div className="h-1 w-full bg-gray-100 dark:bg-white/5 rounded-full overflow-hidden">
|
||||
<div className="h-full bg-[#FF4500]" style={{ width: '85%' }}></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Link
|
||||
href={route('profile.edit')}
|
||||
className="flex items-center gap-2 text-gray-500 dark:text-white/40 hover:text-gray-900 dark:hover:text-white text-xs font-medium transition-colors cursor-pointer w-full"
|
||||
>
|
||||
<Settings size={14} />
|
||||
Settings
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#f8f6f5] dark:bg-[#0a0a0b] font-sans selection:bg-[#FF4500]/30 flex transition-colors duration-300">
|
||||
|
||||
{/* Desktop Sidebar */}
|
||||
<aside className="hidden lg:block w-64 flex-shrink-0 fixed inset-y-0 left-0 z-50">
|
||||
<SidebarContent />
|
||||
</aside>
|
||||
|
||||
{/* Mobile Header / Sheet */}
|
||||
<header className="lg:hidden fixed top-0 left-0 right-0 h-16 bg-white/80 dark:bg-[#0a0a0b]/80 backdrop-blur-md border-b border-gray-200 dark:border-white/5 flex items-center justify-between px-4 z-50">
|
||||
<div className="flex items-center gap-3">
|
||||
<Sheet>
|
||||
<SheetTrigger asChild>
|
||||
<Button variant="ghost" size="icon" className="text-gray-900 dark:text-white">
|
||||
<Menu size={20} />
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side="left" className="p-0 border-r-0 bg-white dark:bg-[#0a0a0b] w-64">
|
||||
<SidebarContent />
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
<div className="font-bold text-gray-900 dark:text-white tracking-tight">NIHONBUZZ</div>
|
||||
</div>
|
||||
<Avatar className="h-8 w-8">
|
||||
<AvatarImage src={user.avatar} />
|
||||
<AvatarFallback>{user.name.charAt(0)}</AvatarFallback>
|
||||
</Avatar>
|
||||
</header>
|
||||
|
||||
{/* Main Content */}
|
||||
<main className="flex-1 lg:pl-64 flex flex-col min-h-screen pt-16 lg:pt-0 transition-all">
|
||||
|
||||
{/* Desktop Topbar */}
|
||||
<header className="hidden lg:flex h-16 border-b border-gray-200 dark:border-white/5 items-center justify-between px-8 bg-white/50 dark:bg-[#0a0a0b]/50 backdrop-blur-md sticky top-0 z-40">
|
||||
<div className="flex items-center gap-2 text-sm text-gray-500 dark:text-white/40">
|
||||
<span>Dashboard</span>
|
||||
<span className="text-gray-300 dark:text-white/20">/</span>
|
||||
<span className="text-gray-900 dark:text-white font-medium">Home</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-4">
|
||||
<Button className="bg-[#FF4500] hover:bg-[#FF4500]/90 text-white font-bold h-9 rounded shadow-[0_0_15px_rgba(255,68,0,0.3)] transition-all">
|
||||
<Plus size={16} className="mr-2" />
|
||||
Quick Add
|
||||
</Button>
|
||||
|
||||
<Button variant="outline" size="icon" className="h-9 w-9 border-gray-200 dark:border-white/10 bg-transparent text-gray-500 dark:text-white/60 hover:text-gray-900 dark:hover:text-white hover:bg-gray-100 dark:hover:bg-white/5">
|
||||
<Bell size={20} />
|
||||
</Button>
|
||||
|
||||
<ModeToggle />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="p-4 sm:p-6 lg:p-8 max-w-7xl mx-auto w-full">
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user