@burdenoff/microfe-buildmyiq 2026.705.4 → 2026.705.5

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 (125) hide show
  1. package/dist/buildmyiq/BuildmyiqRoot.d.ts +5 -0
  2. package/dist/buildmyiq/BuildmyiqRoot.d.ts.map +1 -0
  3. package/dist/buildmyiq/BuildmyiqRoutes.d.ts +4 -0
  4. package/dist/buildmyiq/BuildmyiqRoutes.d.ts.map +1 -0
  5. package/dist/buildmyiq/components/BuiDiscussion.d.ts +18 -0
  6. package/dist/buildmyiq/components/BuiDiscussion.d.ts.map +1 -0
  7. package/dist/buildmyiq/components/BuiTags.d.ts +17 -0
  8. package/dist/buildmyiq/components/BuiTags.d.ts.map +1 -0
  9. package/dist/buildmyiq/components/DataTable.d.ts +28 -0
  10. package/dist/buildmyiq/components/DataTable.d.ts.map +1 -0
  11. package/dist/buildmyiq/components/EmptyState.d.ts +18 -0
  12. package/dist/buildmyiq/components/EmptyState.d.ts.map +1 -0
  13. package/dist/buildmyiq/components/EvidenceUpload.d.ts +14 -0
  14. package/dist/buildmyiq/components/EvidenceUpload.d.ts.map +1 -0
  15. package/dist/buildmyiq/components/FileLink.d.ts +15 -0
  16. package/dist/buildmyiq/components/FileLink.d.ts.map +1 -0
  17. package/dist/buildmyiq/components/FilterTabs.d.ts +18 -0
  18. package/dist/buildmyiq/components/FilterTabs.d.ts.map +1 -0
  19. package/dist/buildmyiq/components/MetricsCard.d.ts +23 -0
  20. package/dist/buildmyiq/components/MetricsCard.d.ts.map +1 -0
  21. package/dist/buildmyiq/components/MiniBar.d.ts +16 -0
  22. package/dist/buildmyiq/components/MiniBar.d.ts.map +1 -0
  23. package/dist/buildmyiq/components/PageLayout.d.ts +16 -0
  24. package/dist/buildmyiq/components/PageLayout.d.ts.map +1 -0
  25. package/dist/buildmyiq/components/Pagination.d.ts +22 -0
  26. package/dist/buildmyiq/components/Pagination.d.ts.map +1 -0
  27. package/dist/buildmyiq/components/StateViews.d.ts +24 -0
  28. package/dist/buildmyiq/components/StateViews.d.ts.map +1 -0
  29. package/dist/buildmyiq/components/StatusBadge.d.ts +9 -0
  30. package/dist/buildmyiq/components/StatusBadge.d.ts.map +1 -0
  31. package/dist/buildmyiq/components/Tabs.d.ts +17 -0
  32. package/dist/buildmyiq/components/Tabs.d.ts.map +1 -0
  33. package/dist/buildmyiq/components/index.d.ts +19 -0
  34. package/dist/buildmyiq/components/index.d.ts.map +1 -0
  35. package/dist/buildmyiq/context/BuildmyiqContext.d.ts +16 -0
  36. package/dist/buildmyiq/context/BuildmyiqContext.d.ts.map +1 -0
  37. package/dist/buildmyiq/context/index.d.ts +6 -0
  38. package/dist/buildmyiq/context/index.d.ts.map +1 -0
  39. package/dist/buildmyiq/hooks/index.d.ts +10 -0
  40. package/dist/buildmyiq/hooks/index.d.ts.map +1 -0
  41. package/dist/buildmyiq/hooks/useBuiComments.d.ts +36 -0
  42. package/dist/buildmyiq/hooks/useBuiComments.d.ts.map +1 -0
  43. package/dist/buildmyiq/hooks/useBuiFiles.d.ts +47 -0
  44. package/dist/buildmyiq/hooks/useBuiFiles.d.ts.map +1 -0
  45. package/dist/buildmyiq/hooks/useBuiTags.d.ts +44 -0
  46. package/dist/buildmyiq/hooks/useBuiTags.d.ts.map +1 -0
  47. package/dist/buildmyiq/hooks/useBuildmyiqApi.d.ts +65 -0
  48. package/dist/buildmyiq/hooks/useBuildmyiqApi.d.ts.map +1 -0
  49. package/dist/buildmyiq/hooks/useBuildmyiqApi.js +1 -1
  50. package/dist/buildmyiq/hooks/useBuildmyiqApi.js.map +1 -1
  51. package/dist/buildmyiq/hooks/useCursorPager.d.ts +33 -0
  52. package/dist/buildmyiq/hooks/useCursorPager.d.ts.map +1 -0
  53. package/dist/buildmyiq/index.d.ts +9 -0
  54. package/dist/buildmyiq/index.d.ts.map +1 -0
  55. package/dist/buildmyiq/pages/DashboardPage.d.ts +4 -0
  56. package/dist/buildmyiq/pages/DashboardPage.d.ts.map +1 -0
  57. package/dist/buildmyiq/pages/_shared/CourseCard.d.ts +13 -0
  58. package/dist/buildmyiq/pages/_shared/CourseCard.d.ts.map +1 -0
  59. package/dist/buildmyiq/pages/_shared/CourseCard.js +2 -2
  60. package/dist/buildmyiq/pages/_shared/CourseCard.js.map +1 -1
  61. package/dist/buildmyiq/pages/_shared/index.d.ts +6 -0
  62. package/dist/buildmyiq/pages/_shared/index.d.ts.map +1 -0
  63. package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.d.ts +4 -0
  64. package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.d.ts.map +1 -0
  65. package/dist/buildmyiq/pages/assessments/AssessmentsPage.d.ts +4 -0
  66. package/dist/buildmyiq/pages/assessments/AssessmentsPage.d.ts.map +1 -0
  67. package/dist/buildmyiq/pages/authoring/AuthoringPage.d.ts +4 -0
  68. package/dist/buildmyiq/pages/authoring/AuthoringPage.d.ts.map +1 -0
  69. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.d.ts +4 -0
  70. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.d.ts.map +1 -0
  71. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js +2 -2
  72. package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js.map +1 -1
  73. package/dist/buildmyiq/pages/authoring/_forms.d.ts +44 -0
  74. package/dist/buildmyiq/pages/authoring/_forms.d.ts.map +1 -0
  75. package/dist/buildmyiq/pages/catalog/CatalogPage.d.ts +4 -0
  76. package/dist/buildmyiq/pages/catalog/CatalogPage.d.ts.map +1 -0
  77. package/dist/buildmyiq/pages/catalog/CourseDetailPage.d.ts +4 -0
  78. package/dist/buildmyiq/pages/catalog/CourseDetailPage.d.ts.map +1 -0
  79. package/dist/buildmyiq/pages/catalog/CourseDetailPage.js +2 -2
  80. package/dist/buildmyiq/pages/catalog/CourseDetailPage.js.map +1 -1
  81. package/dist/buildmyiq/pages/certificates/CertificatesPage.d.ts +4 -0
  82. package/dist/buildmyiq/pages/certificates/CertificatesPage.d.ts.map +1 -0
  83. package/dist/buildmyiq/pages/cohorts/CohortsPage.d.ts +4 -0
  84. package/dist/buildmyiq/pages/cohorts/CohortsPage.d.ts.map +1 -0
  85. package/dist/buildmyiq/pages/learning/LessonPlayerPage.d.ts +4 -0
  86. package/dist/buildmyiq/pages/learning/LessonPlayerPage.d.ts.map +1 -0
  87. package/dist/buildmyiq/pages/learning/MyLearningPage.d.ts +4 -0
  88. package/dist/buildmyiq/pages/learning/MyLearningPage.d.ts.map +1 -0
  89. package/dist/buildmyiq/pages/skills/SkillsPage.d.ts +4 -0
  90. package/dist/buildmyiq/pages/skills/SkillsPage.d.ts.map +1 -0
  91. package/dist/buildmyiq/pages/submissions/SubmissionsPage.d.ts +4 -0
  92. package/dist/buildmyiq/pages/submissions/SubmissionsPage.d.ts.map +1 -0
  93. package/dist/buildmyiq/types/index.d.ts +382 -0
  94. package/dist/buildmyiq/types/index.d.ts.map +1 -0
  95. package/dist/buildmyiq/types/index.js.map +1 -1
  96. package/dist/buildmyiq/utils/cn.d.ts +4 -0
  97. package/dist/buildmyiq/utils/cn.d.ts.map +1 -0
  98. package/dist/buildmyiq/utils/cn.js +2 -2
  99. package/dist/buildmyiq/utils/formatters.d.ts +33 -0
  100. package/dist/buildmyiq/utils/formatters.d.ts.map +1 -0
  101. package/dist/buildmyiq/utils/i18n.d.ts +6 -0
  102. package/dist/buildmyiq/utils/i18n.d.ts.map +1 -0
  103. package/dist/buildmyiq/utils/index.d.ts +9 -0
  104. package/dist/buildmyiq/utils/index.d.ts.map +1 -0
  105. package/dist/buildmyiq/utils/navigation.d.ts +14 -0
  106. package/dist/buildmyiq/utils/navigation.d.ts.map +1 -0
  107. package/dist/dev/main.d.ts +2 -0
  108. package/dist/dev/main.d.ts.map +1 -0
  109. package/dist/generated/global-operations.d.ts +2 -0
  110. package/dist/generated/global-operations.d.ts.map +1 -0
  111. package/dist/generated/global-types.d.ts +18199 -0
  112. package/dist/generated/global-types.d.ts.map +1 -0
  113. package/dist/generated/wspace-operations.d.ts +2 -0
  114. package/dist/generated/wspace-operations.d.ts.map +1 -0
  115. package/dist/generated/wspace-types.d.ts +43046 -0
  116. package/dist/generated/wspace-types.d.ts.map +1 -0
  117. package/dist/index.d.ts +2 -0
  118. package/dist/index.d.ts.map +1 -0
  119. package/dist/node_modules/{clsx → .bun/clsx@2.1.1/node_modules/clsx}/dist/clsx.js +1 -1
  120. package/dist/node_modules/.bun/clsx@2.1.1/node_modules/clsx/dist/clsx.js.map +1 -0
  121. package/dist/node_modules/{tailwind-merge → .bun/tailwind-merge@3.5.0/node_modules/tailwind-merge}/dist/bundle-mjs.js +170 -212
  122. package/dist/node_modules/.bun/tailwind-merge@3.5.0/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
  123. package/package.json +1 -1
  124. package/dist/node_modules/clsx/dist/clsx.js.map +0 -1
  125. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +0 -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?.url ? (\n <img src={course.cover.url} 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`), 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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"}
@@ -0,0 +1,44 @@
1
+ import { FC, ReactNode } from 'react';
2
+
3
+ /** Sentinel value for an "unset" optional Select (radix forbids an empty string value). */
4
+ export declare const NO_SKILL = "__none__";
5
+ /** Turn a free-text title into a URL-safe slug. */
6
+ export declare const slugify: (text: string) => string;
7
+ interface ModalProps {
8
+ open: boolean;
9
+ onClose: () => void;
10
+ title: string;
11
+ description?: string;
12
+ footer?: ReactNode;
13
+ className?: string;
14
+ children: ReactNode;
15
+ }
16
+ /** Controlled modal built on the fe-libs Dialog. Closes on ESC / overlay / X. */
17
+ export declare const Modal: FC<ModalProps>;
18
+ interface FieldProps {
19
+ label: string;
20
+ htmlFor?: string;
21
+ required?: boolean;
22
+ hint?: string;
23
+ error?: string;
24
+ className?: string;
25
+ children: ReactNode;
26
+ }
27
+ /** Label + control + hint/error wrapper for form rows. */
28
+ export declare const Field: FC<FieldProps>;
29
+ export interface SelectOption {
30
+ value: string;
31
+ label: string;
32
+ }
33
+ interface SelectFieldProps {
34
+ id?: string;
35
+ value: string;
36
+ onChange: (value: string) => void;
37
+ options: SelectOption[];
38
+ placeholder?: string;
39
+ disabled?: boolean;
40
+ }
41
+ /** A single-select control over the fe-libs Select primitive. */
42
+ export declare const SelectField: FC<SelectFieldProps>;
43
+ export {};
44
+ //# sourceMappingURL=_forms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_forms.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/authoring/_forms.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAiB3C,2FAA2F;AAC3F,eAAO,MAAM,QAAQ,aAAa,CAAC;AAEnC,mDAAmD;AACnD,eAAO,MAAM,OAAO,GAAI,MAAM,MAAM,KAAG,MAMtB,CAAC;AAElB,UAAU,UAAU;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,iFAAiF;AACjF,eAAO,MAAM,KAAK,EAAE,EAAE,CAAC,UAAU,CAwBhC,CAAC;AAEF,UAAU,UAAU;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED,0DAA0D;AAC1D,eAAO,MAAM,KAAK,EAAE,EAAE,CAAC,UAAU,CAkBhC,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,gBAAgB;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,iEAAiE;AACjE,eAAO,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CAoB5C,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { FC } from 'react';
2
+
3
+ export declare const CatalogPage: FC;
4
+ //# sourceMappingURL=CatalogPage.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,4 @@
1
+ import { FC } from 'react';
2
+
3
+ export declare const CourseDetailPage: FC;
4
+ //# sourceMappingURL=CourseDetailPage.d.ts.map
@@ -0,0 +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,EAuV9B,CAAC"}
@@ -88,8 +88,8 @@ var B = "buildmyiq-course", V = {
88
88
  className: "grid grid-cols-1 lg:grid-cols-[minmax(0,2fr)_minmax(0,3fr)]",
89
89
  children: [/* @__PURE__ */ I("div", {
90
90
  className: "relative h-48 w-full overflow-hidden bg-bg-sunken lg:h-full",
91
- children: K.cover?.url ? /* @__PURE__ */ I("img", {
92
- src: K.cover.url,
91
+ children: K.cover?.downloadUrl ? /* @__PURE__ */ I("img", {
92
+ src: K.cover.downloadUrl,
93
93
  alt: "",
94
94
  className: "h-full w-full object-cover"
95
95
  }) : /* @__PURE__ */ I("div", {
@@ -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?.url ? (\n <img src={course.cover.url} 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 </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":"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"}
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 </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":"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"}
@@ -0,0 +1,4 @@
1
+ import { FC } from 'react';
2
+
3
+ export declare const CertificatesPage: FC;
4
+ //# sourceMappingURL=CertificatesPage.d.ts.map
@@ -0,0 +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;AA8DnD,eAAO,MAAM,gBAAgB,EAAE,EA2X9B,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { FC } from 'react';
2
+
3
+ export declare const CohortsPage: FC;
4
+ //# sourceMappingURL=CohortsPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CohortsPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/cohorts/CohortsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAqB,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAsDnD,eAAO,MAAM,WAAW,EAAE,EAiWzB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { FC } from 'react';
2
+
3
+ export declare const LessonPlayerPage: FC;
4
+ //# sourceMappingURL=LessonPlayerPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LessonPlayerPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/learning/LessonPlayerPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AA2ChC,eAAO,MAAM,gBAAgB,EAAE,EAwX9B,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { FC } from 'react';
2
+
3
+ export declare const MyLearningPage: FC;
4
+ //# sourceMappingURL=MyLearningPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MyLearningPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/learning/MyLearningPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AAyBhC,eAAO,MAAM,cAAc,EAAE,EAoO5B,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { FC } from 'react';
2
+
3
+ export declare const SkillsPage: FC;
4
+ //# sourceMappingURL=SkillsPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SkillsPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/skills/SkillsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AA6FhC,eAAO,MAAM,UAAU,EAAE,EA8LxB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { FC } from 'react';
2
+
3
+ export declare const SubmissionsPage: FC;
4
+ //# sourceMappingURL=SubmissionsPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SubmissionsPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/submissions/SubmissionsPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,EAAE,EAAa,MAAM,OAAO,CAAC;AAmG3C,eAAO,MAAM,eAAe,EAAE,EAyX7B,CAAC"}