@burdenoff/microfe-buildmyiq 2026.705.13 → 2026.706.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/buildmyiq/BuildmyiqRoot.d.ts.map +1 -1
- package/dist/buildmyiq/BuildmyiqRoot.js +57 -49
- package/dist/buildmyiq/BuildmyiqRoot.js.map +1 -1
- package/dist/buildmyiq/BuildmyiqRoutes.d.ts.map +1 -1
- package/dist/buildmyiq/BuildmyiqRoutes.js +15 -11
- package/dist/buildmyiq/BuildmyiqRoutes.js.map +1 -1
- package/dist/buildmyiq/components/Avatar.d.ts +15 -0
- package/dist/buildmyiq/components/Avatar.d.ts.map +1 -0
- package/dist/buildmyiq/components/Avatar.js +42 -0
- package/dist/buildmyiq/components/Avatar.js.map +1 -0
- package/dist/buildmyiq/components/Viz.d.ts +25 -0
- package/dist/buildmyiq/components/Viz.d.ts.map +1 -0
- package/dist/buildmyiq/components/Viz.js +78 -0
- package/dist/buildmyiq/components/Viz.js.map +1 -0
- package/dist/buildmyiq/components/index.d.ts +2 -0
- package/dist/buildmyiq/components/index.d.ts.map +1 -1
- package/dist/buildmyiq/components/index.js +2 -0
- package/dist/buildmyiq/pages/DashboardPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/DashboardPage.js +200 -98
- package/dist/buildmyiq/pages/DashboardPage.js.map +1 -1
- package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.js +262 -241
- package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.js.map +1 -1
- package/dist/buildmyiq/pages/assessments/AssessmentsPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/assessments/AssessmentsPage.js +142 -115
- package/dist/buildmyiq/pages/assessments/AssessmentsPage.js.map +1 -1
- package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.js +220 -156
- package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.js.map +1 -1
- package/dist/buildmyiq/pages/authoring/AuthoringPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/authoring/AuthoringPage.js +51 -46
- package/dist/buildmyiq/pages/authoring/AuthoringPage.js.map +1 -1
- package/dist/buildmyiq/pages/authoring/CourseBuilderPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js +581 -448
- package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js.map +1 -1
- package/dist/buildmyiq/pages/catalog/CatalogPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/catalog/CatalogPage.js +52 -42
- package/dist/buildmyiq/pages/catalog/CatalogPage.js.map +1 -1
- package/dist/buildmyiq/pages/catalog/CourseDetailPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/catalog/CourseDetailPage.js +422 -301
- package/dist/buildmyiq/pages/catalog/CourseDetailPage.js.map +1 -1
- package/dist/buildmyiq/pages/certificates/CertificatesPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/certificates/CertificatesPage.js +237 -151
- package/dist/buildmyiq/pages/certificates/CertificatesPage.js.map +1 -1
- package/dist/buildmyiq/pages/cohorts/CohortsPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/cohorts/CohortsPage.js +219 -138
- package/dist/buildmyiq/pages/cohorts/CohortsPage.js.map +1 -1
- package/dist/buildmyiq/pages/insights/InsightsPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/insights/InsightsPage.js +276 -152
- package/dist/buildmyiq/pages/insights/InsightsPage.js.map +1 -1
- package/dist/buildmyiq/pages/learning/LessonPlayerPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/learning/LessonPlayerPage.js +237 -174
- package/dist/buildmyiq/pages/learning/LessonPlayerPage.js.map +1 -1
- package/dist/buildmyiq/pages/learning/MyLearningPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/learning/MyLearningPage.js +307 -169
- package/dist/buildmyiq/pages/learning/MyLearningPage.js.map +1 -1
- package/dist/buildmyiq/pages/paths/PathsPage.d.ts +4 -0
- package/dist/buildmyiq/pages/paths/PathsPage.d.ts.map +1 -0
- package/dist/buildmyiq/pages/paths/PathsPage.js +187 -0
- package/dist/buildmyiq/pages/paths/PathsPage.js.map +1 -0
- package/dist/buildmyiq/pages/skills/SkillsPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/skills/SkillsPage.js +233 -140
- package/dist/buildmyiq/pages/skills/SkillsPage.js.map +1 -1
- package/dist/buildmyiq/pages/submissions/SubmissionsPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/submissions/SubmissionsPage.js +379 -275
- package/dist/buildmyiq/pages/submissions/SubmissionsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CourseDetailPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/catalog/CourseDetailPage.tsx"],"sourcesContent":["/**\n * CourseDetailPage — a single published course: hero, curriculum, assessments,\n * discussion + tags, and the enrol / continue-learning call to action.\n * @module buildmyiq/pages/catalog\n *\n * All data is live: `buiCourse` (hero + flat lessons), `buiListModules`\n * (curriculum), `buiListAssessments` (course exams), and `buiListEnrollments`\n * (to decide Enrol vs Continue). Enrolling calls `buiEnroll` then routes the\n * learner straight into their new enrollment. Every list has its own\n * loading / error / empty state.\n */\n\nimport type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport type { LucideIcon } from 'lucide-react';\nimport {\n ArrowLeft,\n BookOpen,\n ClipboardCheck,\n ClipboardList,\n Clock,\n FileText,\n GraduationCap,\n Layers,\n ListChecks,\n MousePointerClick,\n PlayCircle,\n Radio,\n Sparkles,\n Video,\n} from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n Section,\n LoadingView,\n ErrorView,\n EmptyState,\n MiniBar,\n BuiDiscussion,\n BuiTags,\n} from '../../components';\nimport { useBuildmyiq } from '../../context';\nimport {\n useBuiCourse,\n useBuiModules,\n useBuiAssessments,\n useBuiEnrollments,\n useBuiEnroll,\n} from '../../hooks';\nimport { formatHours, formatDuration, formatEnumLabel } from '../../utils/formatters';\nimport type { BuiLesson, BuiLessonType } from '../../types';\n\nconst COURSE_ENTITY = 'buildmyiq-course';\n\n/** Lesson-type → icon, so learners can scan a module at a glance. */\nconst LESSON_ICON: Record<BuiLessonType, LucideIcon> = {\n ARTICLE: FileText,\n VIDEO: Video,\n QUIZ: ListChecks,\n ASSIGNMENT: ClipboardList,\n INTERACTIVE: MousePointerClick,\n LIVE_SESSION: Radio,\n};\n\nconst LessonRow: FC<{ lesson: BuiLesson }> = ({ lesson }) => {\n const Icon = LESSON_ICON[lesson.type] ?? FileText;\n return (\n <li className=\"flex items-center gap-3 px-4 py-3\">\n <span className=\"flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-bg-sunken text-text-secondary\">\n <Icon className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">{lesson.title}</p>\n <p className=\"text-xs text-text-tertiary\">{formatEnumLabel(lesson.type)}</p>\n </div>\n {lesson.isPreview && (\n <span className=\"shrink-0 rounded-full bg-status-success-bg-subtle px-2 py-0.5 text-xs font-medium text-status-success-text\">\n Preview\n </span>\n )}\n {lesson.durationMin > 0 && (\n <span className=\"hidden shrink-0 items-center gap-1 text-xs tabular-nums text-text-secondary sm:inline-flex\">\n <Clock className=\"h-3.5 w-3.5\" /> {formatDuration(lesson.durationMin)}\n </span>\n )}\n </li>\n );\n};\n\nexport const CourseDetailPage: FC = () => {\n const { courseId } = useParams<{ courseId: string }>();\n const { currentUser, navigateTo } = useBuildmyiq();\n const learnerId = currentUser?.id;\n\n const courseQuery = useBuiCourse(courseId);\n const modulesQuery = useBuiModules(courseId);\n const assessmentsQuery = useBuiAssessments(courseId);\n const enrollmentsQuery = useBuiEnrollments({\n courseId,\n learnerId,\n enabled: Boolean(courseId && learnerId),\n });\n\n const enroll = useBuiEnroll();\n const [enrolError, setEnrolError] = useState<string | null>(null);\n\n const course = courseQuery.data ?? null;\n const modules = modulesQuery.data ?? [];\n const assessments = assessmentsQuery.data ?? [];\n const enrollments = enrollmentsQuery.data?.items ?? [];\n\n // An existing, non-terminal enrollment means \"Continue\" instead of \"Enrol\".\n const activeEnrollment = useMemo(\n () => enrollments.find((e) => e.status !== 'DROPPED' && e.status !== 'EXPIRED'),\n [enrollments]\n );\n\n // Lessons attached directly to the course (no module) — surfaced as their own group.\n const ungroupedLessons = useMemo(\n () => (course?.lessons ?? []).filter((lesson) => !lesson.moduleId),\n [course]\n );\n\n const totalLessons = course?.lessons?.length ?? 0;\n\n const handleEnrol = async (): Promise<void> => {\n if (!courseId || !learnerId) return;\n setEnrolError(null);\n try {\n const created = await enroll.mutateAsync({ courseId, learnerId });\n navigateTo(`learning/${created.id}`);\n } catch (err) {\n setEnrolError(err instanceof Error ? err.message : 'Could not enrol in this course.');\n }\n };\n\n // Top-level page states.\n if (!courseId) {\n return (\n <PageLayout title=\"Course\">\n <ErrorView title=\"Missing course\" message=\"No course was specified.\" />\n </PageLayout>\n );\n }\n\n if (courseQuery.isLoading) {\n return (\n <PageLayout title=\"Course\">\n <LoadingView label=\"Loading course…\" />\n </PageLayout>\n );\n }\n\n if (courseQuery.isError) {\n return (\n <PageLayout title=\"Course\">\n <ErrorView\n title=\"Couldn't load this course\"\n message={courseQuery.error instanceof Error ? courseQuery.error.message : undefined}\n onRetry={() => courseQuery.refetch()}\n />\n </PageLayout>\n );\n }\n\n if (!course) {\n return (\n <PageLayout title=\"Course\">\n <EmptyState\n illustration=\"empty-search\"\n title=\"Course not found\"\n description=\"This course may have been unpublished or removed.\"\n action={<Button onClick={() => navigateTo('catalog')}>Back to catalog</Button>}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout title={course.title}>\n <div className=\"mx-auto flex w-full max-w-5xl flex-col gap-8\">\n <button\n type=\"button\"\n onClick={() => navigateTo('catalog')}\n className=\"inline-flex w-fit items-center gap-1.5 text-sm text-text-secondary transition-colors hover:text-text-primary\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back to catalog\n </button>\n\n {/* Hero */}\n <GlassCard treatment=\"plain\" className=\"overflow-hidden\">\n <div className=\"grid grid-cols-1 lg:grid-cols-[minmax(0,2fr)_minmax(0,3fr)]\">\n <div className=\"relative h-48 w-full overflow-hidden bg-bg-sunken lg:h-full\">\n {course.cover?.downloadUrl ? (\n <img src={course.cover.downloadUrl} alt=\"\" className=\"h-full w-full object-cover\" />\n ) : (\n <div className=\"flex h-full min-h-[12rem] w-full items-center justify-center bg-[var(--color-accent-soft)]\">\n <BookOpen className=\"h-10 w-10 text-text-secondary opacity-60\" />\n </div>\n )}\n </div>\n\n <div className=\"flex flex-col gap-4 p-6\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <StatusBadge status={course.level} />\n {course.skill?.name && (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary\">\n <Sparkles className=\"h-3.5 w-3.5\" /> {course.skill.name}\n </span>\n )}\n </div>\n\n <div>\n <h1 className=\"text-2xl font-semibold text-text-primary\">{course.title}</h1>\n {course.summary && (\n <p className=\"mt-2 text-sm text-text-secondary\">{course.summary}</p>\n )}\n </div>\n\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-2 text-xs text-text-secondary\">\n <span className=\"inline-flex items-center gap-1\">\n <Layers className=\"h-3.5 w-3.5\" /> {modules.length} module\n {modules.length === 1 ? '' : 's'} · {totalLessons} lesson\n {totalLessons === 1 ? '' : 's'}\n </span>\n {course.estimatedHours > 0 && (\n <span className=\"inline-flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" /> {formatHours(course.estimatedHours)}\n </span>\n )}\n </div>\n\n {course.tags.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\">\n {course.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n\n {course.description && (\n <p className=\"whitespace-pre-wrap text-sm leading-relaxed text-text-secondary\">\n {course.description}\n </p>\n )}\n\n {/* Primary CTA — Continue if enrolled, else Enrol */}\n <div className=\"mt-auto flex flex-col gap-2 pt-2\">\n {activeEnrollment ? (\n <>\n <Button\n size=\"lg\"\n className=\"w-full sm:w-auto\"\n onClick={() => navigateTo(`learning/${activeEnrollment.id}`)}\n >\n <PlayCircle className=\"mr-2 h-4 w-4\" /> Continue learning\n </Button>\n <div className=\"flex items-center gap-2\">\n <MiniBar value={activeEnrollment.progressPct} className=\"flex-1\" />\n <span className=\"shrink-0 text-xs tabular-nums text-text-secondary\">\n {activeEnrollment.progressPct}% complete\n </span>\n </div>\n </>\n ) : (\n <>\n <Button\n size=\"lg\"\n className=\"w-full sm:w-auto\"\n onClick={handleEnrol}\n disabled={enroll.isPending || !learnerId}\n >\n <GraduationCap className=\"mr-2 h-4 w-4\" />\n {enroll.isPending ? 'Enrolling…' : 'Enrol in this course'}\n </Button>\n {!learnerId && (\n <p className=\"text-xs text-text-tertiary\">\n Sign in to your workspace to enrol.\n </p>\n )}\n {enrolError && <p className=\"text-xs text-status-error-text\">{enrolError}</p>}\n </>\n )}\n </div>\n </div>\n </div>\n </GlassCard>\n\n {/* Curriculum */}\n <Section title=\"Curriculum\" description=\"Modules and lessons in this course.\">\n {modulesQuery.isLoading ? (\n <LoadingView label=\"Loading curriculum…\" />\n ) : modulesQuery.isError ? (\n <ErrorView\n title=\"Couldn't load the curriculum\"\n message={modulesQuery.error instanceof Error ? modulesQuery.error.message : undefined}\n onRetry={() => modulesQuery.refetch()}\n />\n ) : modules.length === 0 && ungroupedLessons.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No lessons yet\"\n description=\"The instructor hasn't added any modules or lessons to this course.\"\n />\n ) : (\n <div className=\"flex flex-col gap-4\">\n {modules.map((module, index) => {\n const lessons = module.lessons ?? [];\n return (\n <div\n key={module.id}\n className=\"overflow-hidden rounded-xl border border-border-seam bg-bg-surface\"\n >\n <div className=\"border-b border-border-seam px-4 py-3\">\n <div className=\"flex items-baseline justify-between gap-3\">\n <h3 className=\"font-semibold text-text-primary\">\n <span className=\"mr-2 text-text-tertiary\">\n {String(index + 1).padStart(2, '0')}\n </span>\n {module.title}\n </h3>\n <span className=\"shrink-0 text-xs text-text-tertiary\">\n {lessons.length} lesson{lessons.length === 1 ? '' : 's'}\n </span>\n </div>\n {module.description && (\n <p className=\"mt-1 text-sm text-text-secondary\">{module.description}</p>\n )}\n </div>\n {lessons.length === 0 ? (\n <p className=\"px-4 py-3 text-sm text-text-tertiary\">\n No lessons in this module yet.\n </p>\n ) : (\n <ul className=\"divide-y divide-border-seam\">\n {lessons.map((lesson) => (\n <LessonRow key={lesson.id} lesson={lesson} />\n ))}\n </ul>\n )}\n </div>\n );\n })}\n\n {ungroupedLessons.length > 0 && (\n <div className=\"overflow-hidden rounded-xl border border-border-seam bg-bg-surface\">\n <div className=\"border-b border-border-seam px-4 py-3\">\n <h3 className=\"font-semibold text-text-primary\">Additional lessons</h3>\n </div>\n <ul className=\"divide-y divide-border-seam\">\n {ungroupedLessons.map((lesson) => (\n <LessonRow key={lesson.id} lesson={lesson} />\n ))}\n </ul>\n </div>\n )}\n </div>\n )}\n </Section>\n\n {/* Assessments */}\n <Section title=\"Assessments\" description=\"Quizzes and exams for this course.\">\n {assessmentsQuery.isLoading ? (\n <LoadingView label=\"Loading assessments…\" />\n ) : assessmentsQuery.isError ? (\n <ErrorView\n title=\"Couldn't load assessments\"\n message={\n assessmentsQuery.error instanceof Error ? assessmentsQuery.error.message : undefined\n }\n onRetry={() => assessmentsQuery.refetch()}\n />\n ) : assessments.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<ClipboardCheck className=\"h-9 w-9\" />}\n title=\"No assessments yet\"\n description=\"This course doesn't have any quizzes or exams attached.\"\n />\n ) : (\n <ul className=\"flex flex-col gap-3\">\n {assessments.map((assessment) => (\n <li\n key={assessment.id}\n className=\"flex flex-wrap items-center gap-3 rounded-xl border border-border-seam bg-bg-surface p-4\"\n >\n <span className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <ClipboardCheck className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate font-medium text-text-primary\">{assessment.title}</p>\n <div className=\"mt-1 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-secondary\">\n <span>Pass ≥ {assessment.passingScore}%</span>\n <span>\n {assessment.maxAttempts} attempt\n {assessment.maxAttempts === 1 ? '' : 's'}\n </span>\n {assessment.timeLimitMin > 0 && (\n <span className=\"inline-flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" />\n {formatDuration(assessment.timeLimitMin)}\n </span>\n )}\n </div>\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <StatusBadge status={assessment.type} />\n <StatusBadge status={assessment.status} />\n {assessment.status === 'PUBLISHED' && activeEnrollment && (\n <Button\n size=\"sm\"\n onClick={() => navigateTo(`assessments/${assessment.id}/take`)}\n >\n Take\n </Button>\n )}\n </div>\n </li>\n ))}\n </ul>\n )}\n </Section>\n\n {/* Tags (platform wspace-tags) */}\n <BuiTags entityType={COURSE_ENTITY} entityId={courseId} />\n\n {/* Discussion (platform wspace-conversations) */}\n <BuiDiscussion\n entityType={COURSE_ENTITY}\n entityId={courseId}\n description=\"Ask questions and discuss this course with your workspace.\"\n />\n </div>\n </PageLayout>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAuDA,IAAM,IAAgB,oBAGhB,IAAiD;CACrD,SAAS;CACT,OAAO;CACP,MAAM;CACN,YAAY;CACZ,aAAa;CACb,cAAc;CACf,EAEK,KAAwC,EAAE,gBAG5C,kBAAC,MAAD;CAAI,WAAU;WAAd;EACE,kBAAC,QAAD;GAAM,WAAU;aACd,kBAJO,EAAY,EAAO,SAAS,GAInC,EAAM,WAAU,WAAY,CAAA;GACvB,CAAA;EACP,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cAAkD,EAAO;IAAU,CAAA,EAChF,kBAAC,KAAD;IAAG,WAAU;cAA8B,EAAgB,EAAO,KAAK;IAAK,CAAA,CACxE;;EACL,EAAO,aACN,kBAAC,QAAD;GAAM,WAAU;aAA6G;GAEtH,CAAA;EAER,EAAO,cAAc,KACpB,kBAAC,QAAD;GAAM,WAAU;aAAhB;IACE,kBAAC,GAAD,EAAO,WAAU,eAAgB,CAAA;;IAAE,EAAe,EAAO,YAAY;IAChE;;EAEN;IAII,UAA6B;CACxC,IAAM,EAAE,gBAAa,GAAiC,EAChD,EAAE,gBAAa,kBAAe,GAAc,EAC5C,IAAY,GAAa,IAEzB,IAAc,EAAa,EAAS,EACpC,IAAe,EAAc,EAAS,EACtC,IAAmB,EAAkB,EAAS,EAC9C,IAAmB,EAAkB;EACzC;EACA;EACA,SAAS,GAAQ,KAAY;EAC9B,CAAC,EAEI,IAAS,GAAc,EACvB,CAAC,GAAY,KAAiB,EAAwB,KAAK,EAE3D,IAAS,EAAY,QAAQ,MAC7B,IAAU,EAAa,QAAQ,EAAE,EACjC,IAAc,EAAiB,QAAQ,EAAE,EACzC,IAAc,EAAiB,MAAM,SAAS,EAAE,EAGhD,IAAmB,QACjB,EAAY,MAAM,MAAM,EAAE,WAAW,aAAa,EAAE,WAAW,UAAU,EAC/E,CAAC,EAAY,CACd,EAGK,IAAmB,SAChB,GAAQ,WAAW,EAAE,EAAE,QAAQ,MAAW,CAAC,EAAO,SAAS,EAClE,CAAC,EAAO,CACT,EAEK,IAAe,GAAQ,SAAS,UAAU;AAuDhD,QAzCK,IAQD,EAAY,YAEZ,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD,EAAa,OAAM,mBAAoB,CAAA;EAC5B,CAAA,GAIb,EAAY,UAEZ,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD;GACE,OAAM;GACN,SAAS,EAAY,iBAAiB,QAAQ,EAAY,MAAM,UAAU,KAAA;GAC1E,eAAe,EAAY,SAAS;GACpC,CAAA;EACS,CAAA,GAIZ,IAcH,kBAAC,GAAD;EAAY,OAAO,EAAO;YACxB,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,UAAD;KACE,MAAK;KACL,eAAe,EAAW,UAAU;KACpC,WAAU;eAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,WAAY,CAAA,EAAA,mBAC1B;;IAGT,kBAAC,GAAD;KAAW,WAAU;KAAQ,WAAU;eACrC,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAO,OAAO,cACb,kBAAC,OAAD;QAAK,KAAK,EAAO,MAAM;QAAa,KAAI;QAAG,WAAU;QAA+B,CAAA,GAEpF,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD,EAAU,WAAU,4CAA6C,CAAA;QAC7D,CAAA;OAEJ,CAAA,EAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD,EAAa,QAAQ,EAAO,OAAS,CAAA,EACpC,EAAO,OAAO,QACb,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WACE,kBAAC,GAAD,EAAU,WAAU,eAAgB,CAAA;;WAAE,EAAO,MAAM;WAC9C;YAEL;;QAEN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;SAAI,WAAU;mBAA4C,EAAO;SAAW,CAAA,EAC3E,EAAO,WACN,kBAAC,KAAD;SAAG,WAAU;mBAAoC,EAAO;SAAY,CAAA,CAElE,EAAA,CAAA;QAEN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WACE,kBAAC,GAAD,EAAQ,WAAU,eAAgB,CAAA;;WAAE,EAAQ;WAAO;WAClD,EAAQ,WAAW,IAAI,KAAK;WAAI;WAAI;WAAa;WACjD,MAAiB,IAAI,KAAK;WACtB;aACN,EAAO,iBAAiB,KACvB,kBAAC,QAAD;UAAM,WAAU;oBAAhB;WACE,kBAAC,GAAD,EAAO,WAAU,eAAgB,CAAA;;WAAE,EAAY,EAAO,eAAe;WAChE;YAEL;;QAEL,EAAO,KAAK,SAAS,KACpB,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAO,KAAK,KAAK,MAChB,kBAAC,QAAD;UAEE,WAAU;oBAET;UACI,EAJA,EAIA,CACP;SACE,CAAA;QAGP,EAAO,eACN,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAO;SACN,CAAA;QAIN,kBAAC,OAAD;SAAK,WAAU;mBACZ,IACC,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,GAAD;UACE,MAAK;UACL,WAAU;UACV,eAAe,EAAW,YAAY,EAAiB,KAAK;oBAH9D,CAKE,kBAAC,GAAD,EAAY,WAAU,gBAAiB,CAAA,EAAA,qBAChC;aACT,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD;WAAS,OAAO,EAAiB;WAAa,WAAU;WAAW,CAAA,EACnE,kBAAC,QAAD;WAAM,WAAU;qBAAhB,CACG,EAAiB,aAAY,aACzB;aACH;YACL,EAAA,CAAA,GAEH,kBAAA,GAAA,EAAA,UAAA;UACE,kBAAC,GAAD;WACE,MAAK;WACL,WAAU;WACV,SArJA,YAA2B;AACzC,mBAAC,KAAY,CAAC,IAClB;eAAc,KAAK;AACnB,iBAAI;AAEF,gBAAW,aADK,MAAM,EAAO,YAAY;eAAE;eAAU;eAAW,CAAC,EAClC,KAAK;sBAC7B,GAAK;AACZ,gBAAc,aAAe,QAAQ,EAAI,UAAU,kCAAkC;;;;WA+IrE,UAAU,EAAO,aAAa,CAAC;qBAJjC,CAME,kBAAC,GAAD,EAAe,WAAU,gBAAiB,CAAA,EACzC,EAAO,YAAY,eAAe,uBAC5B;;UACR,CAAC,KACA,kBAAC,KAAD;WAAG,WAAU;qBAA6B;WAEtC,CAAA;UAEL,KAAc,kBAAC,KAAD;WAAG,WAAU;qBAAkC;WAAe,CAAA;UAC5E,EAAA,CAAA;SAED,CAAA;QACF;SACF;;KACI,CAAA;IAGZ,kBAAC,GAAD;KAAS,OAAM;KAAa,aAAY;eACrC,EAAa,YACZ,kBAAC,GAAD,EAAa,OAAM,uBAAwB,CAAA,GACzC,EAAa,UACf,kBAAC,GAAD;MACE,OAAM;MACN,SAAS,EAAa,iBAAiB,QAAQ,EAAa,MAAM,UAAU,KAAA;MAC5E,eAAe,EAAa,SAAS;MACrC,CAAA,GACA,EAAQ,WAAW,KAAK,EAAiB,WAAW,IACtD,kBAAC,GAAD;MACE,cAAa;MACb,OAAM;MACN,aAAY;MACZ,CAAA,GAEF,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACG,EAAQ,KAAK,GAAQ,MAAU;OAC9B,IAAM,IAAU,EAAO,WAAW,EAAE;AACpC,cACE,kBAAC,OAAD;QAEE,WAAU;kBAFZ,CAIE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,MAAD;WAAI,WAAU;qBAAd,CACE,kBAAC,QAAD;YAAM,WAAU;sBACb,OAAO,IAAQ,EAAE,CAAC,SAAS,GAAG,IAAI;YAC9B,CAAA,EACN,EAAO,MACL;cACL,kBAAC,QAAD;WAAM,WAAU;qBAAhB;YACG,EAAQ;YAAO;YAAQ,EAAQ,WAAW,IAAI,KAAK;YAC/C;aACH;aACL,EAAO,eACN,kBAAC,KAAD;UAAG,WAAU;oBAAoC,EAAO;UAAgB,CAAA,CAEtE;YACL,EAAQ,WAAW,IAClB,kBAAC,KAAD;SAAG,WAAU;mBAAuC;SAEhD,CAAA,GAEJ,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAQ,KAAK,MACZ,kBAAC,GAAD,EAAmC,WAAU,EAA7B,EAAO,GAAsB,CAC7C;SACC,CAAA,CAEH;UA9BC,EAAO,GA8BR;QAER,EAED,EAAiB,SAAS,KACzB,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,MAAD;SAAI,WAAU;mBAAkC;SAAuB,CAAA;QACnE,CAAA,EACN,kBAAC,MAAD;QAAI,WAAU;kBACX,EAAiB,KAAK,MACrB,kBAAC,GAAD,EAAmC,WAAU,EAA7B,EAAO,GAAsB,CAC7C;QACC,CAAA,CACD;SAEJ;;KAEA,CAAA;IAGV,kBAAC,GAAD;KAAS,OAAM;KAAc,aAAY;eACtC,EAAiB,YAChB,kBAAC,GAAD,EAAa,OAAM,wBAAyB,CAAA,GAC1C,EAAiB,UACnB,kBAAC,GAAD;MACE,OAAM;MACN,SACE,EAAiB,iBAAiB,QAAQ,EAAiB,MAAM,UAAU,KAAA;MAE7E,eAAe,EAAiB,SAAS;MACzC,CAAA,GACA,EAAY,WAAW,IACzB,kBAAC,GAAD;MACE,cAAa;MACb,MAAM,kBAAC,GAAD,EAAgB,WAAU,WAAY,CAAA;MAC5C,OAAM;MACN,aAAY;MACZ,CAAA,GAEF,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAY,KAAK,MAChB,kBAAC,MAAD;OAEE,WAAU;iBAFZ;QAIE,kBAAC,QAAD;SAAM,WAAU;mBACd,kBAAC,GAAD,EAAgB,WAAU,WAAY,CAAA;SACjC,CAAA;QACP,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBAA0C,EAAW;UAAU,CAAA,EAC5E,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,QAAD,EAAA,UAAA;YAAM;YAAQ,EAAW;YAAa;YAAQ,EAAA,CAAA;WAC9C,kBAAC,QAAD,EAAA,UAAA;YACG,EAAW;YAAY;YACvB,EAAW,gBAAgB,IAAI,KAAK;YAChC,EAAA,CAAA;WACN,EAAW,eAAe,KACzB,kBAAC,QAAD;YAAM,WAAU;sBAAhB,CACE,kBAAC,GAAD,EAAO,WAAU,eAAgB,CAAA,EAChC,EAAe,EAAW,aAAa,CACnC;;WAEL;YACF;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,GAAD,EAAa,QAAQ,EAAW,MAAQ,CAAA;UACxC,kBAAC,GAAD,EAAa,QAAQ,EAAW,QAAU,CAAA;UACzC,EAAW,WAAW,eAAe,KACpC,kBAAC,GAAD;WACE,MAAK;WACL,eAAe,EAAW,eAAe,EAAW,GAAG,OAAO;qBAC/D;WAEQ,CAAA;UAEP;;QACH;SAlCE,EAAW,GAkCb,CACL;MACC,CAAA;KAEC,CAAA;IAGV,kBAAC,GAAD;KAAS,YAAY;KAAe,UAAU;KAAY,CAAA;IAG1D,kBAAC,GAAD;KACE,YAAY;KACZ,UAAU;KACV,aAAY;KACZ,CAAA;IACE;;EACK,CAAA,GA/QX,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD;GACE,cAAa;GACb,OAAM;GACN,aAAY;GACZ,QAAQ,kBAAC,GAAD;IAAQ,eAAe,EAAW,UAAU;cAAE;IAAwB,CAAA;GAC9E,CAAA;EACS,CAAA,GAnCb,kBAAC,GAAD;EAAY,OAAM;YAChB,kBAAC,GAAD;GAAW,OAAM;GAAiB,SAAQ;GAA6B,CAAA;EAC5D,CAAA"}
|
|
1
|
+
{"version":3,"file":"CourseDetailPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/catalog/CourseDetailPage.tsx"],"sourcesContent":["/**\n * CourseDetailPage — a single published course: hero, curriculum, assessments,\n * discussion + tags, and the enrol / continue-learning call to action.\n * @module buildmyiq/pages/catalog\n *\n * All data is live: `buiCourse` (hero + flat lessons), `buiListModules`\n * (curriculum), `buiListAssessments` (course exams), and `buiListEnrollments`\n * (to decide Enrol vs Continue). Enrolling calls `buiEnroll` then routes the\n * learner straight into their new enrollment. Every list has its own\n * loading / error / empty state.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport type { LucideIcon } from 'lucide-react';\nimport {\n ArrowLeft,\n ArrowRight,\n BookOpen,\n ChevronDown,\n ClipboardCheck,\n ClipboardList,\n Clock,\n FileText,\n GraduationCap,\n Layers,\n ListChecks,\n MousePointerClick,\n PlayCircle,\n Radio,\n Sparkles,\n Video,\n} from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n Section,\n LoadingView,\n ErrorView,\n EmptyState,\n MiniBar,\n BuiDiscussion,\n BuiTags,\n} from '../../components';\nimport { cn } from '../../utils/cn';\nimport { useBuildmyiq } from '../../context';\nimport {\n useBuiCourse,\n useBuiModules,\n useBuiAssessments,\n useBuiEnrollments,\n useBuiEnroll,\n} from '../../hooks';\nimport { formatHours, formatDuration, formatEnumLabel } from '../../utils/formatters';\nimport type { BuiLesson, BuiLessonType } from '../../types';\n\nconst COURSE_ENTITY = 'buildmyiq-course';\nconst UNGROUPED_ID = '__ungrouped__';\n\n/** A curriculum group is either a real module or the \"additional lessons\" bucket. */\ninterface CurriculumGroup {\n id: string;\n title: string;\n description?: string | null;\n lessons: BuiLesson[];\n}\n\n/** Lesson-type → icon, so learners can scan a module at a glance. */\nconst LESSON_ICON: Record<BuiLessonType, LucideIcon> = {\n ARTICLE: FileText,\n VIDEO: Video,\n QUIZ: ListChecks,\n ASSIGNMENT: ClipboardList,\n INTERACTIVE: MousePointerClick,\n LIVE_SESSION: Radio,\n};\n\nconst LessonRow: FC<{ lesson: BuiLesson; index: number }> = ({ lesson, index }) => {\n const Icon = LESSON_ICON[lesson.type] ?? FileText;\n return (\n <li className=\"flex items-center gap-3 px-4 py-3 transition-colors hover:bg-bg-sunken/60\">\n <span className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Icon className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n <span className=\"mr-1.5 tabular-nums text-text-tertiary\">{index + 1}.</span>\n {lesson.title}\n </p>\n <p className=\"text-xs text-text-tertiary\">{formatEnumLabel(lesson.type)}</p>\n </div>\n {lesson.durationMin > 0 && (\n <span className=\"hidden shrink-0 items-center gap-1 text-xs tabular-nums text-text-secondary sm:inline-flex\">\n <Clock className=\"h-3.5 w-3.5\" /> {formatDuration(lesson.durationMin)}\n </span>\n )}\n {lesson.isPreview && (\n <span className=\"shrink-0 rounded-full border border-[var(--color-accent-teal)] px-2 py-0.5 text-xs font-medium text-[var(--color-accent-teal)]\">\n Preview\n </span>\n )}\n </li>\n );\n};\n\n/** A single hero meta pill (skill / structure / duration). */\nconst MetaPill: FC<{ icon: LucideIcon; children: ReactNode }> = ({ icon: Icon, children }) => (\n <span className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam bg-bg-surface px-3 py-1.5 text-xs font-medium text-text-secondary\">\n <Icon className=\"h-3.5 w-3.5 text-text-tertiary\" aria-hidden />\n {children}\n </span>\n);\n\nexport const CourseDetailPage: FC = () => {\n const { courseId } = useParams<{ courseId: string }>();\n const { currentUser, navigateTo } = useBuildmyiq();\n const learnerId = currentUser?.id;\n\n const courseQuery = useBuiCourse(courseId);\n const modulesQuery = useBuiModules(courseId);\n const assessmentsQuery = useBuiAssessments(courseId);\n const enrollmentsQuery = useBuiEnrollments({\n courseId,\n learnerId,\n enabled: Boolean(courseId && learnerId),\n });\n\n const enroll = useBuiEnroll();\n const [enrolError, setEnrolError] = useState<string | null>(null);\n // null = \"not yet touched\" → default-open the first module only.\n const [openGroups, setOpenGroups] = useState<Set<string> | null>(null);\n\n const course = courseQuery.data ?? null;\n const modules = modulesQuery.data ?? [];\n const assessments = assessmentsQuery.data ?? [];\n const enrollments = enrollmentsQuery.data?.items ?? [];\n\n // An existing, non-terminal enrollment means \"Continue\" instead of \"Enrol\".\n const activeEnrollment = useMemo(\n () => enrollments.find((e) => e.status !== 'DROPPED' && e.status !== 'EXPIRED'),\n [enrollments]\n );\n\n // Lessons attached directly to the course (no module) — surfaced as their own group.\n const ungroupedLessons = useMemo(\n () => (course?.lessons ?? []).filter((lesson) => !lesson.moduleId),\n [course]\n );\n\n // Unified, ordered list of curriculum groups (modules + the leftovers bucket).\n const groups: CurriculumGroup[] = useMemo(() => {\n const moduleGroups: CurriculumGroup[] = modules.map((m) => ({\n id: m.id,\n title: m.title,\n description: m.description,\n lessons: m.lessons ?? [],\n }));\n if (ungroupedLessons.length > 0) {\n moduleGroups.push({\n id: UNGROUPED_ID,\n title: 'Additional lessons',\n lessons: ungroupedLessons,\n });\n }\n return moduleGroups;\n }, [modules, ungroupedLessons]);\n\n const totalLessons = course?.lessons?.length ?? 0;\n const publishedAssessments = assessments.filter((a) => a.status === 'PUBLISHED').length;\n\n // Effective open-set: default to the first group when the learner hasn't toggled.\n const effectiveOpen = openGroups ?? new Set(groups.slice(0, 1).map((g) => g.id));\n const allOpen = groups.length > 0 && groups.every((g) => effectiveOpen.has(g.id));\n\n const toggleGroup = (id: string): void => {\n const next = new Set(effectiveOpen);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n setOpenGroups(next);\n };\n\n const toggleAll = (): void => {\n setOpenGroups(allOpen ? new Set() : new Set(groups.map((g) => g.id)));\n };\n\n const handleEnrol = async (): Promise<void> => {\n if (!courseId || !learnerId) return;\n setEnrolError(null);\n try {\n const created = await enroll.mutateAsync({ courseId, learnerId });\n navigateTo(`learning/${created.id}`);\n } catch (err) {\n setEnrolError(err instanceof Error ? err.message : 'Could not enrol in this course.');\n }\n };\n\n // Top-level page states.\n if (!courseId) {\n return (\n <PageLayout title=\"Course\">\n <ErrorView title=\"Missing course\" message=\"No course was specified.\" />\n </PageLayout>\n );\n }\n\n if (courseQuery.isLoading) {\n return (\n <PageLayout title=\"Course\">\n <LoadingView label=\"Loading course…\" />\n </PageLayout>\n );\n }\n\n if (courseQuery.isError) {\n return (\n <PageLayout title=\"Course\">\n <ErrorView\n title=\"Couldn't load this course\"\n message={courseQuery.error instanceof Error ? courseQuery.error.message : undefined}\n onRetry={() => courseQuery.refetch()}\n />\n </PageLayout>\n );\n }\n\n if (!course) {\n return (\n <PageLayout title=\"Course\">\n <EmptyState\n illustration=\"empty-search\"\n title=\"Course not found\"\n description=\"This course may have been unpublished or removed.\"\n action={<Button onClick={() => navigateTo('catalog')}>Back to catalog</Button>}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout title={course.title}>\n <div className=\"mx-auto flex w-full max-w-6xl flex-col gap-6\">\n {/* Breadcrumb + back */}\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <nav className=\"flex min-w-0 items-center gap-1.5 text-sm\" aria-label=\"Breadcrumb\">\n <button\n type=\"button\"\n onClick={() => navigateTo('catalog')}\n className=\"shrink-0 font-medium text-[var(--color-accent-teal)] transition-colors hover:opacity-80\"\n >\n Catalog\n </button>\n <span className=\"shrink-0 text-text-tertiary\">/</span>\n <span className=\"truncate text-text-secondary\">{course.title}</span>\n </nav>\n <button\n type=\"button\"\n onClick={() => navigateTo('catalog')}\n className=\"inline-flex shrink-0 items-center gap-1.5 text-sm text-text-secondary transition-colors hover:text-text-primary\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back to catalog\n </button>\n </div>\n\n {/* Hero: cover + info (left) and enrol / progress card (right) */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1fr)_20rem]\">\n <GlassCard treatment=\"plain\" className=\"overflow-hidden\">\n <div className=\"grid grid-cols-1 sm:grid-cols-[minmax(0,15rem)_minmax(0,1fr)]\">\n <div className=\"relative h-44 w-full overflow-hidden bg-bg-sunken sm:h-full\">\n {course.cover?.downloadUrl ? (\n <img\n src={course.cover.downloadUrl}\n alt=\"\"\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"flex h-full min-h-[11rem] w-full items-center justify-center bg-[var(--color-accent-soft)]\">\n <BookOpen className=\"h-10 w-10 text-text-secondary opacity-60\" />\n </div>\n )}\n </div>\n\n <div className=\"flex flex-col gap-4 p-6\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <StatusBadge status={course.level} />\n <StatusBadge status={course.status} />\n </div>\n\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight text-text-primary\">\n {course.title}\n </h1>\n {course.summary && (\n <p className=\"mt-2 text-sm text-text-secondary\">{course.summary}</p>\n )}\n </div>\n\n <div className=\"flex flex-wrap gap-2\">\n {course.skill?.name && <MetaPill icon={Sparkles}>{course.skill.name}</MetaPill>}\n <MetaPill icon={Layers}>\n {modules.length} module{modules.length === 1 ? '' : 's'} · {totalLessons} lesson\n {totalLessons === 1 ? '' : 's'}\n </MetaPill>\n {course.estimatedHours > 0 && (\n <MetaPill icon={Clock}>{formatHours(course.estimatedHours)}</MetaPill>\n )}\n </div>\n\n {course.tags.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\">\n {course.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n\n {course.description && (\n <p className=\"whitespace-pre-wrap text-sm leading-relaxed text-text-secondary\">\n {course.description}\n </p>\n )}\n </div>\n </div>\n </GlassCard>\n\n {/* Enrol / progress card */}\n <GlassCard treatment=\"glass\" glow className=\"h-fit p-5 lg:sticky lg:top-4\">\n {activeEnrollment ? (\n <div className=\"flex flex-col gap-4\">\n <Button\n size=\"lg\"\n className=\"w-full\"\n onClick={() => navigateTo(`learning/${activeEnrollment.id}`)}\n >\n <PlayCircle className=\"mr-2 h-4 w-4\" /> Continue learning\n </Button>\n <div className=\"flex flex-col gap-1.5\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">Your progress</span>\n <span className=\"font-semibold tabular-nums text-text-primary\">\n {activeEnrollment.progressPct}%\n </span>\n </div>\n <MiniBar value={activeEnrollment.progressPct} />\n </div>\n <div className=\"flex items-center justify-between border-t border-border-seam pt-3 text-xs\">\n <span className=\"text-text-secondary\">Enrollment</span>\n <StatusBadge status={activeEnrollment.status} />\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col gap-4\">\n <Button\n size=\"lg\"\n className=\"w-full\"\n onClick={handleEnrol}\n disabled={enroll.isPending || !learnerId}\n >\n <GraduationCap className=\"mr-2 h-4 w-4\" />\n {enroll.isPending ? 'Enrolling…' : 'Enrol in this course'}\n {!enroll.isPending && <ArrowRight className=\"ml-2 h-4 w-4\" />}\n </Button>\n {!learnerId && (\n <p className=\"text-xs text-text-tertiary\">Sign in to your workspace to enrol.</p>\n )}\n {enrolError && <p className=\"text-xs text-status-error-text\">{enrolError}</p>}\n <dl className=\"flex flex-col gap-2.5 border-t border-border-seam pt-3 text-sm\">\n <div className=\"flex items-center justify-between\">\n <dt className=\"inline-flex items-center gap-1.5 text-text-secondary\">\n <Layers className=\"h-3.5 w-3.5 text-text-tertiary\" /> Lessons\n </dt>\n <dd className=\"font-medium tabular-nums text-text-primary\">{totalLessons}</dd>\n </div>\n {course.estimatedHours > 0 && (\n <div className=\"flex items-center justify-between\">\n <dt className=\"inline-flex items-center gap-1.5 text-text-secondary\">\n <Clock className=\"h-3.5 w-3.5 text-text-tertiary\" /> Duration\n </dt>\n <dd className=\"font-medium text-text-primary\">\n {formatHours(course.estimatedHours)}\n </dd>\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <dt className=\"inline-flex items-center gap-1.5 text-text-secondary\">\n <ClipboardCheck className=\"h-3.5 w-3.5 text-text-tertiary\" /> Assessments\n </dt>\n <dd className=\"font-medium tabular-nums text-text-primary\">\n {publishedAssessments}\n </dd>\n </div>\n </dl>\n </div>\n )}\n </GlassCard>\n </div>\n\n {/* Curriculum (left) + Assessments & Discussion (right) */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1.7fr)_minmax(0,1fr)]\">\n {/* Curriculum */}\n <div>\n <Section\n title=\"Curriculum\"\n description=\"Modules and lessons in this course.\"\n action={\n groups.length > 1 ? (\n <button\n type=\"button\"\n onClick={toggleAll}\n className=\"text-sm font-medium text-[var(--color-accent-teal)] transition-colors hover:opacity-80\"\n >\n {allOpen ? 'Collapse all' : 'Expand all'}\n </button>\n ) : undefined\n }\n >\n {modulesQuery.isLoading ? (\n <LoadingView label=\"Loading curriculum…\" />\n ) : modulesQuery.isError ? (\n <ErrorView\n title=\"Couldn't load the curriculum\"\n message={\n modulesQuery.error instanceof Error ? modulesQuery.error.message : undefined\n }\n onRetry={() => modulesQuery.refetch()}\n />\n ) : groups.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No lessons yet\"\n description=\"The instructor hasn't added any modules or lessons to this course.\"\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {groups.map((group, index) => {\n const open = effectiveOpen.has(group.id);\n const panelId = `curriculum-panel-${group.id}`;\n return (\n <div\n key={group.id}\n className={cn(\n 'overflow-hidden rounded-xl border bg-bg-surface transition-colors',\n open ? 'border-[var(--color-accent-soft)]' : 'border-border-seam'\n )}\n >\n <button\n type=\"button\"\n onClick={() => toggleGroup(group.id)}\n aria-expanded={open}\n aria-controls={panelId}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-bg-sunken/50\"\n >\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg text-xs font-semibold text-action-primary-text\">\n {index + 1}\n </span>\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"truncate font-semibold text-text-primary\">\n {group.title}\n </h3>\n {group.description && open && (\n <p className=\"mt-0.5 line-clamp-2 text-sm text-text-secondary\">\n {group.description}\n </p>\n )}\n </div>\n <span className=\"shrink-0 text-xs text-text-tertiary\">\n {group.lessons.length} lesson{group.lessons.length === 1 ? '' : 's'}\n </span>\n <ChevronDown\n className={cn(\n 'h-4 w-4 shrink-0 text-text-tertiary transition-transform',\n open && 'rotate-180'\n )}\n aria-hidden\n />\n </button>\n {open &&\n (group.lessons.length === 0 ? (\n <p className=\"border-t border-border-seam px-4 py-3 text-sm text-text-tertiary\">\n No lessons in this module yet.\n </p>\n ) : (\n <ul\n id={panelId}\n className=\"divide-y divide-border-seam border-t border-border-seam\"\n >\n {group.lessons.map((lesson, li) => (\n <LessonRow key={lesson.id} lesson={lesson} index={li} />\n ))}\n </ul>\n ))}\n </div>\n );\n })}\n </div>\n )}\n </Section>\n </div>\n\n {/* Assessments + Discussion */}\n <div className=\"flex flex-col gap-6\">\n <div>\n <Section title=\"Assessments\" description=\"Quizzes and exams for this course.\">\n {assessmentsQuery.isLoading ? (\n <LoadingView label=\"Loading assessments…\" />\n ) : assessmentsQuery.isError ? (\n <ErrorView\n title=\"Couldn't load assessments\"\n message={\n assessmentsQuery.error instanceof Error\n ? assessmentsQuery.error.message\n : undefined\n }\n onRetry={() => assessmentsQuery.refetch()}\n />\n ) : assessments.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<ClipboardCheck className=\"h-9 w-9\" />}\n title=\"No assessments yet\"\n description=\"This course doesn't have any quizzes or exams attached.\"\n />\n ) : (\n <ul className=\"flex flex-col gap-3\">\n {assessments.map((assessment) => (\n <li\n key={assessment.id}\n className=\"rounded-xl border border-border-seam bg-bg-surface p-4\"\n >\n <div className=\"flex items-start gap-3\">\n <span className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <ClipboardCheck className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-medium text-text-primary\">{assessment.title}</p>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={assessment.type} />\n <StatusBadge status={assessment.status} />\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-secondary\">\n <span>Pass ≥ {assessment.passingScore}%</span>\n <span>\n {assessment.maxAttempts} attempt\n {assessment.maxAttempts === 1 ? '' : 's'}\n </span>\n {assessment.timeLimitMin > 0 && (\n <span className=\"inline-flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" />\n {formatDuration(assessment.timeLimitMin)}\n </span>\n )}\n </div>\n </div>\n </div>\n {assessment.status === 'PUBLISHED' && activeEnrollment && (\n <div className=\"mt-3 flex justify-end\">\n <Button\n size=\"sm\"\n onClick={() => navigateTo(`assessments/${assessment.id}/take`)}\n >\n Take\n </Button>\n </div>\n )}\n </li>\n ))}\n </ul>\n )}\n </Section>\n </div>\n\n {/* Discussion (platform wspace-conversations) */}\n <div>\n <BuiDiscussion\n entityType={COURSE_ENTITY}\n entityId={courseId}\n description=\"Ask questions and discuss this course with your workspace.\"\n />\n </div>\n </div>\n </div>\n\n {/* Tags (platform wspace-tags) — full width */}\n <div>\n <BuiTags entityType={COURSE_ENTITY} entityId={courseId} />\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CertificatesPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/certificates/CertificatesPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAqB,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"CertificatesPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/certificates/CertificatesPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAqB,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAmEnD,eAAO,MAAM,gBAAgB,EAAE,EAgf9B,CAAC"}
|