@hydralms/components 0.2.0 → 0.3.1

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 (302) hide show
  1. package/dist/StudentProfile-BPsZBaJj.cjs +1 -0
  2. package/dist/StudentProfile-Cw2p-RZn.js +3273 -0
  3. package/dist/assessment-toolbar/question-navigator.d.ts +1 -1
  4. package/dist/assessment-toolbar/timer-display.d.ts +1 -1
  5. package/dist/common/index.d.ts +2 -1
  6. package/dist/common/pagination.d.ts +26 -0
  7. package/dist/common/types.d.ts +1 -0
  8. package/dist/components.css +1 -1
  9. package/dist/content/audio-player.d.ts +22 -0
  10. package/dist/content/code-block.d.ts +30 -0
  11. package/dist/content/embed-block.d.ts +28 -0
  12. package/dist/content/index.d.ts +6 -0
  13. package/dist/content/types.d.ts +24 -0
  14. package/dist/curriculum/course-card.d.ts +51 -0
  15. package/dist/curriculum/index.d.ts +2 -0
  16. package/dist/curriculum/types.d.ts +2 -2
  17. package/dist/index.cjs +1 -1
  18. package/dist/index.d.ts +1 -0
  19. package/dist/index.js +495 -439
  20. package/dist/license/HydraContext.d.ts +16 -0
  21. package/dist/license/ProBadge.d.ts +6 -0
  22. package/dist/license/index.d.ts +7 -0
  23. package/dist/license/tiers.d.ts +6 -0
  24. package/dist/license/useHydraLicense.d.ts +6 -0
  25. package/dist/license/validate.d.ts +13 -0
  26. package/dist/license/withProGate.d.ts +6 -0
  27. package/dist/modules/AssignmentModule/AssignmentModule.d.ts +4 -7
  28. package/dist/modules/AssignmentModule/types.d.ts +5 -1
  29. package/dist/modules/CertificateModule/CertificateModule.d.ts +4 -8
  30. package/dist/modules/CertificateModule/types.d.ts +6 -4
  31. package/dist/modules/CourseCatalogModule/CourseCatalogModule.d.ts +5 -0
  32. package/dist/modules/CourseCatalogModule/types.d.ts +43 -0
  33. package/dist/modules/CoursePlayer/CoursePlayer.d.ts +4 -1
  34. package/dist/modules/DiscussionModule/DiscussionModule.d.ts +4 -7
  35. package/dist/modules/ExamModule/ExamModule.d.ts +4 -7
  36. package/dist/modules/ExamModule/types.d.ts +5 -14
  37. package/dist/modules/FlashcardLab/FlashcardLab.d.ts +4 -1
  38. package/dist/modules/FlashcardLab/types.d.ts +2 -0
  39. package/dist/modules/GradeCenterModule/GradeCenterModule.d.ts +4 -8
  40. package/dist/modules/GradeCenterModule/types.d.ts +2 -0
  41. package/dist/modules/QuizModule/QuizModule.d.ts +4 -1
  42. package/dist/modules/QuizModule/types.d.ts +5 -14
  43. package/dist/modules/StudentDashboardModule/StudentDashboardModule.d.ts +5 -0
  44. package/dist/modules/StudentDashboardModule/types.d.ts +54 -0
  45. package/dist/modules/StudentProfileModule/StudentProfileModule.d.ts +5 -0
  46. package/dist/modules/StudentProfileModule/types.d.ts +43 -0
  47. package/dist/modules/SurveyModule/SurveyModule.d.ts +4 -6
  48. package/dist/modules/SurveyModule/types.d.ts +2 -0
  49. package/dist/modules/_shared/assessment-intro.d.ts +16 -0
  50. package/dist/modules/_shared/assessment-results.d.ts +23 -0
  51. package/dist/modules/_shared/types.d.ts +10 -0
  52. package/dist/modules/_shared/use-timer.d.ts +9 -0
  53. package/dist/modules/index.d.ts +6 -0
  54. package/dist/modules.cjs +1 -1
  55. package/dist/modules.js +1267 -854
  56. package/dist/progress/types.d.ts +2 -0
  57. package/dist/provider/HydraProvider.d.ts +5 -1
  58. package/dist/questions/choice.d.ts +1 -1
  59. package/dist/questions/confidence-indicator.d.ts +37 -0
  60. package/dist/questions/essay.d.ts +1 -1
  61. package/dist/questions/fill-in-the-blank.d.ts +1 -1
  62. package/dist/questions/hotspot.d.ts +1 -1
  63. package/dist/questions/index.d.ts +2 -0
  64. package/dist/questions/inline-choice.d.ts +1 -1
  65. package/dist/questions/matching.d.ts +1 -1
  66. package/dist/questions/multiple-choice.d.ts +1 -1
  67. package/dist/questions/numeric.d.ts +1 -1
  68. package/dist/questions/ordering.d.ts +1 -1
  69. package/dist/questions/question-renderer.d.ts +1 -1
  70. package/dist/questions/scenario.d.ts +1 -1
  71. package/dist/questions/spreadsheet.d.ts +1 -1
  72. package/dist/questions/true-false.d.ts +1 -1
  73. package/dist/sections/AdaptiveLearningPath/AdaptiveLearningPath.d.ts +5 -0
  74. package/dist/sections/AdaptiveLearningPath/path-connector.d.ts +8 -0
  75. package/dist/sections/AdaptiveLearningPath/path-milestone-marker.d.ts +7 -0
  76. package/dist/sections/AdaptiveLearningPath/path-node-card.d.ts +10 -0
  77. package/dist/sections/AdaptiveLearningPath/path-skill-bar.d.ts +8 -0
  78. package/dist/sections/AdaptiveLearningPath/types.d.ts +136 -0
  79. package/dist/sections/AnnouncementFeed/AnnouncementFeed.d.ts +1 -1
  80. package/dist/sections/AnnouncementFeed/types.d.ts +15 -1
  81. package/dist/sections/AssessmentReview/AssessmentReview.d.ts +1 -1
  82. package/dist/sections/AssessmentReview/types.d.ts +6 -0
  83. package/dist/sections/AssignmentSubmission/AssignmentSubmission.d.ts +1 -1
  84. package/dist/sections/AssignmentSubmission/types.d.ts +6 -0
  85. package/dist/sections/CertificateViewer/CertificateViewer.d.ts +1 -1
  86. package/dist/sections/CertificateViewer/certificate-variants.d.ts +42 -0
  87. package/dist/sections/CertificateViewer/types.d.ts +6 -0
  88. package/dist/sections/ContentAuthoringStudio/ContentAuthoringStudio.d.ts +5 -0
  89. package/dist/sections/ContentAuthoringStudio/block-editor-item.d.ts +14 -0
  90. package/dist/sections/ContentAuthoringStudio/block-type-picker.d.ts +12 -0
  91. package/dist/sections/ContentAuthoringStudio/types.d.ts +67 -0
  92. package/dist/sections/CourseCatalog/CourseCatalog.d.ts +2 -0
  93. package/dist/sections/CourseCatalog/types.d.ts +80 -0
  94. package/dist/sections/CourseOutline/CourseOutline.d.ts +1 -1
  95. package/dist/sections/CourseOutline/types.d.ts +6 -0
  96. package/dist/sections/DiscussionThread/DiscussionThread.d.ts +1 -1
  97. package/dist/sections/DiscussionThread/types.d.ts +6 -0
  98. package/dist/sections/EnrollmentWizard/EnrollmentWizard.d.ts +2 -0
  99. package/dist/sections/EnrollmentWizard/types.d.ts +66 -0
  100. package/dist/sections/ExamSession/ExamSession.d.ts +1 -1
  101. package/dist/sections/ExamSession/types.d.ts +6 -0
  102. package/dist/sections/FlashcardStudySession/FlashcardStudySession.d.ts +1 -1
  103. package/dist/sections/FlashcardStudySession/types.d.ts +6 -0
  104. package/dist/sections/ForumBoard/ForumBoard.d.ts +1 -1
  105. package/dist/sections/ForumBoard/types.d.ts +14 -0
  106. package/dist/sections/GradebookTable/GradebookTable.d.ts +1 -1
  107. package/dist/sections/GradebookTable/types.d.ts +14 -0
  108. package/dist/sections/LecturePlayer/LecturePlayer.d.ts +1 -1
  109. package/dist/sections/LecturePlayer/types.d.ts +8 -0
  110. package/dist/sections/LessonPage/LessonPage.d.ts +1 -1
  111. package/dist/sections/LessonPage/types.d.ts +6 -0
  112. package/dist/sections/PracticeQuiz/PracticeQuiz.d.ts +1 -1
  113. package/dist/sections/PracticeQuiz/types.d.ts +6 -0
  114. package/dist/sections/ProgressDashboard/ProgressDashboard.d.ts +1 -1
  115. package/dist/sections/ProgressDashboard/types.d.ts +6 -0
  116. package/dist/sections/QuizSession/QuizSession.d.ts +1 -1
  117. package/dist/sections/QuizSession/types.d.ts +6 -0
  118. package/dist/sections/RequirementsChecklist/RequirementsChecklist.d.ts +1 -1
  119. package/dist/sections/RequirementsChecklist/types.d.ts +6 -0
  120. package/dist/sections/ResourceLibrary/ResourceLibrary.d.ts +1 -1
  121. package/dist/sections/ResourceLibrary/types.d.ts +15 -1
  122. package/dist/sections/RubricView/RubricView.d.ts +1 -1
  123. package/dist/sections/RubricView/types.d.ts +6 -0
  124. package/dist/sections/ScrollableQuiz/ScrollableQuiz.d.ts +1 -1
  125. package/dist/sections/ScrollableQuiz/types.d.ts +6 -0
  126. package/dist/sections/StudentProfile/StudentProfile.d.ts +2 -0
  127. package/dist/sections/StudentProfile/types.d.ts +98 -0
  128. package/dist/sections/SurveyForm/SurveyForm.d.ts +1 -1
  129. package/dist/sections/SurveyForm/types.d.ts +6 -0
  130. package/dist/sections/_shared/merge-answers.d.ts +9 -0
  131. package/dist/sections/_shared/section-shell.d.ts +20 -0
  132. package/dist/sections/_shared/use-assessment-session.d.ts +30 -0
  133. package/dist/sections/index.d.ts +10 -0
  134. package/dist/sections.cjs +1 -1
  135. package/dist/sections.js +1361 -307
  136. package/dist/ui/badge.d.ts +1 -1
  137. package/dist/ui/index.d.ts +2 -0
  138. package/dist/ui/progress.d.ts +1 -1
  139. package/dist/ui/rich-text-editor.d.ts +3 -1
  140. package/dist/ui/toast.d.ts +43 -0
  141. package/dist/utils/debounce.d.ts +5 -1
  142. package/dist/utils/pick-palette-color.d.ts +19 -0
  143. package/dist/video/types.d.ts +15 -0
  144. package/dist/video/video-player.d.ts +1 -1
  145. package/dist/withProGate-BJdu1T9Y.cjs +2 -0
  146. package/dist/withProGate-BvFc7Jwy.js +4975 -0
  147. package/package.json +57 -226
  148. package/src/assessment-toolbar/question-navigator.tsx +10 -5
  149. package/src/assessment-toolbar/timer-display.tsx +4 -3
  150. package/src/assessment-toolbar/use-countdown.ts +1 -1
  151. package/src/common/empty-state.tsx +1 -0
  152. package/src/common/index.ts +2 -0
  153. package/src/common/pagination.tsx +135 -0
  154. package/src/common/search-input.tsx +2 -1
  155. package/src/common/types.ts +2 -0
  156. package/src/content/attachment-list.tsx +2 -0
  157. package/src/content/audio-player.tsx +196 -0
  158. package/src/content/code-block.tsx +113 -0
  159. package/src/content/content-block.tsx +64 -0
  160. package/src/content/embed-block.tsx +78 -0
  161. package/src/content/file-upload-zone.tsx +10 -0
  162. package/src/content/index.ts +6 -0
  163. package/src/content/types.ts +5 -0
  164. package/src/curriculum/course-card.tsx +199 -0
  165. package/src/curriculum/curriculum-item.tsx +3 -3
  166. package/src/curriculum/curriculum-tree.tsx +20 -13
  167. package/src/curriculum/index.ts +2 -0
  168. package/src/curriculum/types.ts +2 -2
  169. package/src/flashcards/flashcard.tsx +28 -8
  170. package/src/index.ts +3 -0
  171. package/src/license/HydraContext.tsx +62 -0
  172. package/src/license/ProBadge.tsx +43 -0
  173. package/src/license/index.ts +7 -0
  174. package/src/license/tiers.ts +34 -0
  175. package/src/license/useHydraLicense.ts +10 -0
  176. package/src/license/validate.ts +90 -0
  177. package/src/license/withProGate.tsx +21 -0
  178. package/src/modules/AssignmentModule/AssignmentModule.tsx +17 -8
  179. package/src/modules/AssignmentModule/types.ts +5 -1
  180. package/src/modules/CertificateModule/CertificateModule.tsx +21 -9
  181. package/src/modules/CertificateModule/types.ts +6 -4
  182. package/src/modules/CourseCatalogModule/CourseCatalogModule.tsx +126 -0
  183. package/src/modules/CourseCatalogModule/types.ts +47 -0
  184. package/src/modules/CoursePlayer/CoursePlayer.tsx +39 -22
  185. package/src/modules/DiscussionModule/DiscussionModule.tsx +57 -22
  186. package/src/modules/ExamModule/ExamModule.tsx +64 -198
  187. package/src/modules/ExamModule/types.ts +5 -14
  188. package/src/modules/FlashcardLab/FlashcardLab.tsx +10 -5
  189. package/src/modules/FlashcardLab/types.ts +2 -0
  190. package/src/modules/GradeCenterModule/GradeCenterModule.tsx +7 -2
  191. package/src/modules/GradeCenterModule/types.ts +2 -0
  192. package/src/modules/QuizModule/QuizModule.tsx +49 -169
  193. package/src/modules/QuizModule/types.ts +5 -15
  194. package/src/modules/StudentDashboardModule/StudentDashboardModule.tsx +117 -0
  195. package/src/modules/StudentDashboardModule/types.ts +56 -0
  196. package/src/modules/StudentProfileModule/StudentProfileModule.tsx +289 -0
  197. package/src/modules/StudentProfileModule/types.ts +45 -0
  198. package/src/modules/SurveyModule/SurveyModule.tsx +9 -4
  199. package/src/modules/SurveyModule/types.ts +2 -0
  200. package/src/modules/_shared/assessment-intro.tsx +75 -0
  201. package/src/modules/_shared/assessment-results.tsx +133 -0
  202. package/src/modules/_shared/types.ts +11 -0
  203. package/src/modules/_shared/use-timer.ts +49 -0
  204. package/src/modules/index.ts +9 -0
  205. package/src/progress/achievement-badge.tsx +3 -3
  206. package/src/progress/grade-indicator.tsx +9 -1
  207. package/src/progress/progress-ring.tsx +2 -1
  208. package/src/progress/stat-card.tsx +14 -2
  209. package/src/progress/types.ts +2 -0
  210. package/src/provider/HydraProvider.tsx +15 -6
  211. package/src/questions/choice.tsx +13 -6
  212. package/src/questions/confidence-indicator.tsx +107 -0
  213. package/src/questions/essay.tsx +6 -4
  214. package/src/questions/fill-in-the-blank.tsx +8 -4
  215. package/src/questions/hotspot.tsx +4 -4
  216. package/src/questions/index.ts +2 -0
  217. package/src/questions/inline-choice.tsx +5 -4
  218. package/src/questions/matching.tsx +5 -4
  219. package/src/questions/multiple-choice.tsx +13 -6
  220. package/src/questions/numeric.tsx +8 -4
  221. package/src/questions/ordering.tsx +12 -4
  222. package/src/questions/question-renderer.tsx +3 -2
  223. package/src/questions/scenario.tsx +4 -4
  224. package/src/questions/spreadsheet.tsx +5 -4
  225. package/src/questions/true-false.tsx +13 -6
  226. package/src/sections/AdaptiveLearningPath/AdaptiveLearningPath.tsx +251 -0
  227. package/src/sections/AdaptiveLearningPath/path-connector.tsx +27 -0
  228. package/src/sections/AdaptiveLearningPath/path-milestone-marker.tsx +50 -0
  229. package/src/sections/AdaptiveLearningPath/path-node-card.tsx +166 -0
  230. package/src/sections/AdaptiveLearningPath/path-skill-bar.tsx +49 -0
  231. package/src/sections/AdaptiveLearningPath/types.ts +159 -0
  232. package/src/sections/AnnouncementFeed/AnnouncementFeed.tsx +64 -8
  233. package/src/sections/AnnouncementFeed/types.ts +15 -1
  234. package/src/sections/AssessmentReview/AssessmentReview.tsx +37 -0
  235. package/src/sections/AssessmentReview/types.ts +6 -0
  236. package/src/sections/AssignmentSubmission/AssignmentSubmission.tsx +37 -1
  237. package/src/sections/AssignmentSubmission/types.ts +6 -0
  238. package/src/sections/CertificateViewer/CertificateViewer.tsx +29 -227
  239. package/src/sections/CertificateViewer/certificate-variants.tsx +170 -0
  240. package/src/sections/CertificateViewer/types.ts +6 -0
  241. package/src/sections/ContentAuthoringStudio/ContentAuthoringStudio.tsx +289 -0
  242. package/src/sections/ContentAuthoringStudio/block-editor-item.tsx +487 -0
  243. package/src/sections/ContentAuthoringStudio/block-type-picker.tsx +123 -0
  244. package/src/sections/ContentAuthoringStudio/types.ts +67 -0
  245. package/src/sections/CourseCatalog/CourseCatalog.tsx +220 -0
  246. package/src/sections/CourseCatalog/types.ts +76 -0
  247. package/src/sections/CourseOutline/CourseOutline.tsx +41 -0
  248. package/src/sections/CourseOutline/types.ts +6 -0
  249. package/src/sections/DiscussionThread/DiscussionThread.tsx +42 -1
  250. package/src/sections/DiscussionThread/types.ts +6 -0
  251. package/src/sections/EnrollmentWizard/EnrollmentWizard.tsx +343 -0
  252. package/src/sections/EnrollmentWizard/types.ts +65 -0
  253. package/src/sections/ExamSession/ExamSession.tsx +100 -94
  254. package/src/sections/ExamSession/types.ts +6 -0
  255. package/src/sections/FlashcardStudySession/FlashcardStudySession.tsx +53 -36
  256. package/src/sections/FlashcardStudySession/types.ts +6 -0
  257. package/src/sections/ForumBoard/ForumBoard.tsx +67 -7
  258. package/src/sections/ForumBoard/types.ts +14 -0
  259. package/src/sections/GradebookTable/GradebookTable.tsx +54 -1
  260. package/src/sections/GradebookTable/types.ts +14 -0
  261. package/src/sections/LecturePlayer/LecturePlayer.tsx +63 -37
  262. package/src/sections/LecturePlayer/types.ts +8 -0
  263. package/src/sections/LessonPage/LessonPage.tsx +34 -6
  264. package/src/sections/LessonPage/types.ts +6 -0
  265. package/src/sections/PracticeQuiz/PracticeQuiz.tsx +106 -74
  266. package/src/sections/PracticeQuiz/types.ts +6 -0
  267. package/src/sections/ProgressDashboard/ProgressDashboard.tsx +64 -10
  268. package/src/sections/ProgressDashboard/types.ts +6 -0
  269. package/src/sections/QuizSession/QuizSession.tsx +71 -82
  270. package/src/sections/QuizSession/types.ts +6 -0
  271. package/src/sections/RequirementsChecklist/RequirementsChecklist.tsx +41 -1
  272. package/src/sections/RequirementsChecklist/types.ts +6 -0
  273. package/src/sections/ResourceLibrary/ResourceLibrary.tsx +64 -8
  274. package/src/sections/ResourceLibrary/types.ts +15 -1
  275. package/src/sections/RubricView/RubricView.tsx +37 -1
  276. package/src/sections/RubricView/types.ts +6 -0
  277. package/src/sections/ScrollableQuiz/ScrollableQuiz.tsx +36 -15
  278. package/src/sections/ScrollableQuiz/types.ts +6 -0
  279. package/src/sections/StudentProfile/StudentProfile.tsx +279 -0
  280. package/src/sections/StudentProfile/types.ts +99 -0
  281. package/src/sections/SurveyForm/SurveyForm.tsx +32 -5
  282. package/src/sections/SurveyForm/types.ts +6 -0
  283. package/src/sections/_shared/merge-answers.ts +22 -0
  284. package/src/sections/_shared/section-shell.tsx +64 -0
  285. package/src/sections/_shared/use-assessment-session.ts +125 -0
  286. package/src/sections/index.ts +40 -0
  287. package/src/social/user-avatar.tsx +9 -5
  288. package/src/styles/globals.css +39 -41
  289. package/src/ui/badge.tsx +8 -0
  290. package/src/ui/index.ts +2 -0
  291. package/src/ui/progress.tsx +4 -0
  292. package/src/ui/rich-text-editor.tsx +10 -0
  293. package/src/ui/rich-text-toolbar.tsx +2 -1
  294. package/src/ui/toast.tsx +170 -0
  295. package/src/utils/debounce.ts +8 -2
  296. package/src/utils/pick-palette-color.ts +33 -0
  297. package/src/video/types.ts +16 -0
  298. package/src/video/video-player.tsx +27 -6
  299. package/dist/ForumBoard-CHXU3mjC.js +0 -2207
  300. package/dist/ForumBoard-d1w5-r6n.cjs +0 -1
  301. package/dist/tabs-DRM2Iq_J.cjs +0 -172
  302. package/dist/tabs-Wf3h_Cx3.js +0 -21580
@@ -0,0 +1,159 @@
1
+ import type { ReactNode } from "react";
2
+
3
+ /**
4
+ * Status of a single node in the learning path.
5
+ * - `locked` — prerequisites not met, node is inaccessible
6
+ * - `available` — prerequisites met, learner can start
7
+ * - `in_progress` — learner is currently working on this node
8
+ * - `completed` — learner finished but below mastery threshold
9
+ * - `mastered` — learner finished and reached the mastery threshold
10
+ * - `skipped` — node was bypassed by the adaptive engine
11
+ */
12
+ export type PathNodeStatus =
13
+ | "locked"
14
+ | "available"
15
+ | "in_progress"
16
+ | "completed"
17
+ | "mastered"
18
+ | "skipped";
19
+
20
+ /**
21
+ * The type of learning activity a path node represents.
22
+ * Aligns with LearningObjectIcon type strings.
23
+ */
24
+ export type PathNodeType =
25
+ | "lesson"
26
+ | "quiz"
27
+ | "assignment"
28
+ | "video"
29
+ | "discussion"
30
+ | "document"
31
+ | "assessment"
32
+ | "audio"
33
+ | "link";
34
+
35
+ /** A skill or competency area that the learning path develops. */
36
+ export interface PathSkill {
37
+ uid: string;
38
+ name: string;
39
+ /** Current proficiency level 0–100 */
40
+ proficiency: number;
41
+ /** Target proficiency level 0–100 */
42
+ targetProficiency?: number;
43
+ }
44
+
45
+ /** A milestone marker along the learning path. */
46
+ export interface PathMilestone {
47
+ uid: string;
48
+ title: string;
49
+ description?: string;
50
+ /** Whether the milestone has been reached */
51
+ reached: boolean;
52
+ /** Index of the node after which this milestone appears (zero-based) */
53
+ afterNodeIndex: number;
54
+ variant?: "default" | "gold" | "silver" | "bronze";
55
+ }
56
+
57
+ /** A single node in the adaptive learning path. */
58
+ export interface PathNode {
59
+ uid: string;
60
+ title: string;
61
+ description?: string;
62
+ /** Learning activity type — used for icon selection via LearningObjectIcon */
63
+ type: PathNodeType;
64
+ status: PathNodeStatus;
65
+ /** Estimated duration in seconds */
66
+ estimatedDuration?: number;
67
+ /** Performance score if completed (0–100) */
68
+ score?: number;
69
+ /** Whether this node is recommended as the next step */
70
+ recommended?: boolean;
71
+ /** Skill UIDs that this node develops */
72
+ skillUids?: string[];
73
+ /** UIDs of prerequisite nodes */
74
+ prerequisites?: string[];
75
+ /** Optional custom icon (overrides type-based icon) */
76
+ icon?: ReactNode;
77
+ }
78
+
79
+ /** A branch point where the adaptive engine may redirect the learner. */
80
+ export interface PathBranch {
81
+ uid: string;
82
+ /** UID of the node where the branch originates */
83
+ fromNodeUid: string;
84
+ /** Label for this branch (e.g., "Review Fundamentals") */
85
+ label: string;
86
+ /** UIDs of nodes in this branch path */
87
+ nodeUids: string[];
88
+ /** Whether this branch is the active/chosen branch */
89
+ active?: boolean;
90
+ /** Condition description (e.g., "Score below 70%") */
91
+ condition?: string;
92
+ }
93
+
94
+ /** Overall progress summary for the path. */
95
+ export interface PathProgress {
96
+ /** Percentage of path completed (0–100) */
97
+ completionPercentage: number;
98
+ completedNodes: number;
99
+ totalNodes: number;
100
+ /** Total time spent in seconds */
101
+ totalTimeSpent: number;
102
+ /** Average score across completed nodes (0–100) */
103
+ averageScore?: number;
104
+ /** Current streak in days */
105
+ currentStreak?: number;
106
+ }
107
+
108
+ /**
109
+ * AdaptiveLearningPath section — a premium visualization of a personalized
110
+ * learning sequence that adapts based on learner performance.
111
+ *
112
+ * Shows a visual path map with nodes, connectors, milestones, skill bars,
113
+ * and summary stats. Each node represents a learning activity with status,
114
+ * score, and recommended-next indicators.
115
+ *
116
+ * @example
117
+ * <AdaptiveLearningPath
118
+ * title="React Mastery Path"
119
+ * description="A personalized journey for mastering React"
120
+ * nodes={pathNodes}
121
+ * progress={pathProgress}
122
+ * onNodeClick={(uid) => navigate(`/activity/${uid}`)}
123
+ * onNodeStart={(uid) => startActivity(uid)}
124
+ * />
125
+ */
126
+ export interface AdaptiveLearningPathProps {
127
+ /** Path title */
128
+ title: string;
129
+ /** Optional path description */
130
+ description?: string;
131
+ /** Difficulty level displayed in the header */
132
+ difficulty?: "beginner" | "intermediate" | "advanced";
133
+ /** Ordered list of learning path nodes */
134
+ nodes: PathNode[];
135
+ /** Overall path progress data */
136
+ progress: PathProgress;
137
+ /** Skill/competency areas covered by this path */
138
+ skills?: PathSkill[];
139
+ /** Milestones along the path */
140
+ milestones?: PathMilestone[];
141
+ /** Branch points for adaptive routing */
142
+ branches?: PathBranch[];
143
+ /** Called when the user clicks a node */
144
+ onNodeClick?: (nodeUid: string) => void;
145
+ /** Called when the user starts a recommended or available activity */
146
+ onNodeStart?: (nodeUid: string) => void;
147
+ /** Called when the user clicks a skill */
148
+ onSkillClick?: (skillUid: string) => void;
149
+ /** When true, disables all interactive elements. @default false */
150
+ readOnly?: boolean;
151
+ /** Render skeleton placeholders instead of content */
152
+ isLoading?: boolean;
153
+ /** Error message — renders an error state with optional retry */
154
+ error?: string | null;
155
+ /** Called when the user clicks retry in the error state */
156
+ onRetry?: () => void;
157
+ className?: string;
158
+ style?: React.CSSProperties;
159
+ }
@@ -1,13 +1,15 @@
1
1
  import { useMemo, useState } from "react";
2
- import { Pin } from "lucide-react";
2
+ import { AlertCircle, Pin } from "lucide-react";
3
3
  import { UserAvatar } from "../../social";
4
4
  import { EmptyState } from "../../common";
5
+ import { Skeleton } from "../../ui/skeleton";
5
6
  import { Badge } from "../../ui/badge";
6
7
  import { Button } from "../../ui/button";
7
8
  import { Card, CardContent } from "../../ui/card";
8
9
  import type { AnnouncementFeedProps } from "./types";
9
10
  import { cn } from "../../lib/utils";
10
11
  import { formatTimestamp } from "../../utils/format-timestamp";
12
+ import { Pagination } from "../../common/pagination";
11
13
 
12
14
  export function AnnouncementFeed({
13
15
  announcements,
@@ -17,6 +19,13 @@ export function AnnouncementFeed({
17
19
  previewLines = 3,
18
20
  emptyMessage = "No announcements yet",
19
21
  readOnly = false,
22
+ isLoading,
23
+ error,
24
+ onRetry,
25
+ pageSize,
26
+ currentPage = 1,
27
+ totalItems,
28
+ onPageChange,
20
29
  className,
21
30
  style,
22
31
  }: AnnouncementFeedProps) {
@@ -41,6 +50,42 @@ export function AnnouncementFeed({
41
50
  }
42
51
  }
43
52
 
53
+ if (isLoading) {
54
+ return (
55
+ <div className={cn("space-y-4", className)} style={style}>
56
+ {Array.from({ length: 3 }).map((_, i) => (
57
+ <div key={i} className="flex gap-3 items-start">
58
+ <Skeleton className="h-10 w-10 rounded-full shrink-0" />
59
+ <div className="flex-1 space-y-2">
60
+ <Skeleton className="h-5 w-48" />
61
+ <Skeleton className="h-4 w-full" />
62
+ <Skeleton className="h-4 w-full" />
63
+ </div>
64
+ </div>
65
+ ))}
66
+ </div>
67
+ );
68
+ }
69
+
70
+ if (error) {
71
+ return (
72
+ <div className={cn("py-12", className)} style={style}>
73
+ <EmptyState
74
+ icon={<AlertCircle className="size-10 text-destructive" />}
75
+ title="Something went wrong"
76
+ description={error}
77
+ action={
78
+ onRetry ? (
79
+ <Button variant="outline" onClick={onRetry}>
80
+ Retry
81
+ </Button>
82
+ ) : undefined
83
+ }
84
+ />
85
+ </div>
86
+ );
87
+ }
88
+
44
89
  if (sorted.length === 0) {
45
90
  return (
46
91
  <div className={className} style={style}>
@@ -51,7 +96,10 @@ export function AnnouncementFeed({
51
96
 
52
97
  return (
53
98
  <div className={cn("flex flex-col gap-2", className)} style={style}>
54
- {sorted.map((a) => {
99
+ {(onPageChange && pageSize
100
+ ? sorted.slice((currentPage - 1) * pageSize, currentPage * pageSize)
101
+ : sorted
102
+ ).map((a) => {
55
103
  const isExpanded = expandedUids.has(a.uid);
56
104
 
57
105
  return (
@@ -91,9 +139,9 @@ export function AnnouncementFeed({
91
139
  <span className="block text-xs text-muted-foreground mb-1">
92
140
  {a.author.displayName} · {formatTimestamp(a.createdAt)}
93
141
  </span>
94
- <span
142
+ <div
95
143
  className={cn(
96
- "text-sm text-foreground",
144
+ "text-sm text-foreground [&_p]:mb-2 [&_ul]:list-disc [&_ul]:pl-5 [&_ol]:list-decimal [&_ol]:pl-5",
97
145
  !isExpanded && "line-clamp-(--preview-lines) overflow-hidden",
98
146
  )}
99
147
  style={
@@ -101,10 +149,9 @@ export function AnnouncementFeed({
101
149
  ? { "--preview-lines": previewLines } as React.CSSProperties
102
150
  : undefined
103
151
  }
104
- >
105
- {a.content}
106
- </span>
107
- {!isExpanded && a.content.length > 200 && (
152
+ dangerouslySetInnerHTML={{ __html: a.content }}
153
+ />
154
+ {!isExpanded && a.content.replace(/<[^>]*>/g, "").length > 200 && (
108
155
  <Button
109
156
  variant="link"
110
157
  size="xs"
@@ -123,6 +170,15 @@ export function AnnouncementFeed({
123
170
  </Card>
124
171
  );
125
172
  })}
173
+
174
+ {onPageChange && pageSize && sorted.length > 0 && (
175
+ <Pagination
176
+ currentPage={currentPage}
177
+ totalPages={Math.ceil((totalItems ?? sorted.length) / pageSize)}
178
+ onPageChange={onPageChange}
179
+ className="mt-4"
180
+ />
181
+ )}
126
182
  </div>
127
183
  );
128
184
  }
@@ -26,6 +26,20 @@ export interface AnnouncementFeedProps {
26
26
  emptyMessage?: string;
27
27
  /** When true, disables interactions */
28
28
  readOnly?: boolean;
29
+ /** Render skeleton placeholders instead of content */
30
+ isLoading?: boolean;
31
+ /** Error message — renders an error state with optional retry */
32
+ error?: string | null;
33
+ /** Called when the user clicks retry in the error state */
34
+ onRetry?: () => void;
35
+ /** Number of items per page (enables pagination when set with onPageChange) */
36
+ pageSize?: number;
37
+ /** Current page (1-indexed) */
38
+ currentPage?: number;
39
+ /** Total number of items (for server-side pagination) */
40
+ totalItems?: number;
41
+ /** Called when the user navigates to a different page */
42
+ onPageChange?: (page: number) => void;
29
43
  /** CSS class name for the root element */
30
44
  className?: string;
31
45
  /** Inline styles for the root element */
@@ -37,7 +51,7 @@ export interface Announcement {
37
51
  uid: string;
38
52
  /** Announcement title */
39
53
  title: string;
40
- /** Announcement body content */
54
+ /** Announcement body content. May contain HTML from a rich-text editor. */
41
55
  content: string;
42
56
  /** Author information */
43
57
  author: { displayName: string; avatarUrl?: string; role?: string };
@@ -1,9 +1,13 @@
1
1
  import { useMemo } from "react";
2
+ import { AlertCircle } from "lucide-react";
2
3
  import { QuestionRenderer } from "../../questions";
3
4
  import type { QuestionData, SessionAnswer } from "../../questions/types";
4
5
  import { Badge } from "../../ui/badge";
5
6
  import { Card, CardContent } from "../../ui/card";
6
7
  import { Separator } from "../../ui/separator";
8
+ import { Skeleton } from "../../ui/skeleton";
9
+ import { Button } from "../../ui/button";
10
+ import { EmptyState } from "../../common/empty-state";
7
11
  import { cn } from "../../lib/utils";
8
12
  import type {
9
13
  AssessmentReviewProps,
@@ -140,9 +144,42 @@ export function AssessmentReview({
140
144
  score,
141
145
  questionGroups,
142
146
  showCorrectAnswers = true,
147
+ isLoading,
148
+ error,
149
+ onRetry,
143
150
  className,
144
151
  style,
145
152
  }: AssessmentReviewProps) {
153
+ if (isLoading) {
154
+ return (
155
+ <div className={cn("space-y-4", className)} style={style}>
156
+ <Skeleton className="h-16 w-full" />
157
+ <Skeleton className="h-24 w-full" />
158
+ <Skeleton className="h-24 w-full" />
159
+ <Skeleton className="h-24 w-full" />
160
+ </div>
161
+ );
162
+ }
163
+
164
+ if (error) {
165
+ return (
166
+ <div className={cn("py-12", className)} style={style}>
167
+ <EmptyState
168
+ icon={<AlertCircle className="size-10 text-destructive" />}
169
+ title="Something went wrong"
170
+ description={error}
171
+ action={
172
+ onRetry ? (
173
+ <Button variant="outline" onClick={onRetry}>
174
+ Retry
175
+ </Button>
176
+ ) : undefined
177
+ }
178
+ />
179
+ </div>
180
+ );
181
+ }
182
+
146
183
  return (
147
184
  <div className={cn(className)} style={style}>
148
185
  {score && <ScoreHeader score={score} />}
@@ -34,6 +34,12 @@ export interface AssessmentReviewProps {
34
34
  * @default true
35
35
  */
36
36
  showCorrectAnswers?: boolean;
37
+ /** Render skeleton placeholders instead of content */
38
+ isLoading?: boolean;
39
+ /** Error message — renders an error state with optional retry */
40
+ error?: string | null;
41
+ /** Called when the user clicks retry in the error state */
42
+ onRetry?: () => void;
37
43
  /** CSS class name for the root element */
38
44
  className?: string;
39
45
  /** Inline styles for the root element */
@@ -1,5 +1,5 @@
1
1
  import { useState } from "react";
2
- import { Send, Save } from "lucide-react";
2
+ import { Send, Save, AlertCircle } from "lucide-react";
3
3
  import { StatusBadge, DueDateDisplay } from "../../common";
4
4
  import { FileUploadZone } from "../../content";
5
5
  import { Button } from "../../ui/button";
@@ -9,6 +9,9 @@ import { Separator } from "../../ui/separator";
9
9
  import { Card, CardContent } from "../../ui/card";
10
10
  import { Alert, AlertDescription } from "../../ui/alert";
11
11
  import { Tabs, TabsList, TabsTrigger, TabsContent } from "../../ui/tabs";
12
+ import { Skeleton } from "../../ui/skeleton";
13
+ import { EmptyState } from "../../common/empty-state";
14
+ import { cn } from "../../lib/utils";
12
15
  import type { AssignmentSubmissionProps, SubmissionData } from "./types";
13
16
 
14
17
  export function AssignmentSubmission({
@@ -25,6 +28,9 @@ export function AssignmentSubmission({
25
28
  grade,
26
29
  isSubmitting = false,
27
30
  readOnly = false,
31
+ isLoading,
32
+ error,
33
+ onRetry,
28
34
  className,
29
35
  style,
30
36
  }: AssignmentSubmissionProps) {
@@ -33,6 +39,36 @@ export function AssignmentSubmission({
33
39
  const [url, setUrl] = useState(existingSubmission?.url ?? "");
34
40
  const [activeTab, setActiveTab] = useState<"text" | "file" | "url">(submissionTypes[0]);
35
41
 
42
+ if (isLoading) {
43
+ return (
44
+ <div className={cn("space-y-4", className)} style={style}>
45
+ <Skeleton className="h-8 w-64" />
46
+ <Skeleton className="h-6 w-20" />
47
+ <Skeleton className="h-32 w-full" />
48
+ <Skeleton className="h-10 w-32" />
49
+ </div>
50
+ );
51
+ }
52
+
53
+ if (error) {
54
+ return (
55
+ <div className={cn("py-12", className)} style={style}>
56
+ <EmptyState
57
+ icon={<AlertCircle className="size-10 text-destructive" />}
58
+ title="Something went wrong"
59
+ description={error}
60
+ action={
61
+ onRetry ? (
62
+ <Button variant="outline" onClick={onRetry}>
63
+ Retry
64
+ </Button>
65
+ ) : undefined
66
+ }
67
+ />
68
+ </div>
69
+ );
70
+ }
71
+
36
72
  const isEditable = !readOnly && !["submitted", "graded"].includes(status);
37
73
 
38
74
  function getSubmissionData(): SubmissionData {
@@ -44,6 +44,12 @@ export interface AssignmentSubmissionProps {
44
44
  isSubmitting?: boolean;
45
45
  /** When true, disables all interactions */
46
46
  readOnly?: boolean;
47
+ /** Render skeleton placeholders instead of content */
48
+ isLoading?: boolean;
49
+ /** Error message — renders an error state with optional retry */
50
+ error?: string | null;
51
+ /** Called when the user clicks retry in the error state */
52
+ onRetry?: () => void;
47
53
  /** CSS class name for the root element */
48
54
  className?: string;
49
55
  /** Inline styles for the root element */