@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.
Files changed (67) hide show
  1. package/dist/buildmyiq/BuildmyiqRoot.d.ts.map +1 -1
  2. package/dist/buildmyiq/BuildmyiqRoot.js +57 -49
  3. package/dist/buildmyiq/BuildmyiqRoot.js.map +1 -1
  4. package/dist/buildmyiq/BuildmyiqRoutes.d.ts.map +1 -1
  5. package/dist/buildmyiq/BuildmyiqRoutes.js +15 -11
  6. package/dist/buildmyiq/BuildmyiqRoutes.js.map +1 -1
  7. package/dist/buildmyiq/components/Avatar.d.ts +15 -0
  8. package/dist/buildmyiq/components/Avatar.d.ts.map +1 -0
  9. package/dist/buildmyiq/components/Avatar.js +42 -0
  10. package/dist/buildmyiq/components/Avatar.js.map +1 -0
  11. package/dist/buildmyiq/components/Viz.d.ts +25 -0
  12. package/dist/buildmyiq/components/Viz.d.ts.map +1 -0
  13. package/dist/buildmyiq/components/Viz.js +78 -0
  14. package/dist/buildmyiq/components/Viz.js.map +1 -0
  15. package/dist/buildmyiq/components/index.d.ts +2 -0
  16. package/dist/buildmyiq/components/index.d.ts.map +1 -1
  17. package/dist/buildmyiq/components/index.js +2 -0
  18. package/dist/buildmyiq/pages/DashboardPage.d.ts.map +1 -1
  19. package/dist/buildmyiq/pages/DashboardPage.js +200 -98
  20. package/dist/buildmyiq/pages/DashboardPage.js.map +1 -1
  21. package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.d.ts.map +1 -1
  22. package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.js +262 -241
  23. package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.js.map +1 -1
  24. package/dist/buildmyiq/pages/assessments/AssessmentsPage.d.ts.map +1 -1
  25. package/dist/buildmyiq/pages/assessments/AssessmentsPage.js +142 -115
  26. package/dist/buildmyiq/pages/assessments/AssessmentsPage.js.map +1 -1
  27. package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.d.ts.map +1 -1
  28. package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.js +220 -156
  29. package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.js.map +1 -1
  30. package/dist/buildmyiq/pages/authoring/AuthoringPage.d.ts.map +1 -1
  31. package/dist/buildmyiq/pages/authoring/AuthoringPage.js +51 -46
  32. package/dist/buildmyiq/pages/authoring/AuthoringPage.js.map +1 -1
  33. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.d.ts.map +1 -1
  34. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js +581 -448
  35. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js.map +1 -1
  36. package/dist/buildmyiq/pages/catalog/CatalogPage.d.ts.map +1 -1
  37. package/dist/buildmyiq/pages/catalog/CatalogPage.js +52 -42
  38. package/dist/buildmyiq/pages/catalog/CatalogPage.js.map +1 -1
  39. package/dist/buildmyiq/pages/catalog/CourseDetailPage.d.ts.map +1 -1
  40. package/dist/buildmyiq/pages/catalog/CourseDetailPage.js +422 -301
  41. package/dist/buildmyiq/pages/catalog/CourseDetailPage.js.map +1 -1
  42. package/dist/buildmyiq/pages/certificates/CertificatesPage.d.ts.map +1 -1
  43. package/dist/buildmyiq/pages/certificates/CertificatesPage.js +237 -151
  44. package/dist/buildmyiq/pages/certificates/CertificatesPage.js.map +1 -1
  45. package/dist/buildmyiq/pages/cohorts/CohortsPage.d.ts.map +1 -1
  46. package/dist/buildmyiq/pages/cohorts/CohortsPage.js +219 -138
  47. package/dist/buildmyiq/pages/cohorts/CohortsPage.js.map +1 -1
  48. package/dist/buildmyiq/pages/insights/InsightsPage.d.ts.map +1 -1
  49. package/dist/buildmyiq/pages/insights/InsightsPage.js +276 -152
  50. package/dist/buildmyiq/pages/insights/InsightsPage.js.map +1 -1
  51. package/dist/buildmyiq/pages/learning/LessonPlayerPage.d.ts.map +1 -1
  52. package/dist/buildmyiq/pages/learning/LessonPlayerPage.js +237 -174
  53. package/dist/buildmyiq/pages/learning/LessonPlayerPage.js.map +1 -1
  54. package/dist/buildmyiq/pages/learning/MyLearningPage.d.ts.map +1 -1
  55. package/dist/buildmyiq/pages/learning/MyLearningPage.js +307 -169
  56. package/dist/buildmyiq/pages/learning/MyLearningPage.js.map +1 -1
  57. package/dist/buildmyiq/pages/paths/PathsPage.d.ts +4 -0
  58. package/dist/buildmyiq/pages/paths/PathsPage.d.ts.map +1 -0
  59. package/dist/buildmyiq/pages/paths/PathsPage.js +187 -0
  60. package/dist/buildmyiq/pages/paths/PathsPage.js.map +1 -0
  61. package/dist/buildmyiq/pages/skills/SkillsPage.d.ts.map +1 -1
  62. package/dist/buildmyiq/pages/skills/SkillsPage.js +233 -140
  63. package/dist/buildmyiq/pages/skills/SkillsPage.js.map +1 -1
  64. package/dist/buildmyiq/pages/submissions/SubmissionsPage.d.ts.map +1 -1
  65. package/dist/buildmyiq/pages/submissions/SubmissionsPage.js +379 -275
  66. package/dist/buildmyiq/pages/submissions/SubmissionsPage.js.map +1 -1
  67. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"CourseBuilderPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/authoring/CourseBuilderPage.tsx"],"sourcesContent":["/**\n * CourseBuilderPage — the per-course authoring workspace.\n * @module buildmyiq/pages/authoring\n *\n * Loads one course (`useBuiCourse`) and exposes four tabs:\n * - Details — editable course meta (`useBuiUpdateCourse`), cover upload\n * (EvidenceUpload → coverFileId), Publish (`useBuiPublishCourse`) and Archive\n * (`useBuiArchiveCourse`).\n * - Curriculum — modules (`useBuiModules`, `useBuiCreateModule`) and their\n * lessons (`useBuiCreateLesson`).\n * - Assessments — course assessments (`useBuiAssessments`) linking out to the\n * assessment editor.\n * - Discussion — the shared wspace-conversations thread (`BuiDiscussion`).\n * Every list carries loading / error / empty states; all data is live `bui*` data.\n */\n\nimport type { FC } from 'react';\nimport { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n Archive,\n ArrowLeft,\n BookOpen,\n Clock,\n FileText,\n ListChecks,\n MessageSquare,\n PlayCircle,\n Plus,\n Send,\n} from 'lucide-react';\nimport { Button, GlassCard, Input, Switch, Textarea } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n Tabs,\n type TabItem,\n StatusBadge,\n EmptyState,\n LoadingView,\n ErrorView,\n Section,\n EvidenceUpload,\n BuiDiscussion,\n} from '../../components';\nimport { Modal, Field, SelectField, NO_SKILL } from './_forms';\nimport { useBuildmyiq } from '../../context';\nimport {\n useBuiCourse,\n useBuiUpdateCourse,\n useBuiPublishCourse,\n useBuiArchiveCourse,\n useBuiModules,\n useBuiCreateModule,\n useBuiCreateLesson,\n useBuiAssessments,\n useBuiSkills,\n type UploadedBuiFile,\n} from '../../hooks';\nimport { COURSE_LEVELS, LESSON_TYPES } from '../../types';\nimport type {\n BuiCourse,\n BuiCourseLevel,\n BuiCreateLessonInput,\n BuiCreateModuleInput,\n BuiLessonType,\n BuiModule,\n BuiUpdateCourseInput,\n} from '../../types';\nimport { formatDuration, formatEnumLabel } from '../../utils/formatters';\n\ntype BuilderTab = 'details' | 'curriculum' | 'assessments' | 'discussion';\n\n// ============================================================================\n// Details tab\n// ============================================================================\n\nconst DetailsTab: FC<{ course: BuiCourse }> = ({ course }) => {\n const updateCourse = useBuiUpdateCourse();\n const publishCourse = useBuiPublishCourse();\n const archiveCourse = useBuiArchiveCourse();\n const skillsQuery = useBuiSkills();\n const skills = skillsQuery.data ?? [];\n\n const [title, setTitle] = useState(course.title);\n const [summary, setSummary] = useState(course.summary ?? '');\n const [description, setDescription] = useState(course.description ?? '');\n const [level, setLevel] = useState<BuiCourseLevel>(course.level);\n const [language, setLanguage] = useState(course.language);\n const [estimatedHours, setEstimatedHours] = useState(String(course.estimatedHours ?? 0));\n const [skillId, setSkillId] = useState<string>(course.skillId ?? NO_SKILL);\n const [isPublic, setIsPublic] = useState(course.isPublic);\n const [coverFile, setCoverFile] = useState<UploadedBuiFile | null>(null);\n\n const [saveError, setSaveError] = useState<string | null>(null);\n const [coverError, setCoverError] = useState<string | null>(null);\n const [actionError, setActionError] = useState<string | null>(null);\n const [confirmArchive, setConfirmArchive] = useState(false);\n\n const canPublish = course.status === 'DRAFT' || course.status === 'IN_REVIEW';\n const canArchive = course.status !== 'ARCHIVED';\n\n const handleSave = async () => {\n setSaveError(null);\n const input: BuiUpdateCourseInput = {\n title: title.trim() || course.title,\n summary: summary.trim() || undefined,\n description: description.trim() || undefined,\n level,\n language: language.trim() || 'en',\n estimatedHours: estimatedHours.trim() ? Number(estimatedHours) : 0,\n skillId: skillId !== NO_SKILL ? skillId : undefined,\n isPublic,\n };\n try {\n await updateCourse.mutateAsync({ id: course.id, input });\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : 'Failed to save changes.');\n }\n };\n\n const handleCoverChange = async (file: UploadedBuiFile | null) => {\n setCoverFile(file);\n if (!file) return;\n setCoverError(null);\n try {\n await updateCourse.mutateAsync({ id: course.id, input: { coverFileId: file.fileId } });\n } catch (e) {\n setCoverError(e instanceof Error ? e.message : 'Failed to update the cover.');\n }\n };\n\n const handlePublish = async () => {\n setActionError(null);\n try {\n await publishCourse.mutateAsync(course.id);\n } catch (e) {\n setActionError(e instanceof Error ? e.message : 'Failed to publish the course.');\n }\n };\n\n const handleArchive = async () => {\n setActionError(null);\n try {\n await archiveCourse.mutateAsync(course.id);\n setConfirmArchive(false);\n } catch (e) {\n setActionError(e instanceof Error ? e.message : 'Failed to archive the course.');\n }\n };\n\n return (\n <div className=\"grid gap-6 lg:grid-cols-3\">\n <div className=\"lg:col-span-2\">\n <Section\n title=\"Course details\"\n description=\"Metadata learners see in the catalog.\"\n action={\n <Button onClick={handleSave} disabled={updateCourse.isPending}>\n {updateCourse.isPending ? 'Saving…' : 'Save changes'}\n </Button>\n }\n >\n <div className=\"grid gap-4\">\n <Field label=\"Title\" htmlFor=\"detail-title\" required>\n <Input id=\"detail-title\" value={title} onChange={(e) => setTitle(e.target.value)} />\n </Field>\n <Field label=\"Summary\" htmlFor=\"detail-summary\">\n <Textarea\n id=\"detail-summary\"\n rows={2}\n value={summary}\n onChange={(e) => setSummary(e.target.value)}\n placeholder=\"A short description shown on the catalog card.\"\n />\n </Field>\n <Field label=\"Description\" htmlFor=\"detail-description\">\n <Textarea\n id=\"detail-description\"\n rows={5}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"The full course overview.\"\n />\n </Field>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <Field label=\"Level\">\n <SelectField\n value={level}\n onChange={(v) => setLevel(v as BuiCourseLevel)}\n options={COURSE_LEVELS.map((l) => ({ value: l, label: formatEnumLabel(l) }))}\n />\n </Field>\n <Field label=\"Language\" htmlFor=\"detail-language\">\n <Input\n id=\"detail-language\"\n value={language}\n onChange={(e) => setLanguage(e.target.value)}\n placeholder=\"en\"\n />\n </Field>\n <Field label=\"Estimated hours\" htmlFor=\"detail-hours\">\n <Input\n id=\"detail-hours\"\n type=\"number\"\n min={0}\n value={estimatedHours}\n onChange={(e) => setEstimatedHours(e.target.value)}\n />\n </Field>\n <Field label=\"Skill\">\n <SelectField\n value={skillId}\n onChange={setSkillId}\n options={[\n { value: NO_SKILL, label: skillsQuery.isLoading ? 'Loading…' : 'No skill' },\n ...skills.map((s) => ({ value: s.id, label: s.name })),\n ]}\n />\n </Field>\n </div>\n <label className=\"inline-flex cursor-pointer items-center gap-2 text-sm text-text-secondary\">\n <Switch checked={isPublic} onCheckedChange={setIsPublic} />\n Publicly visible in the catalog\n </label>\n {saveError && <p className=\"text-sm text-status-error-text\">{saveError}</p>}\n </div>\n </Section>\n </div>\n\n <div className=\"flex flex-col gap-6\">\n <Section title=\"Cover image\">\n <div className=\"grid gap-3\">\n <div className=\"h-36 w-full overflow-hidden rounded-xl border border-border-seam bg-bg-sunken\">\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 items-center justify-center text-sm text-text-secondary\">\n No cover yet\n </div>\n )}\n </div>\n <EvidenceUpload\n id=\"course-cover\"\n label=\"Upload cover\"\n value={coverFile}\n onChange={handleCoverChange}\n accept=\"image/*\"\n hint=\"PNG or JPG, shown on catalog cards.\"\n />\n {coverError && <p className=\"text-xs text-status-error-text\">{coverError}</p>}\n </div>\n </Section>\n\n <Section title=\"Publishing\">\n <div className=\"grid gap-3\">\n <div className=\"flex items-center gap-2 text-sm text-text-secondary\">\n Status <StatusBadge status={course.status} />\n </div>\n {canPublish && (\n <Button onClick={handlePublish} disabled={publishCourse.isPending}>\n <Send className=\"mr-1.5 h-4 w-4\" />\n {publishCourse.isPending ? 'Publishing…' : 'Publish course'}\n </Button>\n )}\n {canArchive && (\n <Button variant=\"outline\" onClick={() => setConfirmArchive(true)}>\n <Archive className=\"mr-1.5 h-4 w-4\" /> Archive\n </Button>\n )}\n {actionError && <p className=\"text-sm text-status-error-text\">{actionError}</p>}\n </div>\n </Section>\n </div>\n\n {confirmArchive && (\n <Modal\n open\n onClose={() => setConfirmArchive(false)}\n title=\"Archive this course?\"\n description=\"Archived courses are hidden from the catalog and cannot be enrolled in.\"\n footer={\n <>\n <Button\n variant=\"ghost\"\n onClick={() => setConfirmArchive(false)}\n disabled={archiveCourse.isPending}\n >\n Cancel\n </Button>\n <Button\n variant=\"destructive\"\n onClick={handleArchive}\n disabled={archiveCourse.isPending}\n >\n {archiveCourse.isPending ? 'Archiving…' : 'Archive course'}\n </Button>\n </>\n }\n >\n <p className=\"text-sm text-text-secondary\">\n You can still find it in Authoring under the Archived filter.\n </p>\n </Modal>\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Curriculum tab\n// ============================================================================\n\nconst AddModuleModal: FC<{ courseId: string; position: number; onClose: () => void }> = ({\n courseId,\n position,\n onClose,\n}) => {\n const createModule = useBuiCreateModule();\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const canSubmit = title.trim().length > 0 && !createModule.isPending;\n\n const handleSubmit = async () => {\n if (!canSubmit) return;\n setError(null);\n const input: BuiCreateModuleInput = {\n courseId,\n title: title.trim(),\n description: description.trim() || undefined,\n position,\n };\n try {\n await createModule.mutateAsync(input);\n onClose();\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to add the module.');\n }\n };\n\n return (\n <Modal\n open\n onClose={onClose}\n title=\"Add module\"\n footer={\n <>\n <Button variant=\"ghost\" onClick={onClose} disabled={createModule.isPending}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={!canSubmit}>\n {createModule.isPending ? 'Adding…' : 'Add module'}\n </Button>\n </>\n }\n >\n <div className=\"grid gap-4\">\n <Field label=\"Module title\" htmlFor=\"module-title\" required>\n <Input\n id=\"module-title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"e.g. Getting started\"\n />\n </Field>\n <Field label=\"Description\" htmlFor=\"module-description\">\n <Textarea\n id=\"module-description\"\n rows={3}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n />\n </Field>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n </Modal>\n );\n};\n\nconst AddLessonModal: FC<{\n courseId: string;\n moduleId: string;\n position: number;\n onClose: () => void;\n}> = ({ courseId, moduleId, position, onClose }) => {\n const createLesson = useBuiCreateLesson();\n const [title, setTitle] = useState('');\n const [type, setType] = useState<BuiLessonType>('ARTICLE');\n const [content, setContent] = useState('');\n const [durationMin, setDurationMin] = useState('');\n const [isPreview, setIsPreview] = useState(false);\n const [media, setMedia] = useState<UploadedBuiFile | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const canSubmit = title.trim().length > 0 && !createLesson.isPending;\n\n const handleSubmit = async () => {\n if (!canSubmit) return;\n setError(null);\n const input: BuiCreateLessonInput = {\n courseId,\n moduleId,\n title: title.trim(),\n type,\n content: content.trim() || undefined,\n durationMin: durationMin.trim() ? Number(durationMin) : undefined,\n position,\n isPreview,\n mediaFileId: media?.fileId,\n };\n try {\n await createLesson.mutateAsync(input);\n onClose();\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to add the lesson.');\n }\n };\n\n return (\n <Modal\n open\n onClose={onClose}\n title=\"Add lesson\"\n className=\"sm:max-w-xl\"\n footer={\n <>\n <Button variant=\"ghost\" onClick={onClose} disabled={createLesson.isPending}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={!canSubmit}>\n {createLesson.isPending ? 'Adding…' : 'Add lesson'}\n </Button>\n </>\n }\n >\n <div className=\"grid gap-4\">\n <Field label=\"Lesson title\" htmlFor=\"lesson-title\" required>\n <Input\n id=\"lesson-title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"e.g. What is data literacy?\"\n />\n </Field>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <Field label=\"Type\">\n <SelectField\n value={type}\n onChange={(v) => setType(v as BuiLessonType)}\n options={LESSON_TYPES.map((t) => ({ value: t, label: formatEnumLabel(t) }))}\n />\n </Field>\n <Field label=\"Duration (minutes)\" htmlFor=\"lesson-duration\">\n <Input\n id=\"lesson-duration\"\n type=\"number\"\n min={0}\n value={durationMin}\n onChange={(e) => setDurationMin(e.target.value)}\n placeholder=\"0\"\n />\n </Field>\n </div>\n <Field\n label=\"Content\"\n htmlFor=\"lesson-content\"\n hint=\"Lesson body — markdown or plain text.\"\n >\n <Textarea\n id=\"lesson-content\"\n rows={5}\n value={content}\n onChange={(e) => setContent(e.target.value)}\n />\n </Field>\n <EvidenceUpload\n id=\"lesson-media\"\n label=\"Media (optional)\"\n value={media}\n onChange={setMedia}\n hint=\"Attach a video, audio file or PDF for this lesson.\"\n />\n <label className=\"inline-flex cursor-pointer items-center gap-2 text-sm text-text-secondary\">\n <Switch checked={isPreview} onCheckedChange={setIsPreview} />\n Free preview lesson\n </label>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n </Modal>\n );\n};\n\nconst ModuleCard: FC<{ module: BuiModule; index: number; onAddLesson: () => void }> = ({\n module,\n index,\n onAddLesson,\n}) => {\n const lessons = [...(module.lessons ?? [])].sort((a, b) => a.position - b.position);\n return (\n <GlassCard treatment=\"plain\" className=\"p-4\">\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <h3 className=\"font-semibold text-text-primary\">\n <span className=\"mr-2 text-text-tertiary\">Module {index + 1}</span>\n {module.title}\n </h3>\n {module.description && (\n <p className=\"mt-0.5 text-sm text-text-secondary\">{module.description}</p>\n )}\n </div>\n <Button variant=\"outline\" size=\"sm\" onClick={onAddLesson}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Add lesson\n </Button>\n </div>\n <div className=\"mt-3\">\n {lessons.length === 0 ? (\n <p className=\"rounded-lg border border-dashed border-border-seam px-3 py-3 text-sm text-text-secondary\">\n No lessons yet.\n </p>\n ) : (\n <ul className=\"flex flex-col gap-1.5\">\n {lessons.map((lesson) => (\n <li\n key={lesson.id}\n className=\"flex flex-wrap items-center gap-3 rounded-lg border border-border-seam bg-bg-surface px-3 py-2\"\n >\n <PlayCircle className=\"h-4 w-4 shrink-0 text-text-tertiary\" />\n <span className=\"min-w-0 flex-1 truncate text-sm text-text-primary\">\n {lesson.title}\n </span>\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-secondary\">\n {formatEnumLabel(lesson.type)}\n </span>\n {lesson.isPreview && (\n <span className=\"rounded-full bg-[var(--color-accent-soft)] px-2 py-0.5 text-xs text-text-primary\">\n Preview\n </span>\n )}\n {lesson.durationMin > 0 && (\n <span className=\"inline-flex items-center gap-1 text-xs text-text-secondary\">\n <Clock className=\"h-3.5 w-3.5\" /> {formatDuration(lesson.durationMin)}\n </span>\n )}\n </li>\n ))}\n </ul>\n )}\n </div>\n </GlassCard>\n );\n};\n\nconst CurriculumTab: FC<{ courseId: string }> = ({ courseId }) => {\n const modulesQuery = useBuiModules(courseId);\n const [showAddModule, setShowAddModule] = useState(false);\n const [lessonModuleId, setLessonModuleId] = useState<string | null>(null);\n\n const modules = modulesQuery.data ?? [];\n const lessonModule = modules.find((m) => m.id === lessonModuleId) ?? null;\n\n return (\n <Section\n title=\"Curriculum\"\n description=\"Organise the course into modules and lessons.\"\n action={\n <Button onClick={() => setShowAddModule(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Add module\n </Button>\n }\n >\n {modulesQuery.isLoading ? (\n <LoadingView label=\"Loading curriculum…\" />\n ) : modulesQuery.isError ? (\n <ErrorView\n message=\"We couldn't load the curriculum.\"\n onRetry={() => modulesQuery.refetch()}\n />\n ) : modules.length === 0 ? (\n <EmptyState\n illustration=\"onboarding\"\n title=\"No modules yet\"\n description=\"Add your first module, then fill it with lessons.\"\n action={\n <Button onClick={() => setShowAddModule(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Add module\n </Button>\n }\n />\n ) : (\n <div className=\"flex flex-col gap-4\">\n {[...modules]\n .sort((a, b) => a.position - b.position)\n .map((mod, idx) => (\n <ModuleCard\n key={mod.id}\n module={mod}\n index={idx}\n onAddLesson={() => setLessonModuleId(mod.id)}\n />\n ))}\n </div>\n )}\n\n {showAddModule && (\n <AddModuleModal\n courseId={courseId}\n position={modules.length}\n onClose={() => setShowAddModule(false)}\n />\n )}\n {lessonModuleId && (\n <AddLessonModal\n courseId={courseId}\n moduleId={lessonModuleId}\n position={lessonModule?.lessons?.length ?? 0}\n onClose={() => setLessonModuleId(null)}\n />\n )}\n </Section>\n );\n};\n\n// ============================================================================\n// Assessments tab\n// ============================================================================\n\nconst AssessmentsTab: FC<{ courseId: string }> = ({ courseId }) => {\n const { navigateTo } = useBuildmyiq();\n const assessmentsQuery = useBuiAssessments(courseId);\n const assessments = assessmentsQuery.data ?? [];\n\n return (\n <Section\n title=\"Assessments\"\n description=\"Quizzes, exams and assignments attached to this course.\"\n >\n {assessmentsQuery.isLoading ? (\n <LoadingView label=\"Loading assessments…\" />\n ) : assessmentsQuery.isError ? (\n <ErrorView\n message=\"We couldn't load the assessments.\"\n onRetry={() => assessmentsQuery.refetch()}\n />\n ) : assessments.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No assessments yet\"\n description=\"Create an assessment in the Assessments workspace to test learners on this course.\"\n action={\n <Button variant=\"outline\" onClick={() => navigateTo('assessments')}>\n Open assessments\n </Button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {assessments.map((a) => (\n <GlassCard\n key={a.id}\n treatment=\"plain\"\n className=\"flex cursor-pointer flex-col gap-2 p-4 transition-shadow hover:shadow-lg\"\n onClick={() => navigateTo(`assessments/${a.id}`)}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <h3 className=\"min-w-0 font-semibold text-text-primary\">{a.title}</h3>\n <StatusBadge status={a.status} />\n </div>\n {a.description && (\n <p className=\"line-clamp-2 text-sm text-text-secondary\">{a.description}</p>\n )}\n <div className=\"mt-auto flex flex-wrap items-center gap-3 pt-1 text-xs text-text-secondary\">\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5\">\n {formatEnumLabel(a.type)}\n </span>\n <span>Pass ≥ {a.passingScore ?? 0}%</span>\n {a.timeLimitMin > 0 && (\n <span className=\"inline-flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" /> {formatDuration(a.timeLimitMin)}\n </span>\n )}\n </div>\n </GlassCard>\n ))}\n </div>\n )}\n </Section>\n );\n};\n\n// ============================================================================\n// Page\n// ============================================================================\n\nexport const CourseBuilderPage: FC = () => {\n const { courseId } = useParams<{ courseId: string }>();\n const { navigateTo } = useBuildmyiq();\n const [tab, setTab] = useState<BuilderTab>('details');\n const courseQuery = useBuiCourse(courseId);\n const course = courseQuery.data ?? null;\n\n if (courseQuery.isLoading) {\n return (\n <PageLayout title=\"Course builder\">\n <LoadingView label=\"Loading course…\" />\n </PageLayout>\n );\n }\n\n if (courseQuery.isError) {\n return (\n <PageLayout title=\"Course builder\">\n <ErrorView message=\"We couldn't load this course.\" onRetry={() => courseQuery.refetch()} />\n </PageLayout>\n );\n }\n\n if (!courseId || !course) {\n return (\n <PageLayout title=\"Course builder\">\n <EmptyState\n illustration=\"empty-search\"\n title=\"Course not found\"\n description=\"This course may have been archived or removed.\"\n action={<Button onClick={() => navigateTo('authoring')}>Back to authoring</Button>}\n />\n </PageLayout>\n );\n }\n\n const tabs: TabItem<BuilderTab>[] = [\n { value: 'details', label: 'Details', icon: <FileText className=\"h-4 w-4\" /> },\n {\n value: 'curriculum',\n label: 'Curriculum',\n icon: <BookOpen className=\"h-4 w-4\" />,\n count: course.modules?.length ?? 0,\n },\n { value: 'assessments', label: 'Assessments', icon: <ListChecks className=\"h-4 w-4\" /> },\n { value: 'discussion', label: 'Discussion', icon: <MessageSquare className=\"h-4 w-4\" /> },\n ];\n\n return (\n <PageLayout\n title={course.title}\n description={course.summary ?? undefined}\n actions={\n <Button variant=\"ghost\" onClick={() => navigateTo('authoring')}>\n <ArrowLeft className=\"mr-1.5 h-4 w-4\" /> Back\n </Button>\n }\n >\n <div className=\"flex flex-col gap-5\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <StatusBadge status={course.status} />\n <StatusBadge status={course.level} />\n </div>\n\n <Tabs items={tabs} value={tab} onChange={setTab} />\n\n {tab === 'details' && <DetailsTab key={course.id} course={course} />}\n {tab === 'curriculum' && <CurriculumTab courseId={courseId} />}\n {tab === 'assessments' && <AssessmentsTab courseId={courseId} />}\n {tab === 'discussion' && (\n <BuiDiscussion entityType=\"buildmyiq-course\" entityId={courseId} />\n )}\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"CourseBuilderPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/authoring/CourseBuilderPage.tsx"],"sourcesContent":["/**\n * CourseBuilderPage — the per-course authoring workspace.\n * @module buildmyiq/pages/authoring\n *\n * Loads one course (`useBuiCourse`) and exposes four tabs:\n * - Details — editable course meta (`useBuiUpdateCourse`).\n * - Curriculum — the module/lesson builder (`useBuiModules`, `useBuiCreateModule`,\n * `useBuiCreateLesson`) plus a right rail with the cover-image dropzone\n * (EvidenceUpload → coverFileId) and a live course-details summary.\n * - Assessments — course assessments (`useBuiAssessments`) linking out to the\n * assessment editor.\n * - Discussion — the shared wspace-conversations thread (`BuiDiscussion`).\n *\n * Publish (`useBuiPublishCourse`) and Archive (`useBuiArchiveCourse`) live in the\n * page header so they are reachable from every tab. Every list carries loading /\n * error / empty states; all data is live `bui*` data — no fabrication.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport {\n Archive,\n ArrowLeft,\n BarChart3,\n BookOpen,\n Calendar,\n ClipboardList,\n Clock,\n Eye,\n FileText,\n Globe,\n Image as ImageIcon,\n Link2,\n ListChecks,\n MessageSquare,\n MousePointerClick,\n PlayCircle,\n Plus,\n Radio,\n Send,\n type LucideIcon,\n} from 'lucide-react';\nimport { Button, GlassCard, Input, Switch, Textarea } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n Tabs,\n type TabItem,\n StatusBadge,\n EmptyState,\n LoadingView,\n ErrorView,\n Section,\n EvidenceUpload,\n BuiDiscussion,\n} from '../../components';\nimport { Modal, Field, SelectField, NO_SKILL } from './_forms';\nimport { useBuildmyiq } from '../../context';\nimport {\n useBuiCourse,\n useBuiUpdateCourse,\n useBuiPublishCourse,\n useBuiArchiveCourse,\n useBuiModules,\n useBuiCreateModule,\n useBuiCreateLesson,\n useBuiAssessments,\n useBuiSkills,\n type UploadedBuiFile,\n} from '../../hooks';\nimport { COURSE_LEVELS, LESSON_TYPES } from '../../types';\nimport type {\n BuiCourse,\n BuiCourseLevel,\n BuiCreateLessonInput,\n BuiCreateModuleInput,\n BuiLesson,\n BuiLessonType,\n BuiModule,\n BuiUpdateCourseInput,\n} from '../../types';\nimport { cn } from '../../utils/cn';\nimport { formatDate, formatDuration, formatEnumLabel } from '../../utils/formatters';\n\ntype BuilderTab = 'details' | 'curriculum' | 'assessments' | 'discussion';\n\n/** Per-lesson-type icon + theme-safe tint (semantic tokens only). */\nconst LESSON_META: Record<BuiLessonType, { icon: LucideIcon; tint: string }> = {\n ARTICLE: { icon: FileText, tint: 'bg-bg-sunken text-text-secondary' },\n VIDEO: { icon: PlayCircle, tint: 'bg-[var(--color-accent-soft)] text-text-primary' },\n QUIZ: { icon: ListChecks, tint: 'bg-status-warning-bg-subtle text-status-warning-text' },\n ASSIGNMENT: {\n icon: ClipboardList,\n tint: 'bg-status-success-bg-subtle text-status-success-text',\n },\n INTERACTIVE: {\n icon: MousePointerClick,\n tint: 'bg-[color-mix(in_srgb,var(--color-accent-teal)_18%,transparent)] text-[var(--color-accent-teal)]',\n },\n LIVE_SESSION: {\n icon: Radio,\n tint: 'bg-[color-mix(in_srgb,var(--color-accent-primary)_16%,transparent)] text-[var(--color-accent-primary)]',\n },\n};\n\n// ============================================================================\n// Details tab — pure metadata form (publish/archive live in the page header,\n// cover upload lives in the Curriculum right rail).\n// ============================================================================\n\nconst DetailsTab: FC<{ course: BuiCourse }> = ({ course }) => {\n const updateCourse = useBuiUpdateCourse();\n const skillsQuery = useBuiSkills();\n const skills = skillsQuery.data ?? [];\n\n const [title, setTitle] = useState(course.title);\n const [summary, setSummary] = useState(course.summary ?? '');\n const [description, setDescription] = useState(course.description ?? '');\n const [level, setLevel] = useState<BuiCourseLevel>(course.level);\n const [language, setLanguage] = useState(course.language);\n const [estimatedHours, setEstimatedHours] = useState(String(course.estimatedHours ?? 0));\n const [skillId, setSkillId] = useState<string>(course.skillId ?? NO_SKILL);\n const [isPublic, setIsPublic] = useState(course.isPublic);\n const [saveError, setSaveError] = useState<string | null>(null);\n\n const handleSave = async () => {\n setSaveError(null);\n const input: BuiUpdateCourseInput = {\n title: title.trim() || course.title,\n summary: summary.trim() || undefined,\n description: description.trim() || undefined,\n level,\n language: language.trim() || 'en',\n estimatedHours: estimatedHours.trim() ? Number(estimatedHours) : 0,\n skillId: skillId !== NO_SKILL ? skillId : undefined,\n isPublic,\n };\n try {\n await updateCourse.mutateAsync({ id: course.id, input });\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : 'Failed to save changes.');\n }\n };\n\n return (\n <div className=\"mx-auto max-w-3xl\">\n <Section\n title=\"Course details\"\n description=\"Metadata learners see in the catalog.\"\n action={\n <Button onClick={handleSave} disabled={updateCourse.isPending}>\n {updateCourse.isPending ? 'Saving…' : 'Save changes'}\n </Button>\n }\n >\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <div className=\"grid gap-4\">\n <Field label=\"Title\" htmlFor=\"detail-title\" required>\n <Input id=\"detail-title\" value={title} onChange={(e) => setTitle(e.target.value)} />\n </Field>\n <Field label=\"Summary\" htmlFor=\"detail-summary\">\n <Textarea\n id=\"detail-summary\"\n rows={2}\n value={summary}\n onChange={(e) => setSummary(e.target.value)}\n placeholder=\"A short description shown on the catalog card.\"\n />\n </Field>\n <Field label=\"Description\" htmlFor=\"detail-description\">\n <Textarea\n id=\"detail-description\"\n rows={5}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n placeholder=\"The full course overview.\"\n />\n </Field>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <Field label=\"Level\">\n <SelectField\n value={level}\n onChange={(v) => setLevel(v as BuiCourseLevel)}\n options={COURSE_LEVELS.map((l) => ({ value: l, label: formatEnumLabel(l) }))}\n />\n </Field>\n <Field label=\"Language\" htmlFor=\"detail-language\">\n <Input\n id=\"detail-language\"\n value={language}\n onChange={(e) => setLanguage(e.target.value)}\n placeholder=\"en\"\n />\n </Field>\n <Field label=\"Estimated hours\" htmlFor=\"detail-hours\">\n <Input\n id=\"detail-hours\"\n type=\"number\"\n min={0}\n value={estimatedHours}\n onChange={(e) => setEstimatedHours(e.target.value)}\n />\n </Field>\n <Field label=\"Skill\">\n <SelectField\n value={skillId}\n onChange={setSkillId}\n options={[\n { value: NO_SKILL, label: skillsQuery.isLoading ? 'Loading…' : 'No skill' },\n ...skills.map((s) => ({ value: s.id, label: s.name })),\n ]}\n />\n </Field>\n </div>\n <label className=\"inline-flex cursor-pointer items-center gap-2 text-sm text-text-secondary\">\n <Switch checked={isPublic} onCheckedChange={setIsPublic} />\n Publicly visible in the catalog\n </label>\n {saveError && <p className=\"text-sm text-status-error-text\">{saveError}</p>}\n </div>\n </GlassCard>\n </Section>\n </div>\n );\n};\n\n// ============================================================================\n// Curriculum tab\n// ============================================================================\n\nconst AddModuleModal: FC<{ courseId: string; position: number; onClose: () => void }> = ({\n courseId,\n position,\n onClose,\n}) => {\n const createModule = useBuiCreateModule();\n const [title, setTitle] = useState('');\n const [description, setDescription] = useState('');\n const [error, setError] = useState<string | null>(null);\n\n const canSubmit = title.trim().length > 0 && !createModule.isPending;\n\n const handleSubmit = async () => {\n if (!canSubmit) return;\n setError(null);\n const input: BuiCreateModuleInput = {\n courseId,\n title: title.trim(),\n description: description.trim() || undefined,\n position,\n };\n try {\n await createModule.mutateAsync(input);\n onClose();\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to add the module.');\n }\n };\n\n return (\n <Modal\n open\n onClose={onClose}\n title=\"Add module\"\n footer={\n <>\n <Button variant=\"ghost\" onClick={onClose} disabled={createModule.isPending}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={!canSubmit}>\n {createModule.isPending ? 'Adding…' : 'Add module'}\n </Button>\n </>\n }\n >\n <div className=\"grid gap-4\">\n <Field label=\"Module title\" htmlFor=\"module-title\" required>\n <Input\n id=\"module-title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"e.g. Getting started\"\n />\n </Field>\n <Field label=\"Description\" htmlFor=\"module-description\">\n <Textarea\n id=\"module-description\"\n rows={3}\n value={description}\n onChange={(e) => setDescription(e.target.value)}\n />\n </Field>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n </Modal>\n );\n};\n\nconst AddLessonModal: FC<{\n courseId: string;\n moduleId: string;\n position: number;\n onClose: () => void;\n}> = ({ courseId, moduleId, position, onClose }) => {\n const createLesson = useBuiCreateLesson();\n const [title, setTitle] = useState('');\n const [type, setType] = useState<BuiLessonType>('ARTICLE');\n const [content, setContent] = useState('');\n const [durationMin, setDurationMin] = useState('');\n const [isPreview, setIsPreview] = useState(false);\n const [media, setMedia] = useState<UploadedBuiFile | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n const canSubmit = title.trim().length > 0 && !createLesson.isPending;\n\n const handleSubmit = async () => {\n if (!canSubmit) return;\n setError(null);\n const input: BuiCreateLessonInput = {\n courseId,\n moduleId,\n title: title.trim(),\n type,\n content: content.trim() || undefined,\n durationMin: durationMin.trim() ? Number(durationMin) : undefined,\n position,\n isPreview,\n mediaFileId: media?.fileId,\n };\n try {\n await createLesson.mutateAsync(input);\n onClose();\n } catch (e) {\n setError(e instanceof Error ? e.message : 'Failed to add the lesson.');\n }\n };\n\n return (\n <Modal\n open\n onClose={onClose}\n title=\"Add lesson\"\n className=\"sm:max-w-xl\"\n footer={\n <>\n <Button variant=\"ghost\" onClick={onClose} disabled={createLesson.isPending}>\n Cancel\n </Button>\n <Button onClick={handleSubmit} disabled={!canSubmit}>\n {createLesson.isPending ? 'Adding…' : 'Add lesson'}\n </Button>\n </>\n }\n >\n <div className=\"grid gap-4\">\n <Field label=\"Lesson title\" htmlFor=\"lesson-title\" required>\n <Input\n id=\"lesson-title\"\n value={title}\n onChange={(e) => setTitle(e.target.value)}\n placeholder=\"e.g. What is data literacy?\"\n />\n </Field>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <Field label=\"Type\">\n <SelectField\n value={type}\n onChange={(v) => setType(v as BuiLessonType)}\n options={LESSON_TYPES.map((t) => ({ value: t, label: formatEnumLabel(t) }))}\n />\n </Field>\n <Field label=\"Duration (minutes)\" htmlFor=\"lesson-duration\">\n <Input\n id=\"lesson-duration\"\n type=\"number\"\n min={0}\n value={durationMin}\n onChange={(e) => setDurationMin(e.target.value)}\n placeholder=\"0\"\n />\n </Field>\n </div>\n <Field\n label=\"Content\"\n htmlFor=\"lesson-content\"\n hint=\"Lesson body — markdown or plain text.\"\n >\n <Textarea\n id=\"lesson-content\"\n rows={5}\n value={content}\n onChange={(e) => setContent(e.target.value)}\n />\n </Field>\n <EvidenceUpload\n id=\"lesson-media\"\n label=\"Media (optional)\"\n value={media}\n onChange={setMedia}\n hint=\"Attach a video, audio file or PDF for this lesson.\"\n />\n <label className=\"inline-flex cursor-pointer items-center gap-2 text-sm text-text-secondary\">\n <Switch checked={isPreview} onCheckedChange={setIsPreview} />\n Free preview lesson\n </label>\n {error && <p className=\"text-sm text-status-error-text\">{error}</p>}\n </div>\n </Modal>\n );\n};\n\nconst LessonRow: FC<{ lesson: BuiLesson }> = ({ lesson }) => {\n const meta = LESSON_META[lesson.type];\n const Icon = meta.icon;\n return (\n <li className=\"flex items-center gap-3 rounded-lg border border-border-seam bg-bg-surface px-3 py-2.5\">\n <span\n className={cn('flex h-8 w-8 shrink-0 items-center justify-center rounded-lg', meta.tint)}\n aria-hidden=\"true\"\n >\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.durationMin > 0 && (\n <span className=\"inline-flex shrink-0 items-center gap-1 text-xs text-text-secondary\">\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 bg-[var(--color-accent-soft)] px-2 py-0.5 text-xs font-medium text-text-primary\">\n Preview\n </span>\n )}\n </li>\n );\n};\n\nconst ModuleCard: FC<{ module: BuiModule; index: number; onAddLesson: () => void }> = ({\n module,\n index,\n onAddLesson,\n}) => {\n const lessons = [...(module.lessons ?? [])].sort((a, b) => a.position - b.position);\n const totalMin = lessons.reduce((sum, l) => sum + (l.durationMin || 0), 0);\n return (\n <GlassCard treatment=\"plain\" className=\"overflow-hidden\">\n <div className=\"flex flex-wrap items-center gap-3 border-b border-border-seam bg-bg-sunken/40 px-4 py-3\">\n <span\n className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-action-primary-bg text-sm font-semibold tabular-nums text-action-primary-text\"\n aria-hidden=\"true\"\n >\n {index + 1}\n </span>\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"truncate font-semibold text-text-primary\">\n <span className=\"text-text-tertiary\">Module {index + 1} · </span>\n {module.title}\n </h3>\n <p className=\"text-xs text-text-secondary\">\n {lessons.length} {lessons.length === 1 ? 'lesson' : 'lessons'}\n {totalMin > 0 && ` · ${formatDuration(totalMin)}`}\n </p>\n </div>\n <Button variant=\"outline\" size=\"sm\" onClick={onAddLesson} className=\"shrink-0\">\n <Plus className=\"mr-1.5 h-4 w-4\" /> Add lesson\n </Button>\n </div>\n {module.description && (\n <p className=\"border-b border-border-seam px-4 py-2.5 text-sm text-text-secondary\">\n {module.description}\n </p>\n )}\n <div className=\"p-3\">\n {lessons.length === 0 ? (\n <button\n type=\"button\"\n onClick={onAddLesson}\n className=\"flex w-full items-center justify-center gap-2 rounded-lg border border-dashed border-border-seam px-3 py-3 text-sm text-text-secondary transition-colors hover:border-[var(--color-action-primary-bg)] hover:text-text-primary\"\n >\n <Plus className=\"h-4 w-4\" /> Add the first lesson\n </button>\n ) : (\n <ul className=\"flex flex-col gap-2\">\n {lessons.map((lesson) => (\n <LessonRow key={lesson.id} lesson={lesson} />\n ))}\n </ul>\n )}\n </div>\n </GlassCard>\n );\n};\n\nconst DetailRow: FC<{ icon: ReactNode; label: string; value: string }> = ({\n icon,\n label,\n value,\n}) => (\n <div className=\"flex items-center gap-3 py-2.5\">\n <span className=\"shrink-0 text-text-tertiary\" aria-hidden=\"true\">\n {icon}\n </span>\n <span className=\"text-sm text-text-secondary\">{label}</span>\n <span className=\"ml-auto min-w-0 truncate text-right text-sm font-medium text-text-primary\">\n {value}\n </span>\n </div>\n);\n\nconst CurriculumTab: FC<{ course: BuiCourse }> = ({ course }) => {\n const courseId = course.id;\n const modulesQuery = useBuiModules(courseId);\n const updateCourse = useBuiUpdateCourse();\n const [showAddModule, setShowAddModule] = useState(false);\n const [lessonModuleId, setLessonModuleId] = useState<string | null>(null);\n const [coverFile, setCoverFile] = useState<UploadedBuiFile | null>(null);\n const [coverError, setCoverError] = useState<string | null>(null);\n\n const modules = [...(modulesQuery.data ?? [])].sort((a, b) => a.position - b.position);\n const lessonModule = modules.find((m) => m.id === lessonModuleId) ?? null;\n\n const handleCoverChange = async (file: UploadedBuiFile | null) => {\n setCoverFile(file);\n if (!file) return;\n setCoverError(null);\n try {\n await updateCourse.mutateAsync({ id: courseId, input: { coverFileId: file.fileId } });\n } catch (e) {\n setCoverError(e instanceof Error ? e.message : 'Failed to update the cover.');\n }\n };\n\n return (\n <div className=\"grid gap-6 lg:grid-cols-3\">\n {/* Builder */}\n <div className=\"lg:col-span-2\">\n <Section\n title=\"Curriculum\"\n description=\"Organise the course into modules and lessons.\"\n action={\n <Button onClick={() => setShowAddModule(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Add module\n </Button>\n }\n >\n {modulesQuery.isLoading ? (\n <LoadingView label=\"Loading curriculum…\" />\n ) : modulesQuery.isError ? (\n <ErrorView\n message=\"We couldn't load the curriculum.\"\n onRetry={() => modulesQuery.refetch()}\n />\n ) : modules.length === 0 ? (\n <EmptyState\n illustration=\"onboarding\"\n title=\"No modules yet\"\n description=\"Add your first module, then fill it with lessons.\"\n action={\n <Button onClick={() => setShowAddModule(true)}>\n <Plus className=\"mr-1.5 h-4 w-4\" /> Add module\n </Button>\n }\n />\n ) : (\n <div className=\"flex flex-col gap-4\">\n {modules.map((mod, idx) => (\n <ModuleCard\n key={mod.id}\n module={mod}\n index={idx}\n onAddLesson={() => setLessonModuleId(mod.id)}\n />\n ))}\n <button\n type=\"button\"\n onClick={() => setShowAddModule(true)}\n className=\"flex w-full items-center justify-center gap-2 rounded-xl border-2 border-dashed border-border-seam px-4 py-4 text-sm font-medium text-text-secondary transition-colors hover:border-[var(--color-action-primary-bg)] hover:bg-bg-sunken/40 hover:text-text-primary\"\n >\n <Plus className=\"h-4 w-4\" /> Add module\n </button>\n </div>\n )}\n </Section>\n </div>\n\n {/* Right rail: cover + details */}\n <div className=\"flex flex-col gap-6\">\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <h3 className=\"text-base font-semibold text-text-primary\">Course cover</h3>\n <p className=\"mt-0.5 text-sm text-text-secondary\">\n Upload an image to represent your course.\n </p>\n <div className=\"mt-3 aspect-video w-full overflow-hidden rounded-xl border border-border-seam bg-bg-sunken\">\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 items-center justify-center gap-2 text-sm text-text-tertiary\">\n <ImageIcon className=\"h-5 w-5\" /> No cover yet\n </div>\n )}\n </div>\n <div className=\"mt-3\">\n <EvidenceUpload\n id=\"course-cover\"\n label={course.cover?.downloadUrl ? 'Replace cover' : 'Upload cover'}\n value={coverFile}\n onChange={handleCoverChange}\n accept=\"image/*\"\n hint=\"PNG or JPG · 16:9 ratio recommended.\"\n />\n {coverError && <p className=\"mt-1 text-xs text-status-error-text\">{coverError}</p>}\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <h3 className=\"text-base font-semibold text-text-primary\">Course details</h3>\n <dl className=\"mt-2 flex flex-col divide-y divide-border-seam\">\n <DetailRow icon={<Link2 className=\"h-4 w-4\" />} label=\"Slug\" value={course.slug} />\n <DetailRow\n icon={<Globe className=\"h-4 w-4\" />}\n label=\"Language\"\n value={course.language ? course.language.toUpperCase() : '—'}\n />\n <DetailRow\n icon={<BarChart3 className=\"h-4 w-4\" />}\n label=\"Level\"\n value={formatEnumLabel(course.level)}\n />\n <DetailRow\n icon={<Eye className=\"h-4 w-4\" />}\n label=\"Visibility\"\n value={course.isPublic ? 'Public' : 'Private'}\n />\n <DetailRow\n icon={<Calendar className=\"h-4 w-4\" />}\n label=\"Created\"\n value={formatDate(course.createdAt)}\n />\n </dl>\n </GlassCard>\n </div>\n\n {showAddModule && (\n <AddModuleModal\n courseId={courseId}\n position={modules.length}\n onClose={() => setShowAddModule(false)}\n />\n )}\n {lessonModuleId && (\n <AddLessonModal\n courseId={courseId}\n moduleId={lessonModuleId}\n position={lessonModule?.lessons?.length ?? 0}\n onClose={() => setLessonModuleId(null)}\n />\n )}\n </div>\n );\n};\n\n// ============================================================================\n// Assessments tab\n// ============================================================================\n\nconst AssessmentsTab: FC<{ courseId: string }> = ({ courseId }) => {\n const { navigateTo } = useBuildmyiq();\n const assessmentsQuery = useBuiAssessments(courseId);\n const assessments = assessmentsQuery.data ?? [];\n\n return (\n <Section\n title=\"Assessments\"\n description=\"Quizzes, exams and assignments attached to this course.\"\n >\n {assessmentsQuery.isLoading ? (\n <LoadingView label=\"Loading assessments…\" />\n ) : assessmentsQuery.isError ? (\n <ErrorView\n message=\"We couldn't load the assessments.\"\n onRetry={() => assessmentsQuery.refetch()}\n />\n ) : assessments.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No assessments yet\"\n description=\"Create an assessment in the Assessments workspace to test learners on this course.\"\n action={\n <Button variant=\"outline\" onClick={() => navigateTo('assessments')}>\n Open assessments\n </Button>\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2\">\n {assessments.map((a) => (\n <GlassCard\n key={a.id}\n treatment=\"plain\"\n className=\"flex cursor-pointer flex-col gap-2 p-4 transition-shadow hover:shadow-lg\"\n onClick={() => navigateTo(`assessments/${a.id}`)}\n >\n <div className=\"flex items-start justify-between gap-2\">\n <h3 className=\"min-w-0 font-semibold text-text-primary\">{a.title}</h3>\n <StatusBadge status={a.status} />\n </div>\n {a.description && (\n <p className=\"line-clamp-2 text-sm text-text-secondary\">{a.description}</p>\n )}\n <div className=\"mt-auto flex flex-wrap items-center gap-3 pt-1 text-xs text-text-secondary\">\n <span className=\"rounded-full bg-bg-sunken px-2 py-0.5\">\n {formatEnumLabel(a.type)}\n </span>\n <span>Pass ≥ {a.passingScore ?? 0}%</span>\n {a.timeLimitMin > 0 && (\n <span className=\"inline-flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" /> {formatDuration(a.timeLimitMin)}\n </span>\n )}\n </div>\n </GlassCard>\n ))}\n </div>\n )}\n </Section>\n );\n};\n\n// ============================================================================\n// Page\n// ============================================================================\n\nexport const CourseBuilderPage: FC = () => {\n const { courseId } = useParams<{ courseId: string }>();\n const { navigateTo } = useBuildmyiq();\n const [tab, setTab] = useState<BuilderTab>('details');\n const courseQuery = useBuiCourse(courseId);\n const course = courseQuery.data ?? null;\n\n const publishCourse = useBuiPublishCourse();\n const archiveCourse = useBuiArchiveCourse();\n const [actionError, setActionError] = useState<string | null>(null);\n const [confirmArchive, setConfirmArchive] = useState(false);\n\n const handlePublish = async () => {\n if (!course) return;\n setActionError(null);\n try {\n await publishCourse.mutateAsync(course.id);\n } catch (e) {\n setActionError(e instanceof Error ? e.message : 'Failed to publish the course.');\n }\n };\n\n const handleArchive = async () => {\n if (!course) return;\n setActionError(null);\n try {\n await archiveCourse.mutateAsync(course.id);\n setConfirmArchive(false);\n } catch (e) {\n setActionError(e instanceof Error ? e.message : 'Failed to archive the course.');\n }\n };\n\n if (courseQuery.isLoading) {\n return (\n <PageLayout title=\"Course builder\">\n <LoadingView label=\"Loading course…\" />\n </PageLayout>\n );\n }\n\n if (courseQuery.isError) {\n return (\n <PageLayout title=\"Course builder\">\n <ErrorView message=\"We couldn't load this course.\" onRetry={() => courseQuery.refetch()} />\n </PageLayout>\n );\n }\n\n if (!courseId || !course) {\n return (\n <PageLayout title=\"Course builder\">\n <EmptyState\n illustration=\"empty-search\"\n title=\"Course not found\"\n description=\"This course may have been archived or removed.\"\n action={<Button onClick={() => navigateTo('authoring')}>Back to authoring</Button>}\n />\n </PageLayout>\n );\n }\n\n const canPublish = course.status === 'DRAFT' || course.status === 'IN_REVIEW';\n const canArchive = course.status !== 'ARCHIVED';\n\n const tabs: TabItem<BuilderTab>[] = [\n { value: 'details', label: 'Details', icon: <FileText className=\"h-4 w-4\" /> },\n {\n value: 'curriculum',\n label: 'Curriculum',\n icon: <BookOpen className=\"h-4 w-4\" />,\n count: course.modules?.length ?? 0,\n },\n { value: 'assessments', label: 'Assessments', icon: <ListChecks className=\"h-4 w-4\" /> },\n { value: 'discussion', label: 'Discussion', icon: <MessageSquare className=\"h-4 w-4\" /> },\n ];\n\n return (\n <PageLayout\n title={\n <span className=\"flex flex-wrap items-center gap-3\">\n <span className=\"min-w-0\">{course.title}</span>\n <StatusBadge status={course.status} />\n <StatusBadge status={course.level} />\n </span>\n }\n description={course.summary ?? undefined}\n actions={\n <>\n <Button variant=\"ghost\" onClick={() => navigateTo('authoring')}>\n <ArrowLeft className=\"mr-1.5 h-4 w-4\" /> Back\n </Button>\n {canArchive && (\n <Button variant=\"outline\" onClick={() => setConfirmArchive(true)}>\n <Archive className=\"mr-1.5 h-4 w-4\" /> Archive\n </Button>\n )}\n {canPublish && (\n <Button onClick={handlePublish} disabled={publishCourse.isPending}>\n <Send className=\"mr-1.5 h-4 w-4\" />\n {publishCourse.isPending ? 'Publishing…' : 'Publish'}\n </Button>\n )}\n </>\n }\n >\n <div className=\"flex flex-col gap-5\">\n {actionError && (\n <p className=\"rounded-lg border border-status-error-bg-subtle bg-status-error-bg-subtle px-3 py-2 text-sm text-status-error-text\">\n {actionError}\n </p>\n )}\n\n <Tabs items={tabs} value={tab} onChange={setTab} />\n\n {tab === 'details' && <DetailsTab key={course.id} course={course} />}\n {tab === 'curriculum' && <CurriculumTab course={course} />}\n {tab === 'assessments' && <AssessmentsTab courseId={courseId} />}\n {tab === 'discussion' && (\n <BuiDiscussion entityType=\"buildmyiq-course\" entityId={courseId} />\n )}\n </div>\n\n {confirmArchive && (\n <Modal\n open\n onClose={() => setConfirmArchive(false)}\n title=\"Archive this course?\"\n description=\"Archived courses are hidden from the catalog and cannot be enrolled in.\"\n footer={\n <>\n <Button\n variant=\"ghost\"\n onClick={() => setConfirmArchive(false)}\n disabled={archiveCourse.isPending}\n >\n Cancel\n </Button>\n <Button variant=\"destructive\" onClick={handleArchive} disabled={archiveCourse.isPending}>\n {archiveCourse.isPending ? 'Archiving…' : 'Archive course'}\n </Button>\n </>\n }\n >\n <p className=\"text-sm text-text-secondary\">\n You can still find it in Authoring under the Archived filter.\n </p>\n </Modal>\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CatalogPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/catalog/CatalogPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAwBhC,eAAO,MAAM,WAAW,EAAE,EAkHzB,CAAC"}
1
+ {"version":3,"file":"CatalogPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/catalog/CatalogPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAwBhC,eAAO,MAAM,WAAW,EAAE,EAwIzB,CAAC"}
@@ -14,84 +14,94 @@ import { CourseCard as u } from "../_shared/CourseCard.js";
14
14
  import "../_shared/index.js";
15
15
  import { COURSE_LEVELS as d } from "../../types/index.js";
16
16
  import { useMemo as f, useState as p } from "react";
17
- import { Search as m } from "lucide-react";
18
- import { jsx as h, jsxs as g } from "react/jsx-runtime";
19
- import { Input as _ } from "@burdenoff/fe-libs/ui";
17
+ import { LibraryBig as m, Search as h } from "lucide-react";
18
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
19
+ import { Input as v } from "@burdenoff/fe-libs/ui";
20
20
  //#region src/buildmyiq/pages/catalog/CatalogPage.tsx
21
- var v = 24, y = () => {
22
- let { navigateTo: y } = e(), b = l(), [x, S] = p("ALL"), [C, w] = p(""), T = c({
21
+ var y = 24, b = () => {
22
+ let { navigateTo: b } = e(), x = l(), [S, C] = p("ALL"), [w, T] = p(""), E = c({
23
23
  status: "PUBLISHED",
24
- level: x === "ALL" ? void 0 : x,
25
- cursor: b.cursor,
26
- limit: v
27
- }), E = T.data?.items ?? [], D = f(() => [{
24
+ level: S === "ALL" ? void 0 : S,
25
+ cursor: x.cursor,
26
+ limit: y
27
+ }), D = E.data?.items ?? [], O = f(() => [{
28
28
  value: "ALL",
29
29
  label: "All levels"
30
30
  }, ...d.map((e) => ({
31
31
  value: e,
32
32
  label: n(e)
33
- }))], []), O = C.trim().toLowerCase(), k = f(() => O ? E.filter((e) => `${e.title} ${e.summary ?? ""}`.toLowerCase().includes(O)) : E, [E, O]);
34
- return /* @__PURE__ */ h(t, {
33
+ }))], []), k = w.trim().toLowerCase(), A = f(() => k ? D.filter((e) => `${e.title} ${e.summary ?? ""}`.toLowerCase().includes(k)) : D, [D, k]), j = (e) => {
34
+ e !== S && (C(e), T(""), x.reset());
35
+ }, M = E.data?.pageInfo.hasMore ?? !1, N = !E.isLoading && !E.isError && D.length > 0 && A.length > 0, P = (() => {
36
+ let e = A.length, t = `course${e === 1 ? "" : "s"}`;
37
+ return k ? `${e} ${t} matching “${w.trim()}” on this page` : M ? `Showing ${e} ${t} — more on the next page` : `${e} ${t}`;
38
+ })();
39
+ return /* @__PURE__ */ g(t, {
35
40
  title: "Course Catalog",
36
41
  description: "Browse and enrol in published courses across your workspace.",
37
42
  titleGradient: !0,
38
43
  aurora: !0,
39
- children: /* @__PURE__ */ g("div", {
44
+ children: /* @__PURE__ */ _("div", {
40
45
  className: "flex flex-col gap-6",
41
46
  children: [
42
- /* @__PURE__ */ g("div", {
47
+ /* @__PURE__ */ _("div", {
43
48
  className: "flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between",
44
- children: [/* @__PURE__ */ h(o, {
45
- options: D,
46
- value: x,
47
- onChange: (e) => {
48
- e !== x && (S(e), b.reset());
49
- }
50
- }), /* @__PURE__ */ g("div", {
51
- className: "relative w-full max-w-xs",
52
- children: [/* @__PURE__ */ h(m, {
49
+ children: [/* @__PURE__ */ g(o, {
50
+ options: O,
51
+ value: S,
52
+ onChange: j
53
+ }), /* @__PURE__ */ _("div", {
54
+ className: "relative w-full min-w-0 lg:max-w-xs",
55
+ children: [/* @__PURE__ */ g(h, {
53
56
  className: "pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary",
54
57
  "aria-hidden": !0
55
- }), /* @__PURE__ */ h(_, {
56
- value: C,
57
- onChange: (e) => w(e.target.value),
58
+ }), /* @__PURE__ */ g(v, {
59
+ value: w,
60
+ onChange: (e) => T(e.target.value),
58
61
  placeholder: "Search this page…",
59
62
  className: "pl-9",
60
63
  "aria-label": "Search courses on this page"
61
64
  })]
62
65
  })]
63
66
  }),
64
- T.isLoading ? /* @__PURE__ */ h(a, { label: "Loading the catalog…" }) : T.isError ? /* @__PURE__ */ h(i, {
67
+ N && /* @__PURE__ */ _("p", {
68
+ className: "-mt-2 inline-flex items-center gap-1.5 text-sm text-text-secondary",
69
+ children: [/* @__PURE__ */ g(m, {
70
+ className: "h-4 w-4 text-text-tertiary",
71
+ "aria-hidden": !0
72
+ }), P]
73
+ }),
74
+ E.isLoading ? /* @__PURE__ */ g(a, { label: "Loading the catalog…" }) : E.isError ? /* @__PURE__ */ g(i, {
65
75
  title: "Couldn't load courses",
66
- message: T.error instanceof Error ? T.error.message : void 0,
67
- onRetry: () => T.refetch()
68
- }) : E.length === 0 ? /* @__PURE__ */ h(r, {
76
+ message: E.error instanceof Error ? E.error.message : void 0,
77
+ onRetry: () => E.refetch()
78
+ }) : D.length === 0 ? /* @__PURE__ */ g(r, {
69
79
  illustration: "empty-data",
70
- title: x === "ALL" ? "No published courses yet" : `No ${n(x).toLowerCase()} courses yet`,
80
+ title: S === "ALL" ? "No published courses yet" : `No ${n(S).toLowerCase()} courses yet`,
71
81
  description: "Published courses appear here as instructors publish them. Check back soon or browse another level."
72
- }) : k.length === 0 ? /* @__PURE__ */ h(r, {
82
+ }) : A.length === 0 ? /* @__PURE__ */ g(r, {
73
83
  illustration: "empty-search",
74
84
  title: "No courses match your search",
75
- description: `Nothing on this page matches “${C.trim()}”. Try a different term or clear the search.`
76
- }) : /* @__PURE__ */ h("div", {
85
+ description: `Nothing on this page matches “${w.trim()}”. Try a different term or clear the search.`
86
+ }) : /* @__PURE__ */ g("div", {
77
87
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
78
- children: k.map((e) => /* @__PURE__ */ h(u, {
88
+ children: A.map((e) => /* @__PURE__ */ g(u, {
79
89
  course: e,
80
- onClick: () => y(`catalog/${e.id}`)
90
+ onClick: () => b(`catalog/${e.id}`)
81
91
  }, e.id))
82
92
  }),
83
- !T.isLoading && !T.isError && E.length > 0 && !O && /* @__PURE__ */ h(s, {
84
- page: b.page,
85
- pageSize: v,
86
- itemCount: E.length,
87
- hasMore: T.data?.pageInfo.hasMore ?? !1,
88
- onPageChange: (e) => b.onPageChange(e, T.data?.pageInfo.cursor)
93
+ !E.isLoading && !E.isError && D.length > 0 && !k && /* @__PURE__ */ g(s, {
94
+ page: x.page,
95
+ pageSize: y,
96
+ itemCount: D.length,
97
+ hasMore: M,
98
+ onPageChange: (e) => x.onPageChange(e, E.data?.pageInfo.cursor)
89
99
  })
90
100
  ]
91
101
  })
92
102
  });
93
103
  };
94
104
  //#endregion
95
- export { y as CatalogPage };
105
+ export { b as CatalogPage };
96
106
 
97
107
  //# sourceMappingURL=CatalogPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CatalogPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/catalog/CatalogPage.tsx"],"sourcesContent":["/**\n * CatalogPage — learner-facing course discovery.\n * @module buildmyiq/pages/catalog\n *\n * The primary page of the Catalog section. Browses PUBLISHED courses from the\n * live `buiListCourses` connection, filtered server-side by level and paged by\n * cursor. A client-side search box narrows the current page by title/summary.\n * Every course tile links to its detail/enrol page. Honest empty states only.\n */\n\nimport type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { Search } from 'lucide-react';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n FilterTabs,\n Pagination,\n EmptyState,\n LoadingView,\n ErrorView,\n type FilterOption,\n} from '../../components';\nimport { CourseCard } from '../_shared';\nimport { useBuildmyiq } from '../../context';\nimport { useBuiCourses, useCursorPager } from '../../hooks';\nimport { COURSE_LEVELS } from '../../types';\nimport type { BuiCourse, BuiCourseLevel } from '../../types';\nimport { formatEnumLabel } from '../../utils/formatters';\n\ntype LevelFilter = 'ALL' | BuiCourseLevel;\n\nconst PAGE_SIZE = 24;\n\nexport const CatalogPage: FC = () => {\n const { navigateTo } = useBuildmyiq();\n const pager = useCursorPager();\n\n const [level, setLevel] = useState<LevelFilter>('ALL');\n const [search, setSearch] = useState('');\n\n const coursesQuery = useBuiCourses({\n status: 'PUBLISHED',\n level: level === 'ALL' ? undefined : level,\n cursor: pager.cursor,\n limit: PAGE_SIZE,\n });\n\n const courses: BuiCourse[] = coursesQuery.data?.items ?? [];\n\n const levelOptions: FilterOption<LevelFilter>[] = useMemo(\n () => [\n { value: 'ALL', label: 'All levels' },\n ...COURSE_LEVELS.map((lvl) => ({ value: lvl, label: formatEnumLabel(lvl) })),\n ],\n []\n );\n\n const term = search.trim().toLowerCase();\n const filtered = useMemo(() => {\n if (!term) return courses;\n return courses.filter((course) =>\n `${course.title} ${course.summary ?? ''}`.toLowerCase().includes(term)\n );\n }, [courses, term]);\n\n const handleLevelChange = (next: LevelFilter): void => {\n if (next === level) return;\n setLevel(next);\n pager.reset();\n };\n\n return (\n <PageLayout\n title=\"Course Catalog\"\n description=\"Browse and enrol in published courses across your workspace.\"\n titleGradient\n aurora\n >\n <div className=\"flex flex-col gap-6\">\n {/* Toolbar: level filter + client-side search */}\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <FilterTabs options={levelOptions} value={level} onChange={handleLevelChange} />\n <div className=\"relative w-full max-w-xs\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary\"\n aria-hidden\n />\n <Input\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n placeholder=\"Search this page…\"\n className=\"pl-9\"\n aria-label=\"Search courses on this page\"\n />\n </div>\n </div>\n\n {/* List: loading → error → empty → data */}\n {coursesQuery.isLoading ? (\n <LoadingView label=\"Loading the catalog…\" />\n ) : coursesQuery.isError ? (\n <ErrorView\n title=\"Couldn't load courses\"\n message={coursesQuery.error instanceof Error ? coursesQuery.error.message : undefined}\n onRetry={() => coursesQuery.refetch()}\n />\n ) : courses.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title={\n level === 'ALL'\n ? 'No published courses yet'\n : `No ${formatEnumLabel(level).toLowerCase()} courses yet`\n }\n description=\"Published courses appear here as instructors publish them. Check back soon or browse another level.\"\n />\n ) : filtered.length === 0 ? (\n <EmptyState\n illustration=\"empty-search\"\n title=\"No courses match your search\"\n description={`Nothing on this page matches “${search.trim()}”. Try a different term or clear the search.`}\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n {filtered.map((course) => (\n <CourseCard\n key={course.id}\n course={course}\n onClick={() => navigateTo(`catalog/${course.id}`)}\n />\n ))}\n </div>\n )}\n\n {/* Server pagination — hidden while a search term narrows the current page */}\n {!coursesQuery.isLoading && !coursesQuery.isError && courses.length > 0 && !term && (\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={courses.length}\n hasMore={coursesQuery.data?.pageInfo.hasMore ?? false}\n onPageChange={(next) => pager.onPageChange(next, coursesQuery.data?.pageInfo.cursor)}\n />\n )}\n </div>\n </PageLayout>\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAgCA,IAAM,IAAY,IAEL,UAAwB;CACnC,IAAM,EAAE,kBAAe,GAAc,EAC/B,IAAQ,GAAgB,EAExB,CAAC,GAAO,KAAY,EAAsB,MAAM,EAChD,CAAC,GAAQ,KAAa,EAAS,GAAG,EAElC,IAAe,EAAc;EACjC,QAAQ;EACR,OAAO,MAAU,QAAQ,KAAA,IAAY;EACrC,QAAQ,EAAM;EACd,OAAO;EACR,CAAC,EAEI,IAAuB,EAAa,MAAM,SAAS,EAAE,EAErD,IAA4C,QAC1C,CACJ;EAAE,OAAO;EAAO,OAAO;EAAc,EACrC,GAAG,EAAc,KAAK,OAAS;EAAE,OAAO;EAAK,OAAO,EAAgB,EAAI;EAAE,EAAE,CAC7E,EACD,EAAE,CACH,EAEK,IAAO,EAAO,MAAM,CAAC,aAAa,EAClC,IAAW,QACV,IACE,EAAQ,QAAQ,MACrB,GAAG,EAAO,MAAM,GAAG,EAAO,WAAW,KAAK,aAAa,CAAC,SAAS,EAAK,CACvE,GAHiB,GAIjB,CAAC,GAAS,EAAK,CAAC;AAQnB,QACE,kBAAC,GAAD;EACE,OAAM;EACN,aAAY;EACZ,eAAA;EACA,QAAA;YAEA,kBAAC,OAAD;GAAK,WAAU;aAAf;IAEE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAY,SAAS;MAAc,OAAO;MAAO,WAhB9B,MAA4B;AACjD,aAAS,MACb,EAAS,EAAK,EACd,EAAM,OAAO;;MAayE,CAAA,EAChF,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,WAAU;OACV,eAAA;OACA,CAAA,EACF,kBAAC,GAAD;OACE,OAAO;OACP,WAAW,MAAU,EAAU,EAAM,OAAO,MAAM;OAClD,aAAY;OACZ,WAAU;OACV,cAAW;OACX,CAAA,CACE;QACF;;IAGL,EAAa,YACZ,kBAAC,GAAD,EAAa,OAAM,wBAAyB,CAAA,GAC1C,EAAa,UACf,kBAAC,GAAD;KACE,OAAM;KACN,SAAS,EAAa,iBAAiB,QAAQ,EAAa,MAAM,UAAU,KAAA;KAC5E,eAAe,EAAa,SAAS;KACrC,CAAA,GACA,EAAQ,WAAW,IACrB,kBAAC,GAAD;KACE,cAAa;KACb,OACE,MAAU,QACN,6BACA,MAAM,EAAgB,EAAM,CAAC,aAAa,CAAC;KAEjD,aAAY;KACZ,CAAA,GACA,EAAS,WAAW,IACtB,kBAAC,GAAD;KACE,cAAa;KACb,OAAM;KACN,aAAa,iCAAiC,EAAO,MAAM,CAAC;KAC5D,CAAA,GAEF,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAS,KAAK,MACb,kBAAC,GAAD;MAEU;MACR,eAAe,EAAW,WAAW,EAAO,KAAK;MACjD,EAHK,EAAO,GAGZ,CACF;KACE,CAAA;IAIP,CAAC,EAAa,aAAa,CAAC,EAAa,WAAW,EAAQ,SAAS,KAAK,CAAC,KAC1E,kBAAC,GAAD;KACE,MAAM,EAAM;KACZ,UAAU;KACV,WAAW,EAAQ;KACnB,SAAS,EAAa,MAAM,SAAS,WAAW;KAChD,eAAe,MAAS,EAAM,aAAa,GAAM,EAAa,MAAM,SAAS,OAAO;KACpF,CAAA;IAEA;;EACK,CAAA"}
1
+ {"version":3,"file":"CatalogPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/catalog/CatalogPage.tsx"],"sourcesContent":["/**\n * CatalogPage — learner-facing course discovery.\n * @module buildmyiq/pages/catalog\n *\n * The primary page of the Catalog section. Browses PUBLISHED courses from the\n * live `buiListCourses` connection, filtered server-side by level and paged by\n * cursor. A client-side search box narrows the current page by title/summary.\n * Every course tile links to its detail/enrol page. Honest empty states only.\n */\n\nimport type { FC } from 'react';\nimport { useMemo, useState } from 'react';\nimport { LibraryBig, Search } from 'lucide-react';\nimport { Input } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n FilterTabs,\n Pagination,\n EmptyState,\n LoadingView,\n ErrorView,\n type FilterOption,\n} from '../../components';\nimport { CourseCard } from '../_shared';\nimport { useBuildmyiq } from '../../context';\nimport { useBuiCourses, useCursorPager } from '../../hooks';\nimport { COURSE_LEVELS } from '../../types';\nimport type { BuiCourse, BuiCourseLevel } from '../../types';\nimport { formatEnumLabel } from '../../utils/formatters';\n\ntype LevelFilter = 'ALL' | BuiCourseLevel;\n\nconst PAGE_SIZE = 24;\n\nexport const CatalogPage: FC = () => {\n const { navigateTo } = useBuildmyiq();\n const pager = useCursorPager();\n\n const [level, setLevel] = useState<LevelFilter>('ALL');\n const [search, setSearch] = useState('');\n\n const coursesQuery = useBuiCourses({\n status: 'PUBLISHED',\n level: level === 'ALL' ? undefined : level,\n cursor: pager.cursor,\n limit: PAGE_SIZE,\n });\n\n const courses: BuiCourse[] = coursesQuery.data?.items ?? [];\n\n const levelOptions: FilterOption<LevelFilter>[] = useMemo(\n () => [\n { value: 'ALL', label: 'All levels' },\n ...COURSE_LEVELS.map((lvl) => ({ value: lvl, label: formatEnumLabel(lvl) })),\n ],\n []\n );\n\n const term = search.trim().toLowerCase();\n const filtered = useMemo(() => {\n if (!term) return courses;\n return courses.filter((course) =>\n `${course.title} ${course.summary ?? ''}`.toLowerCase().includes(term)\n );\n }, [courses, term]);\n\n const handleLevelChange = (next: LevelFilter): void => {\n if (next === level) return;\n setLevel(next);\n setSearch('');\n pager.reset();\n };\n\n const hasMore = coursesQuery.data?.pageInfo.hasMore ?? false;\n const hasResults =\n !coursesQuery.isLoading && !coursesQuery.isError && courses.length > 0 && filtered.length > 0;\n\n // Honest, page-scoped result summary (we only ever hold the current page).\n const resultSummary = (() => {\n const n = filtered.length;\n const noun = `course${n === 1 ? '' : 's'}`;\n if (term) return `${n} ${noun} matching “${search.trim()}” on this page`;\n if (hasMore) return `Showing ${n} ${noun} — more on the next page`;\n return `${n} ${noun}`;\n })();\n\n return (\n <PageLayout\n title=\"Course Catalog\"\n description=\"Browse and enrol in published courses across your workspace.\"\n titleGradient\n aurora\n >\n <div className=\"flex flex-col gap-6\">\n {/* Toolbar: level filter + client-side search */}\n <div className=\"flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between\">\n <FilterTabs options={levelOptions} value={level} onChange={handleLevelChange} />\n <div className=\"relative w-full min-w-0 lg:max-w-xs\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary\"\n aria-hidden\n />\n <Input\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n placeholder=\"Search this page…\"\n className=\"pl-9\"\n aria-label=\"Search courses on this page\"\n />\n </div>\n </div>\n\n {/* Result summary — only when a data grid is on screen */}\n {hasResults && (\n <p className=\"-mt-2 inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <LibraryBig className=\"h-4 w-4 text-text-tertiary\" aria-hidden />\n {resultSummary}\n </p>\n )}\n\n {/* List: loading → error → empty → data */}\n {coursesQuery.isLoading ? (\n <LoadingView label=\"Loading the catalog…\" />\n ) : coursesQuery.isError ? (\n <ErrorView\n title=\"Couldn't load courses\"\n message={coursesQuery.error instanceof Error ? coursesQuery.error.message : undefined}\n onRetry={() => coursesQuery.refetch()}\n />\n ) : courses.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title={\n level === 'ALL'\n ? 'No published courses yet'\n : `No ${formatEnumLabel(level).toLowerCase()} courses yet`\n }\n description=\"Published courses appear here as instructors publish them. Check back soon or browse another level.\"\n />\n ) : filtered.length === 0 ? (\n <EmptyState\n illustration=\"empty-search\"\n title=\"No courses match your search\"\n description={`Nothing on this page matches “${search.trim()}”. Try a different term or clear the search.`}\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n {filtered.map((course) => (\n <CourseCard\n key={course.id}\n course={course}\n onClick={() => navigateTo(`catalog/${course.id}`)}\n />\n ))}\n </div>\n )}\n\n {/* Server pagination — hidden while a search term narrows the current page */}\n {!coursesQuery.isLoading && !coursesQuery.isError && courses.length > 0 && !term && (\n <Pagination\n page={pager.page}\n pageSize={PAGE_SIZE}\n itemCount={courses.length}\n hasMore={hasMore}\n onPageChange={(next) => pager.onPageChange(next, coursesQuery.data?.pageInfo.cursor)}\n />\n )}\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"CourseDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/catalog/CourseDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAgFhC,eAAO,MAAM,gBAAgB,EAAE,EA+V9B,CAAC"}
1
+ {"version":3,"file":"CourseDetailPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/catalog/CourseDetailPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAa,MAAM,OAAO,CAAC;AAuG3C,eAAO,MAAM,gBAAgB,EAAE,EAge9B,CAAC"}