"use client"; import { useState, useEffect } from "react"; import { useAuth } from "@/lib/AuthContext"; import { useRouter } from "next/navigation"; import { Lock, Book, Headphones, Award, Sparkles, } from "lucide-react"; /* ------------------------------------------------------------------ */ /* Types */ /* ------------------------------------------------------------------ */ interface CourseEnrollment { progress: number; completed: boolean; } interface LearnCourse { id: string; slug: string; title: string; author: string; description: string; imageUrl: string | null; moduleCount: number; totalMinutes: number; difficulty: string; priceFlh: number | null; priceUsd: number | null; subscriptionOnly: boolean; enrolled: boolean | null; enrollment?: CourseEnrollment; } /* ------------------------------------------------------------------ */ /* Helpers */ /* ------------------------------------------------------------------ */ const DIFFICULTY_STYLES: Record = { beginner: { label: "Beginner", classes: "bg-green-900/30 text-[#00C48C] border border-green-800/30", }, intermediate: { label: "Intermediate", classes: "bg-amber-900/30 text-amber-400 border border-amber-800/30", }, advanced: { label: "Advanced", classes: "bg-red-900/30 text-red-400 border border-red-800/30", }, }; function difficultyLabel(diff: string): { label: string; classes: string } { return DIFFICULTY_STYLES[diff] ?? { label: diff, classes: "bg-gray-800/40 text-gray-400 border border-gray-700/30", }; } function formatPrice(course: LearnCourse): string { if (course.subscriptionOnly) return "Learn Pass"; if (course.priceFlh === null && course.priceUsd === null) return "FREE"; if (course.priceUsd !== null) return `$${course.priceUsd.toFixed(2)}`; if (course.priceFlh !== null) return `FLH ${course.priceFlh.toLocaleString()}`; return "FREE"; } function isFree(course: LearnCourse): boolean { if (course.subscriptionOnly) return false; return course.priceFlh === null && course.priceUsd === null; } /* ------------------------------------------------------------------ */ /* Placeholder Thumbnail */ /* ------------------------------------------------------------------ */ const COURSE_VISUALS: Record = { // A few known slugs get themed visuals default: { icon: "📖", bg: "from-[#C9A84C]/20 to-[#C9A84C]/5" }, }; function courseVisual(slug: string): { icon: string; bg: string } { return COURSE_VISUALS[slug] ?? COURSE_VISUALS.default; } /* ------------------------------------------------------------------ */ /* Page Component */ /* ------------------------------------------------------------------ */ export default function LearnCatalogPage() { const { user, token, loading } = useAuth(); const router = useRouter(); const [courses, setCourses] = useState([]); const [fetching, setFetching] = useState(true); const [error, setError] = useState(null); // Redirect to auth if not logged in useEffect(() => { if (!loading && !token) router.push("/auth"); }, [loading, token, router]); // Fetch courses useEffect(() => { if (!token) return; setFetching(true); setError(null); fetch("/mobile/api/learn/courses", { headers: { Authorization: `Bearer ${token}` }, }) .then((r) => { if (!r.ok) throw new Error("Failed to load courses"); return r.json(); }) .then((data: { courses: LearnCourse[] }) => setCourses(data.courses || []) ) .catch((err: Error) => setError(err.message)) .finally(() => setFetching(false)); }, [token]); // ── Loading state ── if (loading) { return (
); } if (!user) return null; // ── Guard: token present but no user yet ── if (!token) return null; // ── Enrolled / locked separation ── const enrolled = courses.filter((c) => c.enrolled === true); const locked = courses.filter((c) => c.enrolled !== true); return (
{/* ── Header ── */}

Falah Learn

Micro learning courses. 5 minutes at a time.

{/* ── Error state ── */} {error && !fetching && (

{error}

)} {/* ── Loading skeleton ── */} {fetching && (
{[1, 2, 3].map((i) => (
))}
)} {/* ── Course grid ── */} {!fetching && !error && courses.length === 0 && (

No courses available yet

Check back soon for new micro learning content

)} {!fetching && !error && courses.length > 0 && ( <> {/* ── Enrolled courses section ── */} {enrolled.length > 0 && (

My Courses

{enrolled.map((course) => ( ))}
)} {/* ── All / locked courses section ── */}

{enrolled.length > 0 ? "More Courses" : "All Courses"}

{locked.map((course) => ( ))}
)} {/* ── Learn Pass subscription card ── */}
{/* Decorative glow */}

Learn Pass

Get unlimited access to all courses, audio lessons, and certificates with a Learn Pass subscription.

Audio lessons
Certificates
All courses
Get Learn Pass
); } /* ------------------------------------------------------------------ */ /* Course Card */ /* ------------------------------------------------------------------ */ function CourseCard({ course, enrolled, router, }: { course: LearnCourse; enrolled: boolean; router: ReturnType; }) { const vis = courseVisual(course.slug); const diff = difficultyLabel(course.difficulty); const handleClick = () => { if (enrolled) { router.push(`/learn/${course.slug}`); } }; return (
{ if (e.key === "Enter" || e.key === " ") { e.preventDefault(); router.push(`/learn/${course.slug}`); } } : undefined } > {/* Thumbnail placeholder */}
{vis.icon} {/* Lock overlay for locked courses */} {!enrolled && (
)} {/* Progress bar for enrolled courses */} {enrolled && course.enrollment && (
)}
{/* Content */}
{/* Title */}

{course.title}

{/* Author */}

{course.author}

{/* Meta row: difficulty badge + modules */}
{diff.label} {course.moduleCount} module{course.moduleCount !== 1 ? "s" : ""} {course.totalMinutes > 0 && ( ~{course.totalMinutes} min )}
{/* Price / CTA row */}
{formatPrice(course)} {!enrolled && ( e.stopPropagation()} > Get on iStore → )} {enrolled && ( {course.enrollment?.completed ? ( <> Completed ) : course.enrollment && (course.enrollment.progress ?? 0) > 0 ? ( <> {Math.round((course.enrollment.progress ?? 0) * 100)}% ) : ( <> Start )} )}
); }