feat: update CMS service handlers and main application logic.

This commit is contained in:
2025-12-22 13:54:35 -03:00
parent 57b8d7c0a1
commit 32f71852d9
59 changed files with 9125 additions and 59 deletions
+139
View File
@@ -0,0 +1,139 @@
"use client";
import { useEffect, useState } from "react";
import { lmsApi, Course } from "@/lib/api";
import Link from "next/link";
import { useAuth } from "@/context/AuthContext";
import { useRouter } from "next/navigation";
import { Rocket, CheckCircle2, ArrowRight, Star } from "lucide-react";
export default function CatalogPage() {
const [courses, setCourses] = useState<Course[]>([]);
const [enrollments, setEnrollments] = useState<string[]>([]);
const [loading, setLoading] = useState(true);
const { user } = useAuth();
const router = useRouter();
useEffect(() => {
const fetchData = async () => {
try {
const coursesData = await lmsApi.getCatalog();
setCourses(coursesData);
if (user) {
const enrollmentData = await lmsApi.getEnrollments(user.id);
setEnrollments(enrollmentData.map(e => e.course_id));
}
} catch (err) {
console.error(err);
} finally {
setLoading(false);
}
};
fetchData();
}, [user]);
const handleEnroll = async (courseId: string) => {
if (!user) {
router.push("/auth/login");
return;
}
try {
await lmsApi.enroll(courseId, user.id);
setEnrollments(prev => [...prev, courseId]);
} catch (err) {
console.error("Enrollment failed", err);
}
};
if (loading) {
return (
<div className="max-w-7xl mx-auto px-6 py-20">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{[1, 2, 3].map(i => (
<div key={i} className="h-80 glass-card animate-pulse bg-white/5 border-white/5 rounded-3xl"></div>
))}
</div>
</div>
);
}
return (
<div className="max-w-7xl mx-auto px-6 py-20">
<div className="mb-20 flex flex-col md:flex-row md:items-end justify-between gap-8">
<div className="space-y-4">
<div className="flex items-center gap-2 text-[10px] font-black uppercase tracking-[0.3em] text-blue-500">
<Star size={14} className="fill-blue-500" />
<span>Premier Curriculum</span>
</div>
<h1 className="text-6xl font-black tracking-tighter leading-none">
Explore <span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-indigo-600">Courses</span>
</h1>
<p className="text-gray-500 font-medium max-w-xl text-lg">
Master the skills of the future with our high-fidelity educational content.
</p>
</div>
{!user && (
<Link href="/auth/register" className="btn-premium !bg-white !text-black shadow-none !px-8">
Get Started Free
</Link>
)}
</div>
{courses.length === 0 ? (
<div className="py-20 text-center glass-card border-dashed border-white/10 rounded-3xl bg-white/[0.01]">
<p className="text-gray-500 font-bold uppercase tracking-widest">No courses published yet.</p>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
{courses.map((course) => {
const isEnrolled = enrollments.includes(course.id);
return (
<div key={course.id} className="glass-card group relative overflow-hidden h-full flex flex-col p-8 border-white/5 bg-white/[0.02] hover:bg-white/[0.04] transition-all duration-500 rounded-3xl">
<div className="mb-8 flex items-start justify-between">
<div className="w-14 h-14 rounded-2xl bg-gradient-to-br from-blue-600 to-indigo-700 flex items-center justify-center shadow-2xl shadow-blue-500/20 group-hover:scale-110 transition-transform duration-500">
<Rocket size={24} className="text-white fill-white/10" />
</div>
{isEnrolled && (
<span className="text-[10px] font-black uppercase tracking-widest px-3 py-1 rounded-full bg-green-500/10 text-green-400 border border-green-500/20">
Enrolled
</span>
)}
</div>
<h2 className="text-2xl font-black text-white mb-4 leading-tight group-hover:text-blue-400 transition-colors">
{course.title}
</h2>
<div className="flex-1">
<p className="text-gray-500 text-sm font-medium line-clamp-3 mb-10 leading-relaxed">
{course.description || "In-depth curriculum covering foundational principles to advanced mastery, crafted by industry veterans."}
</p>
</div>
<div className="pt-8 border-t border-white/5 flex items-center justify-between mt-auto">
{isEnrolled ? (
<Link href={`/courses/${course.id}`} className="btn-premium w-full !bg-blue-600/10 !text-blue-400 border border-blue-500/20 hover:!bg-blue-600/20 !shadow-none gap-2">
Continue Learning <ArrowRight size={16} />
</Link>
) : (
<button
onClick={() => handleEnroll(course.id)}
className="btn-premium w-full group-hover:scale-[1.02] transition-transform flex items-center justify-center gap-2 group/btn"
>
<CheckCircle2 size={18} className="text-white/50 group-hover/btn:text-white transition-colors" />
Enroll for Free
</button>
)}
</div>
</div>
);
})}
</div>
)}
</div>
);
}