108 lines
4.0 KiB
TypeScript
Executable File
108 lines
4.0 KiB
TypeScript
Executable File
export const dynamic = 'force-dynamic';
|
|
|
|
import { getTeacherCourses } from "./actions"; // Import the server action
|
|
import Link from "next/link";
|
|
import { redirect } from "next/navigation";
|
|
import { requireTeacher } from "@/lib/auth/requireTeacher";
|
|
import { logger } from "@/lib/logger";
|
|
|
|
export default async function TeacherDashboardPage() {
|
|
try {
|
|
// 1. Auth Check (Double protection)
|
|
// We log the attempt
|
|
logger.info("Accessing Teacher Dashboard");
|
|
|
|
// We wrap requireTeacher to catch potential DB/Supabase connection errors
|
|
let user;
|
|
try {
|
|
user = await requireTeacher();
|
|
} catch (authError) {
|
|
logger.error("requireTeacher failed with exception", authError);
|
|
throw authError;
|
|
}
|
|
|
|
if (!user) {
|
|
logger.info("User not authorized as teacher, redirecting");
|
|
redirect("/auth/login?role=teacher");
|
|
}
|
|
|
|
|
|
// 2. Fetch Data
|
|
const { success, data: courses, error } = await getTeacherCourses();
|
|
|
|
return (
|
|
<div className="p-6 max-w-7xl mx-auto">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between mb-8">
|
|
<div>
|
|
<h1 className="text-2xl font-bold tracking-tight">Panel del Profesor</h1>
|
|
<p className="text-gray-500">Gestiona tus cursos y contenidos.</p>
|
|
</div>
|
|
<Link
|
|
href="/teacher/courses/new"
|
|
className="bg-black text-white px-4 py-2 rounded-md hover:bg-gray-800 transition-colors"
|
|
>
|
|
+ Nuevo Curso
|
|
</Link>
|
|
</div>
|
|
|
|
{/* Error State */}
|
|
{!success && (
|
|
<div className="bg-red-50 text-red-600 p-4 rounded-md border border-red-100">
|
|
Error: {error}
|
|
</div>
|
|
)}
|
|
|
|
{/* List State */}
|
|
{success && courses && (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{courses.length === 0 ? (
|
|
// Empty State
|
|
<div className="col-span-full text-center py-12 border-2 border-dashed border-gray-200 rounded-lg">
|
|
<h3 className="mt-2 text-sm font-semibold text-gray-900">No hay cursos</h3>
|
|
<p className="mt-1 text-sm text-gray-500">Empieza creando tu primer curso de Inglés Jurídico.</p>
|
|
</div>
|
|
) : (
|
|
// Course Cards
|
|
courses.map((course) => (
|
|
<Link
|
|
key={course.id}
|
|
href={`/teacher/courses/${course.slug}`}
|
|
className="block group"
|
|
>
|
|
<div className="border border-gray-200 rounded-lg p-5 hover:border-black transition-all bg-white shadow-sm hover:shadow-md">
|
|
<div className="flex justify-between items-start mb-4">
|
|
<span className={`text-xs font-medium px-2 py-1 rounded-full ${course.status === 'PUBLISHED' ? 'bg-green-100 text-green-700' : 'bg-yellow-100 text-yellow-700'
|
|
}`}>
|
|
{course.status === 'PUBLISHED' ? 'Publicado' : 'Borrador'}
|
|
</span>
|
|
<span className="text-sm font-bold text-gray-900">
|
|
${course.price.toString()}
|
|
</span>
|
|
</div>
|
|
|
|
<h3 className="font-semibold text-lg text-gray-900 group-hover:text-blue-600 mb-1">
|
|
{course.title as string}
|
|
</h3>
|
|
<p className="text-sm text-gray-500 mb-4">
|
|
{course.level}
|
|
</p>
|
|
|
|
<div className="flex items-center gap-4 text-xs text-gray-500 border-t pt-4">
|
|
<span>📚 {course._count.modules} Módulos</span>
|
|
<span>👥 {course._count.enrollments} Alumnos</span>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
))
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
} catch (error) {
|
|
logger.error("Critical error in TeacherDashboardPage", error);
|
|
throw error;
|
|
}
|
|
}
|