mirror of
https://github.com/nihonbuzz/nihonbuzz-academy.git
synced 2026-01-26 05:25:37 +07:00
187 lines
11 KiB
TypeScript
187 lines
11 KiB
TypeScript
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
|
|
import { Head, Link } from '@inertiajs/react';
|
|
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/Components/ui/card";
|
|
import { Avatar, AvatarFallback, AvatarImage } from "@/Components/ui/avatar";
|
|
import { Button } from "@/Components/ui/button";
|
|
import { BookOpen, Flame, GraduationCap, Trophy, Play } from "lucide-react";
|
|
import CourseCard from '@/Components/CourseCard';
|
|
|
|
interface DashboardProps {
|
|
stats: {
|
|
xp_points: number;
|
|
current_streak: number;
|
|
active_courses: number;
|
|
certificates: number;
|
|
srs_due: number;
|
|
srs_new: number;
|
|
};
|
|
activeCourses: Array<{
|
|
id: string;
|
|
title: string;
|
|
thumbnail: string;
|
|
level: string;
|
|
progress: number;
|
|
lessonsCount: number;
|
|
completedLessons: number;
|
|
slug: string;
|
|
}>;
|
|
user: {
|
|
name: string;
|
|
avatar: string;
|
|
rank: string;
|
|
xp_points?: number;
|
|
};
|
|
}
|
|
|
|
export default function Dashboard({
|
|
stats: propStats = { xp_points: 0, current_streak: 0, active_courses: 0, certificates: 0, srs_due: 0, srs_new: 0 },
|
|
activeCourses: propCourses = [],
|
|
user: propUser = { name: 'Student', avatar: '', rank: 'Genin' }
|
|
}: Partial<DashboardProps>) {
|
|
|
|
const activeCourses = propCourses ?? [];
|
|
const userData = propUser ?? { name: 'Student', avatar: '', rank: 'Genin' };
|
|
const stats = propStats ?? { xp_points: 0, current_streak: 0, active_courses: 0, certificates: 0, srs_due: 0, srs_new: 0 };
|
|
|
|
return (
|
|
<AuthenticatedLayout
|
|
header={
|
|
<div className="flex flex-col gap-1">
|
|
<h2 className="text-2xl font-bold tracking-tight">Dashboard Siswa</h2>
|
|
<p className="text-muted-foreground text-sm">Selamat datang kembali! Yuk, lanjutkan progres belajarmu hari ini.</p>
|
|
</div>
|
|
}
|
|
>
|
|
<Head title="Dashboard" />
|
|
|
|
<div className="space-y-8 animate-in fade-in duration-700">
|
|
{/* Stats Grid */}
|
|
<div className="grid gap-4 grid-cols-2 lg:grid-cols-4">
|
|
<Card className="shadow-none">
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Total XP</CardTitle>
|
|
<Trophy className="h-4 w-4 text-yellow-500" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-black">{stats.xp_points.toLocaleString()}</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card className="shadow-none">
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Streak</CardTitle>
|
|
<Flame className="h-4 w-4 text-orange-500" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-black">{stats.current_streak} Hari</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card className="shadow-none">
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Kursus</CardTitle>
|
|
<BookOpen className="h-4 w-4 text-blue-500" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-black">{stats.active_courses} Aktif</div>
|
|
</CardContent>
|
|
</Card>
|
|
<Card className="shadow-none">
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-[10px] font-bold uppercase tracking-wider text-muted-foreground">Sertifikat</CardTitle>
|
|
<GraduationCap className="h-4 w-4 text-green-500" />
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-black">{stats.certificates} Diraih</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
<div className="grid gap-8 lg:grid-cols-12">
|
|
{/* Main Content: Course List */}
|
|
<div className="lg:col-span-8 space-y-6">
|
|
<div className="flex items-center justify-between">
|
|
<h3 className="text-lg font-bold tracking-tight">Lanjutkan Belajar</h3>
|
|
<Link href="#" className="text-xs font-semibold text-primary hover:underline">Lihat Semua</Link>
|
|
</div>
|
|
|
|
<div className="grid sm:grid-cols-2 gap-6">
|
|
{activeCourses.length > 0 ? activeCourses.map((course, i) => (
|
|
<CourseCard key={i} {...course} />
|
|
)) : (
|
|
<div className="col-span-full border-2 border-dashed border-border/50 rounded-3xl py-12 flex flex-col items-center justify-center text-center px-6">
|
|
<div className="w-16 h-16 bg-muted rounded-full flex items-center justify-center mb-4">
|
|
<BookOpen className="text-muted-foreground" size={32} />
|
|
</div>
|
|
<h4 className="font-bold text-foreground">Belum ada kursus aktif</h4>
|
|
<p className="text-sm text-muted-foreground max-w-xs mt-1 mb-6">Mulai perjalanan belajarmu sekarang dengan memilih paket kursus yang tersedia.</p>
|
|
<Button className="rounded-full px-8">Explorasi Kursus</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Sidebar: Profile & SRS */}
|
|
<div className="lg:col-span-4 space-y-6">
|
|
<Card className="overflow-hidden border-border/50 bg-card/50 backdrop-blur-xl">
|
|
<div className="h-24 bg-gradient-to-br from-primary/20 via-primary/10 to-transparent dark:from-primary/30 dark:via-primary/5" />
|
|
<CardContent className="relative pt-0 px-6 pb-6">
|
|
<Avatar className="h-20 w-20 absolute -top-10 border-4 border-background ring-1 ring-border/20">
|
|
<AvatarImage src={userData.avatar} />
|
|
<AvatarFallback className="bg-primary text-primary-foreground font-bold">{userData.name.charAt(0)}</AvatarFallback>
|
|
</Avatar>
|
|
<div className="mt-12">
|
|
<h3 className="font-bold text-xl">{userData.name}</h3>
|
|
<p className="text-sm text-muted-foreground font-medium">Rank: <span className="text-primary italic font-bold">{userData.rank}</span></p>
|
|
</div>
|
|
<div className="mt-6 pt-6 border-t border-border/50 grid grid-cols-2 gap-4 text-center">
|
|
<div className="space-y-0.5">
|
|
<p className="text-xl font-black">{stats.xp_points.toLocaleString()}</p>
|
|
<p className="text-[10px] text-muted-foreground uppercase font-bold tracking-wider">Total XP</p>
|
|
</div>
|
|
<div className="space-y-0.5">
|
|
<p className="text-xl font-black">{stats.current_streak}</p>
|
|
<p className="text-[10px] text-muted-foreground uppercase font-bold tracking-wider">Streak</p>
|
|
</div>
|
|
</div>
|
|
<Button variant="outline" className="w-full mt-6 rounded-xl text-xs font-bold border-border/50">Edit Profil</Button>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* SRS Reminder */}
|
|
<Card className="bg-primary text-primary-foreground border-none shadow-xl shadow-primary/20 relative overflow-hidden group">
|
|
<div className="absolute top-0 right-0 p-8 opacity-10 transform translate-x-4 -translate-y-4 group-hover:scale-110 transition-transform duration-700">
|
|
<Flame size={120} />
|
|
</div>
|
|
<CardHeader className="relative z-10">
|
|
<CardTitle className="flex items-center gap-2 text-lg">
|
|
Hafalan (SRS)
|
|
</CardTitle>
|
|
<CardDescription className="text-primary-foreground/80 font-medium">
|
|
{stats.srs_due > 0 ? (
|
|
<>Ada <span className="font-black text-white">{stats.srs_due} kata</span> yang perlu diulas hari ini.</>
|
|
) : stats.srs_new > 0 ? (
|
|
<>Tidak ada ulasan, tapi ada <span className="font-black text-white">{stats.srs_new} kata baru</span> siap dipelajari!</>
|
|
) : (
|
|
<>Luar biasa! Semua hafalanmu sudah selesai untuk hari ini.</>
|
|
)}
|
|
</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="relative z-10">
|
|
<Button
|
|
variant="secondary"
|
|
className="w-full font-black text-primary rounded-xl h-11"
|
|
size="lg"
|
|
onClick={() => window.location.href = route('srs.index')}
|
|
>
|
|
{stats.srs_due > 0 ? 'Mulai Sesi Review' : 'Buka Koleksi Kata'}
|
|
<Play className="ml-2 w-4 h-4 fill-current" />
|
|
</Button>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</AuthenticatedLayout>
|
|
);
|
|
}
|
|
|