@hydralms/components 0.1.3 → 0.2.0

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 (199) hide show
  1. package/dist/ForumBoard-CHXU3mjC.js +2207 -0
  2. package/dist/ForumBoard-d1w5-r6n.cjs +1 -0
  3. package/dist/assessment-toolbar/assessment-toolbar.d.ts +1 -1
  4. package/dist/assessment-toolbar/index.d.ts +5 -1
  5. package/dist/assessment-toolbar/question-header-bar.d.ts +2 -0
  6. package/dist/assessment-toolbar/question-materials-drawer.d.ts +2 -0
  7. package/dist/assessment-toolbar/question-navigator.d.ts +1 -1
  8. package/dist/assessment-toolbar/types.d.ts +52 -4
  9. package/dist/assessment-toolbar/use-countdown.d.ts +43 -0
  10. package/dist/common/index.d.ts +2 -1
  11. package/dist/common/stepper.d.ts +6 -0
  12. package/dist/common/types.d.ts +37 -0
  13. package/dist/components.css +1 -1
  14. package/dist/content/attachment-list.d.ts +6 -0
  15. package/dist/content/content-block.d.ts +1 -1
  16. package/dist/content/index.d.ts +2 -1
  17. package/dist/content/types.d.ts +39 -0
  18. package/dist/curriculum/curriculum-item.d.ts +1 -1
  19. package/dist/index.cjs +1 -1
  20. package/dist/index.js +551 -312
  21. package/dist/modules/AssignmentModule/AssignmentModule.d.ts +8 -0
  22. package/dist/modules/AssignmentModule/types.d.ts +65 -0
  23. package/dist/modules/CertificateModule/CertificateModule.d.ts +9 -0
  24. package/dist/modules/CertificateModule/types.d.ts +49 -0
  25. package/dist/modules/DiscussionModule/DiscussionModule.d.ts +8 -0
  26. package/dist/modules/DiscussionModule/types.d.ts +47 -0
  27. package/dist/modules/ExamModule/ExamModule.d.ts +8 -0
  28. package/dist/modules/ExamModule/types.d.ts +64 -0
  29. package/dist/modules/GradeCenterModule/GradeCenterModule.d.ts +9 -0
  30. package/dist/modules/GradeCenterModule/types.d.ts +54 -0
  31. package/dist/modules/QuizModule/QuizModule.d.ts +1 -1
  32. package/dist/modules/QuizModule/types.d.ts +6 -1
  33. package/dist/modules/SurveyModule/SurveyModule.d.ts +7 -0
  34. package/dist/modules/SurveyModule/types.d.ts +49 -0
  35. package/dist/modules/index.d.ts +12 -0
  36. package/dist/modules.cjs +1 -0
  37. package/dist/modules.js +1422 -0
  38. package/dist/progress/achievement-badge.d.ts +6 -0
  39. package/dist/progress/activity-timeline.d.ts +6 -0
  40. package/dist/progress/index.d.ts +4 -1
  41. package/dist/progress/stat-card.d.ts +1 -1
  42. package/dist/progress/streak-badge.d.ts +6 -0
  43. package/dist/progress/types.d.ts +97 -0
  44. package/dist/questions/essay.d.ts +1 -1
  45. package/dist/questions/hotspot.d.ts +21 -0
  46. package/dist/questions/index.d.ts +9 -1
  47. package/dist/questions/inline-choice.d.ts +21 -0
  48. package/dist/questions/matching.d.ts +22 -0
  49. package/dist/questions/numeric.d.ts +11 -0
  50. package/dist/questions/ordering.d.ts +12 -0
  51. package/dist/questions/scenario.d.ts +23 -0
  52. package/dist/questions/scoring.d.ts +22 -0
  53. package/dist/questions/spreadsheet.d.ts +29 -0
  54. package/dist/questions/types.d.ts +106 -1
  55. package/dist/questions/use-drag-reorder.d.ts +17 -0
  56. package/dist/sections/CertificateViewer/types.d.ts +7 -5
  57. package/dist/sections/ExamSession/ExamSession.d.ts +1 -1
  58. package/dist/sections/ExamSession/types.d.ts +6 -1
  59. package/dist/sections/ForumBoard/ForumBoard.d.ts +8 -0
  60. package/dist/sections/ForumBoard/types.d.ts +64 -0
  61. package/dist/sections/QuizSession/QuizSession.d.ts +1 -1
  62. package/dist/sections/QuizSession/types.d.ts +6 -1
  63. package/dist/sections/RequirementsChecklist/RequirementsChecklist.d.ts +8 -0
  64. package/dist/sections/RequirementsChecklist/types.d.ts +37 -0
  65. package/dist/sections/RubricView/RubricView.d.ts +9 -0
  66. package/dist/sections/RubricView/types.d.ts +50 -0
  67. package/dist/sections/index.d.ts +7 -1
  68. package/dist/sections.cjs +1 -1
  69. package/dist/sections.js +250 -1715
  70. package/dist/social/post-card.d.ts +1 -1
  71. package/dist/tabs-DRM2Iq_J.cjs +172 -0
  72. package/dist/tabs-Wf3h_Cx3.js +21580 -0
  73. package/dist/ui/alert.d.ts +1 -1
  74. package/dist/ui/badge.d.ts +1 -1
  75. package/dist/ui/button.d.ts +1 -1
  76. package/dist/ui/drawer.d.ts +84 -0
  77. package/dist/ui/index.d.ts +3 -0
  78. package/dist/ui/progress.d.ts +1 -1
  79. package/dist/ui/rich-text-editor.d.ts +30 -0
  80. package/dist/ui/rich-text-toolbar.d.ts +8 -0
  81. package/dist/utils/array-utils.d.ts +4 -0
  82. package/dist/utils/flatten-leaves.d.ts +6 -0
  83. package/dist/utils/format-file-size.d.ts +1 -0
  84. package/dist/utils/format-timestamp.d.ts +1 -0
  85. package/dist/utils/is-empty-html.d.ts +5 -0
  86. package/dist/utils/shuffle.d.ts +1 -0
  87. package/dist/utils/string-utils.d.ts +12 -0
  88. package/dist/video/video-bookmark.d.ts +1 -1
  89. package/dist/video/video-playlist-item.d.ts +1 -1
  90. package/package.json +92 -3
  91. package/src/assessment-toolbar/assessment-toolbar.tsx +54 -49
  92. package/src/assessment-toolbar/index.ts +6 -0
  93. package/src/assessment-toolbar/question-header-bar.tsx +61 -0
  94. package/src/assessment-toolbar/question-materials-drawer.tsx +55 -0
  95. package/src/assessment-toolbar/question-navigator.tsx +3 -31
  96. package/src/assessment-toolbar/timer-display.tsx +2 -2
  97. package/src/assessment-toolbar/types.ts +54 -4
  98. package/src/assessment-toolbar/use-countdown.ts +153 -0
  99. package/src/common/index.ts +3 -0
  100. package/src/common/search-input.tsx +7 -6
  101. package/src/common/stepper.tsx +100 -0
  102. package/src/common/types.ts +39 -0
  103. package/src/content/attachment-list.tsx +90 -0
  104. package/src/content/content-block.tsx +4 -2
  105. package/src/content/file-upload-zone.tsx +1 -6
  106. package/src/content/index.ts +3 -0
  107. package/src/content/types.ts +41 -0
  108. package/src/curriculum/curriculum-item.tsx +7 -3
  109. package/src/feedback/feedback-banner.tsx +12 -14
  110. package/src/flashcards/flashcard-deck.tsx +1 -9
  111. package/src/flashcards/flashcard.tsx +1 -1
  112. package/src/modules/AssignmentModule/AssignmentModule.tsx +305 -0
  113. package/src/modules/AssignmentModule/types.ts +73 -0
  114. package/src/modules/CertificateModule/CertificateModule.tsx +161 -0
  115. package/src/modules/CertificateModule/types.ts +47 -0
  116. package/src/modules/CoursePlayer/CoursePlayer.tsx +44 -48
  117. package/src/modules/DiscussionModule/DiscussionModule.tsx +110 -0
  118. package/src/modules/DiscussionModule/types.ts +54 -0
  119. package/src/modules/ExamModule/ExamModule.tsx +285 -0
  120. package/src/modules/ExamModule/types.ts +66 -0
  121. package/src/modules/FlashcardLab/FlashcardLab.tsx +29 -16
  122. package/src/modules/GradeCenterModule/GradeCenterModule.tsx +169 -0
  123. package/src/modules/GradeCenterModule/types.ts +63 -0
  124. package/src/modules/QuizModule/QuizModule.tsx +88 -88
  125. package/src/modules/QuizModule/types.ts +6 -1
  126. package/src/modules/SurveyModule/SurveyModule.tsx +180 -0
  127. package/src/modules/SurveyModule/types.ts +51 -0
  128. package/src/modules/index.ts +24 -0
  129. package/src/progress/achievement-badge.tsx +52 -0
  130. package/src/progress/activity-timeline.tsx +84 -0
  131. package/src/progress/index.ts +7 -0
  132. package/src/progress/stat-card.tsx +30 -18
  133. package/src/progress/streak-badge.tsx +35 -0
  134. package/src/progress/types.ts +101 -0
  135. package/src/questions/choice.tsx +7 -9
  136. package/src/questions/essay.tsx +23 -25
  137. package/src/questions/fill-in-the-blank.tsx +13 -16
  138. package/src/questions/hotspot.tsx +154 -0
  139. package/src/questions/index.ts +16 -0
  140. package/src/questions/inline-choice.tsx +151 -0
  141. package/src/questions/matching.tsx +228 -0
  142. package/src/questions/multiple-choice.tsx +7 -9
  143. package/src/questions/numeric.tsx +102 -0
  144. package/src/questions/ordering.tsx +159 -0
  145. package/src/questions/question-renderer.tsx +21 -0
  146. package/src/questions/scenario.tsx +140 -0
  147. package/src/questions/scoring.ts +201 -0
  148. package/src/questions/spreadsheet.tsx +259 -0
  149. package/src/questions/true-false.tsx +7 -9
  150. package/src/questions/types.ts +123 -1
  151. package/src/questions/use-drag-reorder.ts +80 -0
  152. package/src/sections/AnnouncementFeed/AnnouncementFeed.tsx +2 -15
  153. package/src/sections/AssessmentReview/AssessmentReview.tsx +13 -2
  154. package/src/sections/AssignmentSubmission/AssignmentSubmission.tsx +7 -5
  155. package/src/sections/CertificateViewer/CertificateViewer.tsx +409 -56
  156. package/src/sections/CertificateViewer/types.ts +13 -5
  157. package/src/sections/CourseOutline/CourseOutline.tsx +4 -14
  158. package/src/sections/DiscussionThread/DiscussionThread.tsx +13 -10
  159. package/src/sections/ExamSession/ExamSession.tsx +44 -7
  160. package/src/sections/ExamSession/types.ts +6 -1
  161. package/src/sections/ForumBoard/ForumBoard.tsx +284 -0
  162. package/src/sections/ForumBoard/types.ts +67 -0
  163. package/src/sections/GradebookTable/GradebookTable.tsx +1 -1
  164. package/src/sections/LecturePlayer/LecturePlayer.tsx +1 -1
  165. package/src/sections/LessonPage/LessonPage.tsx +5 -9
  166. package/src/sections/PracticeQuiz/PracticeQuiz.tsx +15 -26
  167. package/src/sections/ProgressDashboard/ProgressDashboard.tsx +65 -65
  168. package/src/sections/QuizSession/QuizSession.tsx +67 -8
  169. package/src/sections/QuizSession/types.ts +6 -1
  170. package/src/sections/RequirementsChecklist/RequirementsChecklist.tsx +107 -0
  171. package/src/sections/RequirementsChecklist/types.ts +38 -0
  172. package/src/sections/ResourceLibrary/ResourceLibrary.tsx +4 -9
  173. package/src/sections/RubricView/RubricView.tsx +138 -0
  174. package/src/sections/RubricView/types.ts +52 -0
  175. package/src/sections/ScrollableQuiz/ScrollableQuiz.tsx +23 -9
  176. package/src/sections/SurveyForm/SurveyForm.tsx +8 -5
  177. package/src/sections/index.ts +20 -1
  178. package/src/social/post-card.tsx +8 -19
  179. package/src/social/user-avatar.tsx +1 -0
  180. package/src/styles/globals.css +13 -0
  181. package/src/ui/drawer.tsx +600 -0
  182. package/src/ui/index.ts +19 -0
  183. package/src/ui/rich-text-editor.tsx +109 -0
  184. package/src/ui/rich-text-toolbar.tsx +156 -0
  185. package/src/utils/array-utils.ts +17 -0
  186. package/src/utils/flatten-leaves.ts +17 -0
  187. package/src/utils/format-file-size.ts +5 -0
  188. package/src/utils/format-timestamp.ts +13 -0
  189. package/src/utils/is-empty-html.ts +7 -0
  190. package/src/utils/shuffle.ts +8 -0
  191. package/src/utils/string-utils.ts +30 -0
  192. package/src/video/video-bookmark.tsx +4 -3
  193. package/src/video/video-chapter-list.tsx +9 -4
  194. package/src/video/video-player.tsx +11 -4
  195. package/src/video/video-playlist-item.tsx +8 -3
  196. package/src/video/video-thumbnail-card.tsx +4 -0
  197. package/src/video/video-transcript.tsx +8 -5
  198. package/dist/table-BrS5cDQu.js +0 -2510
  199. package/dist/table-D6AkBBEo.cjs +0 -1
@@ -3,7 +3,17 @@ export type QuestionTypeEnum =
3
3
  | "choice"
4
4
  | "true_false"
5
5
  | "fill_in_the_blank"
6
- | "essay";
6
+ | "essay"
7
+ | "matching"
8
+ | "ordering"
9
+ | "numeric"
10
+ | "hotspot"
11
+ | "inline_choice"
12
+ | "scenario"
13
+ | "spreadsheet";
14
+
15
+ /** Scoring strategy for scenario questions. */
16
+ export type ScenarioScoringMode = "per_question" | "all_or_nothing";
7
17
 
8
18
  export interface AnswerOption {
9
19
  uid: string;
@@ -20,12 +30,124 @@ export interface SessionAnswer {
20
30
  confidence?: string;
21
31
  }
22
32
 
33
+ /** A pair linking a left-side item to its correct right-side target. */
34
+ export interface MatchingPair {
35
+ uid: string;
36
+ /** Left-side item content (HTML) */
37
+ item: string;
38
+ /** Right-side target content (HTML) */
39
+ target: string;
40
+ sequence: number;
41
+ }
42
+
43
+ /** A clickable region on a hotspot image. Coordinates are percentages (0–100). */
44
+ export interface HotspotRegion {
45
+ uid: string;
46
+ shape: "rect" | "circle";
47
+ /** rect: [x, y, width, height]; circle: [centerX, centerY, radius] */
48
+ coords: number[];
49
+ isCorrect?: boolean;
50
+ label?: string;
51
+ }
52
+
53
+ /** A dropdown blank embedded in an inline-choice (cloze) passage. */
54
+ export interface InlineBlank {
55
+ uid: string;
56
+ options: { uid: string; content: string; isCorrect?: boolean }[];
57
+ sequence: number;
58
+ }
59
+
60
+ /** A column definition for a spreadsheet question. */
61
+ export interface SpreadsheetColumn {
62
+ uid: string;
63
+ /** Column header text */
64
+ label: string;
65
+ /** Data type hint for input rendering and scoring */
66
+ type: "text" | "numeric" | "currency";
67
+ /** Column width in rems (default 8) */
68
+ width?: number;
69
+ }
70
+
71
+ /** A single cell in a spreadsheet row. */
72
+ export interface SpreadsheetCell {
73
+ /** Unique identifier — becomes the answerUid in SessionAnswer */
74
+ uid: string;
75
+ /** Which column this cell belongs to */
76
+ columnUid: string;
77
+ /** When true, cell is pre-filled and not editable */
78
+ locked: boolean;
79
+ /** Display value for locked cells, or pre-fill for editable cells */
80
+ value?: string;
81
+ /** Expected answer for grading (editable cells only) */
82
+ correctAnswer?: string;
83
+ /** Acceptable numeric deviation (default 0 = exact match) */
84
+ tolerance?: number;
85
+ /** Hint shown in review mode (e.g., "= Revenue – Expenses") */
86
+ formula?: string;
87
+ }
88
+
89
+ /** A row in a spreadsheet question grid. */
90
+ export interface SpreadsheetRow {
91
+ uid: string;
92
+ /** Optional row header label (e.g., "Cash", "Accounts Receivable") */
93
+ label?: string;
94
+ /** Ordered cells — one per column */
95
+ cells: SpreadsheetCell[];
96
+ /** Renders as a bold section divider row */
97
+ isHeader?: boolean;
98
+ /** Renders in table footer with top border */
99
+ isTotals?: boolean;
100
+ }
101
+
102
+ /** Content blocks associated with a question for open-book reference. */
103
+ export interface QuestionMaterial {
104
+ /** UID of the question this material is linked to */
105
+ questionUid: string;
106
+ /** Display label for the material group (e.g., "Chapter 3: Bonds") */
107
+ label?: string;
108
+ /** Content blocks to render in the materials drawer */
109
+ blocks: import("../content/types").LessonBlock[];
110
+ }
111
+
23
112
  export interface QuestionData {
24
113
  uid: string;
25
114
  type: QuestionTypeEnum;
115
+ /** Question prompt (HTML) */
26
116
  content: string;
117
+ /** Explanation shown in review mode (HTML) */
27
118
  explanation?: string;
119
+ /** Answer options for choice / multiple_choice / true_false / ordering */
28
120
  answers?: AnswerOption[];
121
+
122
+ /** Matching-specific: pairs of items and targets */
123
+ matchingPairs?: MatchingPair[];
124
+
125
+ /** Numeric-specific: correct answer */
126
+ numericAnswer?: number;
127
+ /** Numeric-specific: acceptable deviation (default 0 = exact) */
128
+ numericTolerance?: number;
129
+ /** Numeric-specific: unit label displayed after input */
130
+ numericUnit?: string;
131
+
132
+ /** Hotspot-specific: image URL */
133
+ hotspotImageUrl?: string;
134
+ /** Hotspot-specific: clickable regions */
135
+ hotspotRegions?: HotspotRegion[];
136
+ /** Hotspot-specific: allow selecting multiple regions */
137
+ hotspotMultiSelect?: boolean;
138
+
139
+ /** InlineChoice-specific: dropdown blanks embedded in content */
140
+ inlineBlanks?: InlineBlank[];
141
+
142
+ /** Scenario-specific: sub-questions grouped under this scenario's shared context */
143
+ scenarioQuestions?: QuestionData[];
144
+ /** Scenario-specific: how the scenario is scored (default "per_question") */
145
+ scenarioScoringMode?: ScenarioScoringMode;
146
+
147
+ /** Spreadsheet-specific: column definitions */
148
+ spreadsheetColumns?: SpreadsheetColumn[];
149
+ /** Spreadsheet-specific: row data with cells */
150
+ spreadsheetRows?: SpreadsheetRow[];
29
151
  }
30
152
 
31
153
  /**
@@ -0,0 +1,80 @@
1
+ import { useState, useCallback, useRef } from "react";
2
+
3
+ interface UseDragReorderOptions<T> {
4
+ items: T[];
5
+ onReorder: (items: T[]) => void;
6
+ disabled?: boolean;
7
+ }
8
+
9
+ export function useDragReorder<T>({
10
+ items,
11
+ onReorder,
12
+ disabled = false,
13
+ }: UseDragReorderOptions<T>) {
14
+ const [dragIndex, setDragIndex] = useState<number | null>(null);
15
+ const [dragOverIndex, setDragOverIndex] = useState<number | null>(null);
16
+ const dragIndexRef = useRef<number | null>(null);
17
+
18
+ const handleDragStart = useCallback(
19
+ (index: number) => (e: React.DragEvent) => {
20
+ if (disabled) {
21
+ e.preventDefault();
22
+ return;
23
+ }
24
+ dragIndexRef.current = index;
25
+ setDragIndex(index);
26
+ e.dataTransfer.effectAllowed = "move";
27
+ e.dataTransfer.setData("text/plain", String(index));
28
+ },
29
+ [disabled],
30
+ );
31
+
32
+ const handleDragOver = useCallback(
33
+ (index: number) => (e: React.DragEvent) => {
34
+ e.preventDefault();
35
+ e.dataTransfer.dropEffect = "move";
36
+ setDragOverIndex(index);
37
+ },
38
+ [],
39
+ );
40
+
41
+ const handleDrop = useCallback(
42
+ (index: number) => (e: React.DragEvent) => {
43
+ e.preventDefault();
44
+ const fromIndex = dragIndexRef.current;
45
+ if (fromIndex === null || fromIndex === index) {
46
+ setDragIndex(null);
47
+ setDragOverIndex(null);
48
+ return;
49
+ }
50
+
51
+ const next = [...items];
52
+ const [moved] = next.splice(fromIndex, 1);
53
+ next.splice(index, 0, moved);
54
+
55
+ setDragIndex(null);
56
+ setDragOverIndex(null);
57
+ onReorder(next);
58
+ },
59
+ [items, onReorder],
60
+ );
61
+
62
+ const handleDragEnd = useCallback(() => {
63
+ setDragIndex(null);
64
+ setDragOverIndex(null);
65
+ dragIndexRef.current = null;
66
+ }, []);
67
+
68
+ const getDragProps = useCallback(
69
+ (index: number) => ({
70
+ draggable: !disabled,
71
+ onDragStart: handleDragStart(index),
72
+ onDragOver: handleDragOver(index),
73
+ onDrop: handleDrop(index),
74
+ onDragEnd: handleDragEnd,
75
+ }),
76
+ [disabled, handleDragStart, handleDragOver, handleDrop, handleDragEnd],
77
+ );
78
+
79
+ return { getDragProps, dragIndex, dragOverIndex };
80
+ }
@@ -7,20 +7,7 @@ import { Button } from "../../ui/button";
7
7
  import { Card, CardContent } from "../../ui/card";
8
8
  import type { AnnouncementFeedProps } from "./types";
9
9
  import { cn } from "../../lib/utils";
10
-
11
- function formatTimestamp(iso: string): string {
12
- const date = new Date(iso);
13
- const now = new Date();
14
- const diffMs = now.getTime() - date.getTime();
15
- const diffMins = Math.floor(diffMs / 60000);
16
- if (diffMins < 1) return "Just now";
17
- if (diffMins < 60) return `${diffMins}m ago`;
18
- const diffHours = Math.floor(diffMins / 60);
19
- if (diffHours < 24) return `${diffHours}h ago`;
20
- const diffDays = Math.floor(diffHours / 24);
21
- if (diffDays < 7) return `${diffDays}d ago`;
22
- return date.toLocaleDateString();
23
- }
10
+ import { formatTimestamp } from "../../utils/format-timestamp";
24
11
 
25
12
  export function AnnouncementFeed({
26
13
  announcements,
@@ -77,7 +64,7 @@ export function AnnouncementFeed({
77
64
  )}
78
65
  onClick={() => onSelect && !readOnly ? onSelect(a) : toggleExpand(a.uid)}
79
66
  >
80
- <CardContent className="pt-4 pb-4">
67
+ <CardContent className="py-4">
81
68
  <div className="flex gap-1.5 items-start">
82
69
  {showAvatars && (
83
70
  <UserAvatar
@@ -1,3 +1,4 @@
1
+ import { useMemo } from "react";
1
2
  import { QuestionRenderer } from "../../questions";
2
3
  import type { QuestionData, SessionAnswer } from "../../questions/types";
3
4
  import { Badge } from "../../ui/badge";
@@ -25,7 +26,7 @@ function ScoreHeader({
25
26
  <Card className="mb-3">
26
27
  <CardContent className="pt-6">
27
28
  <div className="flex flex-wrap items-center gap-2">
28
- <div>
29
+ <div className="flex items-baseline gap-2">
29
30
  <span className="text-2xl font-bold leading-none text-foreground">{pct}%</span>
30
31
  <span className="text-sm text-muted-foreground">
31
32
  {score.correct} of {score.total} correct
@@ -56,6 +57,16 @@ function QuestionList({
56
57
  sessionAnswers: SessionAnswer[];
57
58
  showCorrectAnswers: boolean;
58
59
  }) {
60
+ const answersByQuestion = useMemo(() => {
61
+ const map = new Map<string, SessionAnswer[]>();
62
+ for (const a of sessionAnswers) {
63
+ const list = map.get(a.uid);
64
+ if (list) list.push(a);
65
+ else map.set(a.uid, [a]);
66
+ }
67
+ return map;
68
+ }, [sessionAnswers]);
69
+
59
70
  return (
60
71
  <div className="flex flex-col gap-3">
61
72
  {questions.map((question, idx) => (
@@ -69,7 +80,7 @@ function QuestionList({
69
80
  <CardContent className="pt-4 pb-4">
70
81
  <QuestionRenderer
71
82
  question={question}
72
- sessionAnswers={sessionAnswers.filter((a) => a.uid === question.uid)}
83
+ sessionAnswers={answersByQuestion.get(question.uid) ?? []}
73
84
  readOnly
74
85
  showCorrectAnswers={showCorrectAnswers}
75
86
  />
@@ -3,7 +3,7 @@ import { Send, Save } from "lucide-react";
3
3
  import { StatusBadge, DueDateDisplay } from "../../common";
4
4
  import { FileUploadZone } from "../../content";
5
5
  import { Button } from "../../ui/button";
6
- import { Textarea } from "../../ui/textarea";
6
+ import { RichTextEditor } from "../../ui/rich-text-editor";
7
7
  import { Input } from "../../ui/input";
8
8
  import { Separator } from "../../ui/separator";
9
9
  import { Card, CardContent } from "../../ui/card";
@@ -99,11 +99,12 @@ export function AssignmentSubmission({
99
99
  </TabsList>
100
100
  {submissionTypes.includes("text") && (
101
101
  <TabsContent value="text">
102
- <Textarea
102
+ <RichTextEditor
103
103
  className="min-h-45"
104
104
  placeholder="Type your submission..."
105
105
  value={textContent}
106
- onChange={(e) => setTextContent(e.target.value)}
106
+ onChange={(html) => setTextContent(html)}
107
+ variant="default"
107
108
  />
108
109
  </TabsContent>
109
110
  )}
@@ -132,11 +133,12 @@ export function AssignmentSubmission({
132
133
  ) : (
133
134
  <>
134
135
  {submissionTypes.includes("text") && (
135
- <Textarea
136
+ <RichTextEditor
136
137
  className="min-h-45 mb-2"
137
138
  placeholder="Type your submission..."
138
139
  value={textContent}
139
- onChange={(e) => setTextContent(e.target.value)}
140
+ onChange={(html) => setTextContent(html)}
141
+ variant="default"
140
142
  />
141
143
  )}
142
144
  {submissionTypes.includes("file") && (