import { useEffect, useState } from "react"; import { useNavigate, useParams } from "react-router-dom"; import { ArrowLeft, Pencil, Clock, BookOpen, Layers, BadgeCheck, Tag, Star, Lock, ListChecks, BarChart2, Trophy, Users, Award, } from "lucide-react"; import { useCourses } from "@/contexts/AdminCoursesContext"; import { useDateFormat } from "@/hooks/useDateFormat"; import { PageMeta } from "@/contexts/MetadataContext"; import CourseReadingProgressList from "../../components/courses/CourseReadingProgressList"; import CourseBadge from "@/modules/admin/components/courses/CourseBadge"; import api from "@/utils/api.util"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Skeleton } from "@/components/ui/skeleton"; import { Separator } from "@/components/ui/separator"; // ─── Helpers ────────────────────────────────────────────────────────────────── function InfoRow({ label, children }) { return (
{label} {children ?? —}
); } function SectionCard({ icon: Icon, title, children }) { return (
{Icon && }

{title}

{children}
); } const LEVEL_BADGE = { beginner: "secondary", intermediate: "outline", advanced: "destructive", }; const SUBSCRIPTION_BADGE = { free: "secondary", }; function LoadingSkeleton() { return (
{[...Array(4)].map((_, i) => )}
); } // ─── Tab: Course Details ─────────────────────────────────────────────────────── function CourseDetailsTab({ course, loading, instructors, achievementKeys, achievementRegistry, badgeImageUrl }) { const { fmtDateTime } = useDateFormat(); if (loading && !course) return ; if (!course) return
Course not found.
; return (
{course.title} {course.description && ( {course.description} )}
{course.course_code ?? —} {course.order_index ?? 0}
{course.level ? ( {course.level} ) : null} {course.subscription ?? "free"}
{course.duration_formatted ?? (course.duration_seconds ? `${course.duration_seconds}s` : "—")}
{course.unitCount ?? course.units?.length ?? 0}
{course.lessonCount ?? 0}
{course.objectives?.length > 0 && (
    {course.objectives.map((obj, i) => (
  • {obj.text}
  • ))}
)} {instructors.length === 0 ? (

No instructors assigned.

) : (
    {instructors.map((inst, i) => { const fullName = inst.user?.personal_info?.name?.full_name ?? null; const email = inst.user?.email ?? null; return (
  • {inst.display_name}

    {(fullName || email) && (

    {fullName ?? email}

    )}
    #{i + 1}
  • ); })}
)}

Completion Badge

Label: Course Completion
Trigger: Pass course assessment
Type: Milestone achievement
Color: {course.badge_color ?? "purple"}
Mandatory

Achievements ({achievementKeys.length}/3)

{achievementKeys.length === 0 ? (

No achievements assigned.

) : (
    {achievementKeys.map((key) => { const ach = achievementRegistry.find((a) => a.key === key); return (
  • {ach?.type === "badge" ? : }
    {ach?.label ?? key} {ach?.description && (

    {ach.description}

    )}
    {ach?.type ?? "badge"}
  • ); })}
)}
{course.prerequisites?.length > 0 && (
    {course.prerequisites.map((p, i) => (
  • {p.ref_type} {p.title ?? `Untitled (ID: ${p.ref_id})`}
  • ))}
)} {course.roles?.length > 0 && (
{course.roles.map((r, i) => ( {r.text} ))}
)} {course.assessment && (
{course.assessment.title ?? "Untitled Assessment"} {course.assessment.passing_score ?? 70}% {course.assessment.time_limit_minutes ? `${course.assessment.time_limit_minutes} mins` : "No limit"}
)}
{course.creator?.full_name ?? course.createdBy ?? "—"} {course.updater?.full_name ?? course.updatedBy ?? "—"} {course.createdAt ? fmtDateTime(course.createdAt) : "—"} {course.updatedAt ? fmtDateTime(course.updatedAt) : "—"}
); } // ─── Tabs config ─────────────────────────────────────────────────────────────── const TABS = [ { key: "details", label: "Course Details", icon: BookOpen }, { key: "progress", label: "Reading Progress", icon: BarChart2 }, ]; // ─── Page ───────────────────────────────────────────────────────────────────── export default function ViewCourse() { const navigate = useNavigate(); const { courseId } = useParams(); const { fetchCourse, course, loading } = useCourses(); const [activeTab, setActiveTab] = useState("details"); const [instructors, setInstructors] = useState([]); const [achievementKeys, setAchievementKeys] = useState([]); const [achievementRegistry, setAchievementRegistry] = useState([]); const [badgeImageUrl, setBadgeImageUrl] = useState(null); useEffect(() => { fetchCourse(courseId); api.get(`/admin/courses/${courseId}/instructors`) .then(({ data }) => setInstructors(data.data?.data ?? data.data ?? [])) .catch(() => {}); api.get(`/admin/courses/${courseId}/achievements`) .then(({ data }) => { const rows = data?.data?.data ?? []; setAchievementKeys(rows.map((r) => r.achievement_key)); }) .catch(() => {}); api.get("/admin/achievements") .then(({ data }) => setAchievementRegistry(data.data ?? [])) .catch(() => {}); }, [courseId]); useEffect(() => { if (!course?.badge_asset_id) { setBadgeImageUrl(course?.badge_image_url ?? null); return; } const STREAM_BASE = `${import.meta.env.VITE_API_URL}/client/media/stream`; api.post("/admin/media/token", { asset_id: course.badge_asset_id }) .then(({ data }) => { const thumbUrl = data.data?.thumbnail_url; const token = data.data?.token; setBadgeImageUrl(thumbUrl ?? (token ? `${STREAM_BASE}/${token}` : null)); }) .catch(() => setBadgeImageUrl(course.badge_image_url ?? null)); }, [course?.badge_asset_id, course?.badge_image_url]); return (
{/* ── Sticky header ── */}

View Course

{course && (

{course.course_code ? `${course.course_code} — ` : ""}{course.title}

)}
{activeTab === "details" && ( )}
{/* Underline tabs */}
{TABS.map(({ key, label, icon: Icon }) => ( ))}
{/* ── Content ── */}
{activeTab === "details" && ( )} {activeTab === "progress" && ( )}
); }