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.
) : (
)}
{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" && (
)}
);
}