269 lines
11 KiB
TypeScript
269 lines
11 KiB
TypeScript
/**
|
|
* File: page.tsx
|
|
* Created by: AI Assistant
|
|
* Date: 2025-11-29
|
|
* Purpose: Class detail page for kreatiVortex platform
|
|
* Part of: kreatiVortex - Platform Pembelajaran Tari Online
|
|
*/
|
|
|
|
'use client';
|
|
|
|
import { useEffect, useState } from 'react';
|
|
import { useParams } from 'next/navigation';
|
|
import Link from 'next/link';
|
|
import ActionButton from '@/components/ActionButton';
|
|
|
|
interface ClassMember {
|
|
id: string;
|
|
student: {
|
|
user: {
|
|
name: string;
|
|
image?: string;
|
|
};
|
|
};
|
|
}
|
|
|
|
interface ClassData {
|
|
id: string;
|
|
name: string;
|
|
description: string;
|
|
code: string;
|
|
educatorId: string;
|
|
educator: {
|
|
user: {
|
|
name: string;
|
|
image?: string;
|
|
};
|
|
};
|
|
isActive: boolean;
|
|
maxStudents?: number;
|
|
members: ClassMember[];
|
|
videos: any[];
|
|
assignments: any[];
|
|
createdAt: string;
|
|
updatedAt: string;
|
|
}
|
|
|
|
export default function ClassDetailPage() {
|
|
const params = useParams();
|
|
const [classData, setClassData] = useState<ClassData | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
const fetchClassData = async () => {
|
|
try {
|
|
const response = await fetch(`/api/classes/${params.id}`);
|
|
const result = await response.json();
|
|
|
|
if (result.success) {
|
|
setClassData(result.data);
|
|
} else {
|
|
setError(result.message || 'Failed to fetch class data');
|
|
}
|
|
} catch (err) {
|
|
setError('Error fetching class data');
|
|
console.error('Error:', err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
if (params.id) {
|
|
fetchClassData();
|
|
}
|
|
}, [params.id]);
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="max-w-6xl mx-auto">
|
|
<div className="flex justify-center items-center h-64">
|
|
<div className="text-white">Memuat data kelas...</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error || !classData) {
|
|
return (
|
|
<div className="max-w-6xl mx-auto">
|
|
<div className="flex justify-center items-center h-64">
|
|
<div className="text-red-400">Error: {error || 'Kelas tidak ditemukan'}</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="max-w-6xl mx-auto space-y-6">
|
|
<div className="flex justify-between items-center">
|
|
<Link href="/dashboard/classes" className="text-gray-400 hover:text-white flex items-center">
|
|
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 19l-7-7m0 0l7-7m-7 7h18" />
|
|
</svg>
|
|
Kembali ke Daftar Kelas
|
|
</Link>
|
|
<ActionButton variant="outline" size="sm">
|
|
Pengaturan Kelas
|
|
</ActionButton>
|
|
</div>
|
|
|
|
{/* Class Header */}
|
|
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-8 border border-white/20 relative overflow-hidden">
|
|
<div className="absolute top-0 right-0 p-4">
|
|
<span className={`px-3 py-1 text-sm rounded-full border ${
|
|
classData.isActive
|
|
? 'bg-green-900/50 text-green-200 border-green-500/30'
|
|
: 'bg-red-900/50 text-red-200 border-red-500/30'
|
|
}`}>
|
|
{classData.isActive ? 'Aktif' : 'Tidak Aktif'}
|
|
</span>
|
|
</div>
|
|
|
|
<h1 className="text-3xl font-bold text-white mb-2">{classData.name}</h1>
|
|
<p className="text-xl text-gold-400 mb-6">{classData.code}</p>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 text-sm text-gray-300">
|
|
<div className="flex items-center">
|
|
<svg className="w-5 h-5 mr-2 text-gold-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
|
|
</svg>
|
|
Pengajar: {classData.educator.user.name}
|
|
</div>
|
|
<div className="flex items-center">
|
|
<svg className="w-5 h-5 mr-2 text-gold-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 20h5v-2a3 3 0 00-5.356-1.857M17 20H7m10 0v-2c0-.656-.126-1.283-.356-1.857M7 20H2v-2a3 3 0 015.356-1.857M7 20v-2c0-.656.126-1.283.356-1.857m0 0a5.002 5.002 0 019.288 0M15 7a3 3 0 11-6 0 3 3 0 016 0zm6 3a2 2 0 11-4 0 2 2 0 014 0zM7 10a2 2 0 11-4 0 2 2 0 014 0z" />
|
|
</svg>
|
|
{classData.members.length} / {classData.maxStudents || '∞'} Siswa
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
{/* Main Content */}
|
|
<div className="lg:col-span-2 space-y-6">
|
|
{/* About */}
|
|
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-6 border border-white/20">
|
|
<h2 className="text-xl font-bold text-white mb-4">Tentang Kelas</h2>
|
|
<p className="text-gray-300 leading-relaxed">
|
|
{classData.description || 'Tidak ada deskripsi untuk kelas ini.'}
|
|
</p>
|
|
</div>
|
|
|
|
{/* Quick Actions */}
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<Link href={`/dashboard/forum/${classData.id}`} className="block">
|
|
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-6 border border-white/20 hover:bg-white/15 transition-colors text-center group">
|
|
<div className="w-12 h-12 bg-gold-400/20 rounded-full flex items-center justify-center mx-auto mb-3 group-hover:scale-110 transition-transform">
|
|
<svg className="w-6 h-6 text-gold-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z" />
|
|
</svg>
|
|
</div>
|
|
<h3 className="font-semibold text-white">Forum Kelas</h3>
|
|
</div>
|
|
</Link>
|
|
<Link href={`/dashboard/assignments?classId=${classData.id}`} className="block">
|
|
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-6 border border-white/20 hover:bg-white/15 transition-colors text-center group">
|
|
<div className="w-12 h-12 bg-gold-400/20 rounded-full flex items-center justify-center mx-auto mb-3 group-hover:scale-110 transition-transform">
|
|
<svg className="w-6 h-6 text-gold-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2v2a2 2 0 01-2 2M9 5a2 2 0 012-2v2a2 2 0 01-2 2m-3 7h8m-8 4h8m-8 4h8" />
|
|
</svg>
|
|
</div>
|
|
<h3 className="font-semibold text-white">Tugas Kelas</h3>
|
|
</div>
|
|
</Link>
|
|
</div>
|
|
|
|
{/* Statistics */}
|
|
<div className="grid grid-cols-3 gap-4">
|
|
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-4 border border-white/20 text-center">
|
|
<div className="text-2xl font-bold text-gold-400">{classData.videos.length}</div>
|
|
<div className="text-sm text-gray-300">Video</div>
|
|
</div>
|
|
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-4 border border-white/20 text-center">
|
|
<div className="text-2xl font-bold text-gold-400">{classData.assignments.length}</div>
|
|
<div className="text-sm text-gray-300">Tugas</div>
|
|
</div>
|
|
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-4 border border-white/20 text-center">
|
|
<div className="text-2xl font-bold text-gold-400">{classData.members.length}</div>
|
|
<div className="text-sm text-gray-300">Siswa</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Sidebar */}
|
|
<div className="lg:col-span-1 space-y-6">
|
|
{/* Class Members */}
|
|
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-6 border border-white/20">
|
|
<div className="flex justify-between items-center mb-4">
|
|
<h2 className="text-lg font-bold text-white">Anggota Kelas</h2>
|
|
<span className="text-gold-400 text-sm">{classData.members.length} siswa</span>
|
|
</div>
|
|
<div className="space-y-3">
|
|
{classData.members.slice(0, 5).map((member) => (
|
|
<div key={member.id} className="flex items-center space-x-3">
|
|
<div className="w-8 h-8 rounded-full bg-gold-400/20 flex items-center justify-center">
|
|
{member.student.user.image ? (
|
|
<img
|
|
src={member.student.user.image}
|
|
alt={member.student.user.name}
|
|
className="w-8 h-8 rounded-full object-cover"
|
|
/>
|
|
) : (
|
|
<span className="text-gold-400 text-xs font-bold">
|
|
{member.student.user.name.charAt(0).toUpperCase()}
|
|
</span>
|
|
)}
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-sm font-medium text-white truncate">
|
|
{member.student.user.name}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
{classData.members.length > 5 && (
|
|
<div className="text-center pt-2">
|
|
<button className="text-gold-400 text-sm hover:underline">
|
|
+{classData.members.length - 5} siswa lainnya
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Class Info */}
|
|
<div className="bg-white/10 backdrop-blur-sm rounded-xl p-6 border border-white/20">
|
|
<h2 className="text-lg font-bold text-white mb-4">Informasi Kelas</h2>
|
|
<div className="space-y-3 text-sm">
|
|
<div className="flex justify-between">
|
|
<span className="text-gray-400">Kode Kelas</span>
|
|
<span className="text-white font-medium">{classData.code}</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-gray-400">Status</span>
|
|
<span className={`font-medium ${
|
|
classData.isActive ? 'text-green-400' : 'text-red-400'
|
|
}`}>
|
|
{classData.isActive ? 'Aktif' : 'Tidak Aktif'}
|
|
</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-gray-400">Kapasitas</span>
|
|
<span className="text-white font-medium">
|
|
{classData.members.length} / {classData.maxStudents || '∞'}
|
|
</span>
|
|
</div>
|
|
<div className="flex justify-between">
|
|
<span className="text-gray-400">Dibuat</span>
|
|
<span className="text-white font-medium">
|
|
{new Date(classData.createdAt).toLocaleDateString('id-ID')}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |