import { useCallback, useEffect, useMemo, type CSSProperties } from "react";
import { Link, useLocation, useNavigate, useParams } from "react-router-dom";
import { CourseCover, FloatingActionBar, Pill, ProgressBar } from "@stc/ui";
import type { CoursePayload, LessonPayload, Note, ProgressState } from "../../shared/types";
import { api, ensureStyles, fmtMinutes, fmtTime, lessonHref, refreshLibrary, toast, useApi, useCourse, useLook, useMe, useTrack } from "../api";
import { Check, Next, Prev } from "../icons";
import { CourseFrame, courseDone, moduleNumber } from "../shell/CourseFrame";
import { HeaderExtras } from "../shell/HeaderExtras";
import { LessonView, TitleText } from "../lesson/LessonView";
import { NoteItem } from "../lesson/notes";
function Problem({ title, children }: { title: string; children?: React.ReactNode }) {
ensureStyles();
return (
{title}
{children}
Back to the library
);
}
function CourseMissing({ status, signedIn }: { status: number; signedIn: boolean }) {
if (status === 404) {
return (
This course does not exist, or you do not have access to it.{signedIn ? "" : <> Signing in may help.>}
);
}
return The course could not be loaded. Try again in a moment.;
}
/** First lesson that is not done (in an unlocked module), or the first lesson. */
export function nextLesson(c: CoursePayload): { id: string; title: string } | null {
for (const m of c.modules) {
if (m.locked) break;
for (const l of m.lessons) if (c.progress[l.id]?.state !== "done") return l;
}
return c.modules[0]?.lessons[0] ?? null;
}
/* ---------- /c/:courseId ---------- */
export function CoursePage() {
const { courseId = "" } = useParams();
const { course, error } = useCourse(courseId);
const { user } = useMe();
const [look] = useLook();
useEffect(() => { if (course) document.title = `${course.title} – STCourses`; }, [course]);
if (error && !course) return ;
if (!course) return null;
const { done, total } = courseDone(course);
const next = nextLesson(course);
const started = Object.keys(course.progress).length > 0;
const minutes = course.modules.flatMap((m) => m.lessons).reduce((s, l) => s + (l.minutes ?? 0), 0);
return (
{course.title}
{course.authors.length ?
{course.authors.join(", ")}
: null}
{course.pitch ?
{course.pitch}
: null}
Lessons
{total}
{minutes ?
Time
About {fmtMinutes(minutes)}
: null}
{course.tracks.length > 1 ?
Tracks
{course.tracks.map((t) => t.label).join(", ")}
: null}
{course.gating === "sequential" ?
Order
Each module opens when the one before it is done
: null}
{course.signedIn ?
Progress
{done} of {total}
: null}
{course.status === "error" ?
This course has problems. {course.canEdit ? "The list in the sidebar names them." : "Some lessons may not open."}
: null}
{next ? {started ? "Continue" : "Start"}{started ? : {next.title.replace(/`/g, "")} : null} : null}
{!course.signedIn ? Sign in to keep your progress and notes. : null}