@hydralms/components 0.1.3 → 0.3.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.
- package/dist/StudentProfile-BVfZMbnV.cjs +1 -0
- package/dist/StudentProfile-DeMxdrL3.js +3275 -0
- package/dist/assessment-toolbar/assessment-toolbar.d.ts +1 -1
- package/dist/assessment-toolbar/index.d.ts +5 -1
- package/dist/assessment-toolbar/question-header-bar.d.ts +2 -0
- package/dist/assessment-toolbar/question-materials-drawer.d.ts +2 -0
- package/dist/assessment-toolbar/question-navigator.d.ts +1 -1
- package/dist/assessment-toolbar/timer-display.d.ts +1 -1
- package/dist/assessment-toolbar/types.d.ts +52 -4
- package/dist/assessment-toolbar/use-countdown.d.ts +43 -0
- package/dist/common/index.d.ts +3 -1
- package/dist/common/pagination.d.ts +26 -0
- package/dist/common/stepper.d.ts +6 -0
- package/dist/common/types.d.ts +38 -0
- package/dist/components.css +1 -1
- package/dist/content/attachment-list.d.ts +6 -0
- package/dist/content/audio-player.d.ts +22 -0
- package/dist/content/code-block.d.ts +30 -0
- package/dist/content/content-block.d.ts +1 -1
- package/dist/content/embed-block.d.ts +28 -0
- package/dist/content/index.d.ts +8 -1
- package/dist/content/types.d.ts +63 -0
- package/dist/curriculum/course-card.d.ts +51 -0
- package/dist/curriculum/curriculum-item.d.ts +1 -1
- package/dist/curriculum/index.d.ts +2 -0
- package/dist/curriculum/types.d.ts +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +597 -308
- package/dist/license/HydraContext.d.ts +16 -0
- package/dist/license/ProBadge.d.ts +6 -0
- package/dist/license/index.d.ts +7 -0
- package/dist/license/tiers.d.ts +3 -0
- package/dist/license/useHydraLicense.d.ts +6 -0
- package/dist/license/validate.d.ts +13 -0
- package/dist/license/withProGate.d.ts +6 -0
- package/dist/modules/AssignmentModule/AssignmentModule.d.ts +5 -0
- package/dist/modules/AssignmentModule/types.d.ts +69 -0
- package/dist/modules/CertificateModule/CertificateModule.d.ts +5 -0
- package/dist/modules/CertificateModule/types.d.ts +51 -0
- package/dist/modules/CourseCatalogModule/CourseCatalogModule.d.ts +5 -0
- package/dist/modules/CourseCatalogModule/types.d.ts +43 -0
- package/dist/modules/CoursePlayer/CoursePlayer.d.ts +4 -1
- package/dist/modules/DiscussionModule/DiscussionModule.d.ts +5 -0
- package/dist/modules/DiscussionModule/types.d.ts +47 -0
- package/dist/modules/ExamModule/ExamModule.d.ts +5 -0
- package/dist/modules/ExamModule/types.d.ts +55 -0
- package/dist/modules/FlashcardLab/FlashcardLab.d.ts +4 -1
- package/dist/modules/FlashcardLab/types.d.ts +2 -0
- package/dist/modules/GradeCenterModule/GradeCenterModule.d.ts +5 -0
- package/dist/modules/GradeCenterModule/types.d.ts +56 -0
- package/dist/modules/QuizModule/QuizModule.d.ts +4 -1
- package/dist/modules/QuizModule/types.d.ts +10 -14
- package/dist/modules/StudentDashboardModule/StudentDashboardModule.d.ts +5 -0
- package/dist/modules/StudentDashboardModule/types.d.ts +54 -0
- package/dist/modules/StudentProfileModule/StudentProfileModule.d.ts +5 -0
- package/dist/modules/StudentProfileModule/types.d.ts +43 -0
- package/dist/modules/SurveyModule/SurveyModule.d.ts +5 -0
- package/dist/modules/SurveyModule/types.d.ts +51 -0
- package/dist/modules/_shared/assessment-intro.d.ts +16 -0
- package/dist/modules/_shared/assessment-results.d.ts +23 -0
- package/dist/modules/_shared/types.d.ts +10 -0
- package/dist/modules/_shared/use-timer.d.ts +9 -0
- package/dist/modules/index.d.ts +18 -0
- package/dist/modules.cjs +1 -0
- package/dist/modules.js +1834 -0
- package/dist/progress/achievement-badge.d.ts +6 -0
- package/dist/progress/activity-timeline.d.ts +6 -0
- package/dist/progress/index.d.ts +4 -1
- package/dist/progress/stat-card.d.ts +1 -1
- package/dist/progress/streak-badge.d.ts +6 -0
- package/dist/progress/types.d.ts +99 -0
- package/dist/provider/HydraProvider.d.ts +5 -1
- package/dist/questions/choice.d.ts +1 -1
- package/dist/questions/confidence-indicator.d.ts +37 -0
- package/dist/questions/essay.d.ts +2 -2
- package/dist/questions/fill-in-the-blank.d.ts +1 -1
- package/dist/questions/hotspot.d.ts +21 -0
- package/dist/questions/index.d.ts +11 -1
- package/dist/questions/inline-choice.d.ts +21 -0
- package/dist/questions/matching.d.ts +22 -0
- package/dist/questions/multiple-choice.d.ts +1 -1
- package/dist/questions/numeric.d.ts +11 -0
- package/dist/questions/ordering.d.ts +12 -0
- package/dist/questions/question-renderer.d.ts +1 -1
- package/dist/questions/scenario.d.ts +23 -0
- package/dist/questions/scoring.d.ts +22 -0
- package/dist/questions/spreadsheet.d.ts +29 -0
- package/dist/questions/true-false.d.ts +1 -1
- package/dist/questions/types.d.ts +106 -1
- package/dist/questions/use-drag-reorder.d.ts +17 -0
- package/dist/sections/AnnouncementFeed/AnnouncementFeed.d.ts +1 -1
- package/dist/sections/AnnouncementFeed/types.d.ts +15 -1
- package/dist/sections/AssessmentReview/AssessmentReview.d.ts +1 -1
- package/dist/sections/AssessmentReview/types.d.ts +6 -0
- package/dist/sections/AssignmentSubmission/AssignmentSubmission.d.ts +1 -1
- package/dist/sections/AssignmentSubmission/types.d.ts +6 -0
- package/dist/sections/CertificateViewer/CertificateViewer.d.ts +1 -1
- package/dist/sections/CertificateViewer/certificate-variants.d.ts +42 -0
- package/dist/sections/CertificateViewer/types.d.ts +13 -5
- package/dist/sections/CourseCatalog/CourseCatalog.d.ts +2 -0
- package/dist/sections/CourseCatalog/types.d.ts +80 -0
- package/dist/sections/CourseOutline/CourseOutline.d.ts +1 -1
- package/dist/sections/CourseOutline/types.d.ts +6 -0
- package/dist/sections/DiscussionThread/DiscussionThread.d.ts +1 -1
- package/dist/sections/DiscussionThread/types.d.ts +6 -0
- package/dist/sections/EnrollmentWizard/EnrollmentWizard.d.ts +2 -0
- package/dist/sections/EnrollmentWizard/types.d.ts +66 -0
- package/dist/sections/ExamSession/ExamSession.d.ts +1 -1
- package/dist/sections/ExamSession/types.d.ts +12 -1
- package/dist/sections/FlashcardStudySession/FlashcardStudySession.d.ts +1 -1
- package/dist/sections/FlashcardStudySession/types.d.ts +6 -0
- package/dist/sections/ForumBoard/ForumBoard.d.ts +8 -0
- package/dist/sections/ForumBoard/types.d.ts +78 -0
- package/dist/sections/GradebookTable/GradebookTable.d.ts +1 -1
- package/dist/sections/GradebookTable/types.d.ts +14 -0
- package/dist/sections/LecturePlayer/LecturePlayer.d.ts +1 -1
- package/dist/sections/LecturePlayer/types.d.ts +8 -0
- package/dist/sections/LessonPage/LessonPage.d.ts +1 -1
- package/dist/sections/LessonPage/types.d.ts +6 -0
- package/dist/sections/PracticeQuiz/PracticeQuiz.d.ts +1 -1
- package/dist/sections/PracticeQuiz/types.d.ts +6 -0
- package/dist/sections/ProgressDashboard/ProgressDashboard.d.ts +1 -1
- package/dist/sections/ProgressDashboard/types.d.ts +6 -0
- package/dist/sections/QuizSession/QuizSession.d.ts +1 -1
- package/dist/sections/QuizSession/types.d.ts +12 -1
- package/dist/sections/RequirementsChecklist/RequirementsChecklist.d.ts +8 -0
- package/dist/sections/RequirementsChecklist/types.d.ts +43 -0
- package/dist/sections/ResourceLibrary/ResourceLibrary.d.ts +1 -1
- package/dist/sections/ResourceLibrary/types.d.ts +15 -1
- package/dist/sections/RubricView/RubricView.d.ts +9 -0
- package/dist/sections/RubricView/types.d.ts +56 -0
- package/dist/sections/ScrollableQuiz/ScrollableQuiz.d.ts +1 -1
- package/dist/sections/ScrollableQuiz/types.d.ts +6 -0
- package/dist/sections/StudentProfile/StudentProfile.d.ts +2 -0
- package/dist/sections/StudentProfile/types.d.ts +98 -0
- package/dist/sections/SurveyForm/SurveyForm.d.ts +1 -1
- package/dist/sections/SurveyForm/types.d.ts +6 -0
- package/dist/sections/_shared/merge-answers.d.ts +9 -0
- package/dist/sections/_shared/section-shell.d.ts +20 -0
- package/dist/sections/_shared/use-assessment-session.d.ts +30 -0
- package/dist/sections/index.d.ts +13 -1
- package/dist/sections.cjs +1 -1
- package/dist/sections.js +282 -1786
- package/dist/social/post-card.d.ts +1 -1
- package/dist/tabs-BsfVo2Bl.cjs +173 -0
- package/dist/tabs-BuY1iNJE.js +22305 -0
- package/dist/ui/alert.d.ts +1 -1
- package/dist/ui/badge.d.ts +1 -1
- package/dist/ui/button.d.ts +1 -1
- package/dist/ui/drawer.d.ts +84 -0
- package/dist/ui/index.d.ts +5 -0
- package/dist/ui/progress.d.ts +1 -1
- package/dist/ui/rich-text-editor.d.ts +32 -0
- package/dist/ui/rich-text-toolbar.d.ts +8 -0
- package/dist/ui/toast.d.ts +43 -0
- package/dist/utils/array-utils.d.ts +4 -0
- package/dist/utils/debounce.d.ts +5 -1
- package/dist/utils/flatten-leaves.d.ts +6 -0
- package/dist/utils/format-file-size.d.ts +1 -0
- package/dist/utils/format-timestamp.d.ts +1 -0
- package/dist/utils/is-empty-html.d.ts +5 -0
- package/dist/utils/pick-palette-color.d.ts +19 -0
- package/dist/utils/shuffle.d.ts +1 -0
- package/dist/utils/string-utils.d.ts +12 -0
- package/dist/video/types.d.ts +15 -0
- package/dist/video/video-bookmark.d.ts +1 -1
- package/dist/video/video-player.d.ts +1 -1
- package/dist/video/video-playlist-item.d.ts +1 -1
- package/dist/withProGate-BWqcKdPM.js +137 -0
- package/dist/withProGate-DX6XqKLp.cjs +1 -0
- package/package.json +40 -137
- package/src/assessment-toolbar/assessment-toolbar.tsx +54 -49
- package/src/assessment-toolbar/index.ts +6 -0
- package/src/assessment-toolbar/question-header-bar.tsx +61 -0
- package/src/assessment-toolbar/question-materials-drawer.tsx +55 -0
- package/src/assessment-toolbar/question-navigator.tsx +13 -36
- package/src/assessment-toolbar/timer-display.tsx +6 -5
- package/src/assessment-toolbar/types.ts +54 -4
- package/src/assessment-toolbar/use-countdown.ts +153 -0
- package/src/common/empty-state.tsx +1 -0
- package/src/common/index.ts +5 -0
- package/src/common/pagination.tsx +135 -0
- package/src/common/search-input.tsx +8 -6
- package/src/common/stepper.tsx +100 -0
- package/src/common/types.ts +41 -0
- package/src/content/attachment-list.tsx +92 -0
- package/src/content/audio-player.tsx +196 -0
- package/src/content/code-block.tsx +113 -0
- package/src/content/content-block.tsx +68 -2
- package/src/content/embed-block.tsx +78 -0
- package/src/content/file-upload-zone.tsx +11 -6
- package/src/content/index.ts +9 -0
- package/src/content/types.ts +46 -0
- package/src/curriculum/course-card.tsx +199 -0
- package/src/curriculum/curriculum-item.tsx +9 -5
- package/src/curriculum/curriculum-tree.tsx +20 -13
- package/src/curriculum/index.ts +2 -0
- package/src/curriculum/types.ts +2 -2
- package/src/feedback/feedback-banner.tsx +12 -14
- package/src/flashcards/flashcard-deck.tsx +1 -9
- package/src/flashcards/flashcard.tsx +29 -9
- package/src/index.ts +3 -0
- package/src/license/HydraContext.tsx +62 -0
- package/src/license/ProBadge.tsx +43 -0
- package/src/license/index.ts +7 -0
- package/src/license/tiers.ts +24 -0
- package/src/license/useHydraLicense.ts +10 -0
- package/src/license/validate.ts +90 -0
- package/src/license/withProGate.tsx +21 -0
- package/src/modules/AssignmentModule/AssignmentModule.tsx +314 -0
- package/src/modules/AssignmentModule/types.ts +77 -0
- package/src/modules/CertificateModule/CertificateModule.tsx +173 -0
- package/src/modules/CertificateModule/types.ts +49 -0
- package/src/modules/CourseCatalogModule/CourseCatalogModule.tsx +126 -0
- package/src/modules/CourseCatalogModule/types.ts +47 -0
- package/src/modules/CoursePlayer/CoursePlayer.tsx +80 -69
- package/src/modules/DiscussionModule/DiscussionModule.tsx +145 -0
- package/src/modules/DiscussionModule/types.ts +54 -0
- package/src/modules/ExamModule/ExamModule.tsx +151 -0
- package/src/modules/ExamModule/types.ts +57 -0
- package/src/modules/FlashcardLab/FlashcardLab.tsx +39 -21
- package/src/modules/FlashcardLab/types.ts +2 -0
- package/src/modules/GradeCenterModule/GradeCenterModule.tsx +174 -0
- package/src/modules/GradeCenterModule/types.ts +65 -0
- package/src/modules/QuizModule/QuizModule.tsx +58 -178
- package/src/modules/QuizModule/types.ts +10 -15
- package/src/modules/StudentDashboardModule/StudentDashboardModule.tsx +117 -0
- package/src/modules/StudentDashboardModule/types.ts +56 -0
- package/src/modules/StudentProfileModule/StudentProfileModule.tsx +289 -0
- package/src/modules/StudentProfileModule/types.ts +45 -0
- package/src/modules/SurveyModule/SurveyModule.tsx +185 -0
- package/src/modules/SurveyModule/types.ts +53 -0
- package/src/modules/_shared/assessment-intro.tsx +75 -0
- package/src/modules/_shared/assessment-results.tsx +133 -0
- package/src/modules/_shared/types.ts +11 -0
- package/src/modules/_shared/use-timer.ts +49 -0
- package/src/modules/index.ts +33 -0
- package/src/progress/achievement-badge.tsx +52 -0
- package/src/progress/activity-timeline.tsx +84 -0
- package/src/progress/grade-indicator.tsx +9 -1
- package/src/progress/index.ts +7 -0
- package/src/progress/progress-ring.tsx +2 -1
- package/src/progress/stat-card.tsx +37 -18
- package/src/progress/streak-badge.tsx +35 -0
- package/src/progress/types.ts +103 -0
- package/src/provider/HydraProvider.tsx +15 -6
- package/src/questions/choice.tsx +19 -14
- package/src/questions/confidence-indicator.tsx +107 -0
- package/src/questions/essay.tsx +28 -28
- package/src/questions/fill-in-the-blank.tsx +20 -19
- package/src/questions/hotspot.tsx +154 -0
- package/src/questions/index.ts +18 -0
- package/src/questions/inline-choice.tsx +152 -0
- package/src/questions/matching.tsx +229 -0
- package/src/questions/multiple-choice.tsx +19 -14
- package/src/questions/numeric.tsx +106 -0
- package/src/questions/ordering.tsx +167 -0
- package/src/questions/question-renderer.tsx +24 -2
- package/src/questions/scenario.tsx +140 -0
- package/src/questions/scoring.ts +201 -0
- package/src/questions/spreadsheet.tsx +260 -0
- package/src/questions/true-false.tsx +19 -14
- package/src/questions/types.ts +123 -1
- package/src/questions/use-drag-reorder.ts +80 -0
- package/src/sections/AnnouncementFeed/AnnouncementFeed.tsx +66 -23
- package/src/sections/AnnouncementFeed/types.ts +15 -1
- package/src/sections/AssessmentReview/AssessmentReview.tsx +50 -2
- package/src/sections/AssessmentReview/types.ts +6 -0
- package/src/sections/AssignmentSubmission/AssignmentSubmission.tsx +44 -6
- package/src/sections/AssignmentSubmission/types.ts +6 -0
- package/src/sections/CertificateViewer/CertificateViewer.tsx +215 -60
- package/src/sections/CertificateViewer/certificate-variants.tsx +170 -0
- package/src/sections/CertificateViewer/types.ts +19 -5
- package/src/sections/CourseCatalog/CourseCatalog.tsx +220 -0
- package/src/sections/CourseCatalog/types.ts +76 -0
- package/src/sections/CourseOutline/CourseOutline.tsx +45 -14
- package/src/sections/CourseOutline/types.ts +6 -0
- package/src/sections/DiscussionThread/DiscussionThread.tsx +55 -11
- package/src/sections/DiscussionThread/types.ts +6 -0
- package/src/sections/EnrollmentWizard/EnrollmentWizard.tsx +343 -0
- package/src/sections/EnrollmentWizard/types.ts +65 -0
- package/src/sections/ExamSession/ExamSession.tsx +125 -82
- package/src/sections/ExamSession/types.ts +12 -1
- package/src/sections/FlashcardStudySession/FlashcardStudySession.tsx +53 -36
- package/src/sections/FlashcardStudySession/types.ts +6 -0
- package/src/sections/ForumBoard/ForumBoard.tsx +342 -0
- package/src/sections/ForumBoard/types.ts +81 -0
- package/src/sections/GradebookTable/GradebookTable.tsx +55 -2
- package/src/sections/GradebookTable/types.ts +14 -0
- package/src/sections/LecturePlayer/LecturePlayer.tsx +63 -37
- package/src/sections/LecturePlayer/types.ts +8 -0
- package/src/sections/LessonPage/LessonPage.tsx +40 -13
- package/src/sections/LessonPage/types.ts +6 -0
- package/src/sections/PracticeQuiz/PracticeQuiz.tsx +119 -98
- package/src/sections/PracticeQuiz/types.ts +6 -0
- package/src/sections/ProgressDashboard/ProgressDashboard.tsx +121 -67
- package/src/sections/ProgressDashboard/types.ts +6 -0
- package/src/sections/QuizSession/QuizSession.tsx +115 -67
- package/src/sections/QuizSession/types.ts +12 -1
- package/src/sections/RequirementsChecklist/RequirementsChecklist.tsx +147 -0
- package/src/sections/RequirementsChecklist/types.ts +44 -0
- package/src/sections/ResourceLibrary/ResourceLibrary.tsx +68 -17
- package/src/sections/ResourceLibrary/types.ts +15 -1
- package/src/sections/RubricView/RubricView.tsx +174 -0
- package/src/sections/RubricView/types.ts +58 -0
- package/src/sections/ScrollableQuiz/ScrollableQuiz.tsx +58 -23
- package/src/sections/ScrollableQuiz/types.ts +6 -0
- package/src/sections/StudentProfile/StudentProfile.tsx +279 -0
- package/src/sections/StudentProfile/types.ts +99 -0
- package/src/sections/SurveyForm/SurveyForm.tsx +40 -10
- package/src/sections/SurveyForm/types.ts +6 -0
- package/src/sections/_shared/merge-answers.ts +22 -0
- package/src/sections/_shared/section-shell.tsx +64 -0
- package/src/sections/_shared/use-assessment-session.ts +125 -0
- package/src/sections/index.ts +42 -1
- package/src/social/post-card.tsx +8 -19
- package/src/social/user-avatar.tsx +10 -5
- package/src/styles/globals.css +52 -41
- package/src/ui/badge.tsx +8 -0
- package/src/ui/drawer.tsx +600 -0
- package/src/ui/index.ts +21 -0
- package/src/ui/progress.tsx +4 -0
- package/src/ui/rich-text-editor.tsx +119 -0
- package/src/ui/rich-text-toolbar.tsx +157 -0
- package/src/ui/toast.tsx +170 -0
- package/src/utils/array-utils.ts +17 -0
- package/src/utils/debounce.ts +8 -2
- package/src/utils/flatten-leaves.ts +17 -0
- package/src/utils/format-file-size.ts +5 -0
- package/src/utils/format-timestamp.ts +13 -0
- package/src/utils/is-empty-html.ts +7 -0
- package/src/utils/pick-palette-color.ts +33 -0
- package/src/utils/shuffle.ts +8 -0
- package/src/utils/string-utils.ts +30 -0
- package/src/video/types.ts +16 -0
- package/src/video/video-bookmark.tsx +4 -3
- package/src/video/video-chapter-list.tsx +9 -4
- package/src/video/video-player.tsx +24 -5
- package/src/video/video-playlist-item.tsx +8 -3
- package/src/video/video-thumbnail-card.tsx +4 -0
- package/src/video/video-transcript.tsx +8 -5
- package/dist/table-BrS5cDQu.js +0 -2510
- package/dist/table-D6AkBBEo.cjs +0 -1
|
@@ -1,26 +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
|
-
|
|
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
|
-
}
|
|
11
|
+
import { formatTimestamp } from "../../utils/format-timestamp";
|
|
12
|
+
import { Pagination } from "../../common/pagination";
|
|
24
13
|
|
|
25
14
|
export function AnnouncementFeed({
|
|
26
15
|
announcements,
|
|
@@ -30,6 +19,13 @@ export function AnnouncementFeed({
|
|
|
30
19
|
previewLines = 3,
|
|
31
20
|
emptyMessage = "No announcements yet",
|
|
32
21
|
readOnly = false,
|
|
22
|
+
isLoading,
|
|
23
|
+
error,
|
|
24
|
+
onRetry,
|
|
25
|
+
pageSize,
|
|
26
|
+
currentPage = 1,
|
|
27
|
+
totalItems,
|
|
28
|
+
onPageChange,
|
|
33
29
|
className,
|
|
34
30
|
style,
|
|
35
31
|
}: AnnouncementFeedProps) {
|
|
@@ -54,6 +50,42 @@ export function AnnouncementFeed({
|
|
|
54
50
|
}
|
|
55
51
|
}
|
|
56
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
|
+
|
|
57
89
|
if (sorted.length === 0) {
|
|
58
90
|
return (
|
|
59
91
|
<div className={className} style={style}>
|
|
@@ -64,7 +96,10 @@ export function AnnouncementFeed({
|
|
|
64
96
|
|
|
65
97
|
return (
|
|
66
98
|
<div className={cn("flex flex-col gap-2", className)} style={style}>
|
|
67
|
-
{
|
|
99
|
+
{(onPageChange && pageSize
|
|
100
|
+
? sorted.slice((currentPage - 1) * pageSize, currentPage * pageSize)
|
|
101
|
+
: sorted
|
|
102
|
+
).map((a) => {
|
|
68
103
|
const isExpanded = expandedUids.has(a.uid);
|
|
69
104
|
|
|
70
105
|
return (
|
|
@@ -77,7 +112,7 @@ export function AnnouncementFeed({
|
|
|
77
112
|
)}
|
|
78
113
|
onClick={() => onSelect && !readOnly ? onSelect(a) : toggleExpand(a.uid)}
|
|
79
114
|
>
|
|
80
|
-
<CardContent className="
|
|
115
|
+
<CardContent className="py-4">
|
|
81
116
|
<div className="flex gap-1.5 items-start">
|
|
82
117
|
{showAvatars && (
|
|
83
118
|
<UserAvatar
|
|
@@ -104,9 +139,9 @@ export function AnnouncementFeed({
|
|
|
104
139
|
<span className="block text-xs text-muted-foreground mb-1">
|
|
105
140
|
{a.author.displayName} · {formatTimestamp(a.createdAt)}
|
|
106
141
|
</span>
|
|
107
|
-
<
|
|
142
|
+
<div
|
|
108
143
|
className={cn(
|
|
109
|
-
"text-sm text-foreground",
|
|
144
|
+
"text-sm text-foreground [&_p]:mb-2 [&_ul]:list-disc [&_ul]:pl-5 [&_ol]:list-decimal [&_ol]:pl-5",
|
|
110
145
|
!isExpanded && "line-clamp-(--preview-lines) overflow-hidden",
|
|
111
146
|
)}
|
|
112
147
|
style={
|
|
@@ -114,10 +149,9 @@ export function AnnouncementFeed({
|
|
|
114
149
|
? { "--preview-lines": previewLines } as React.CSSProperties
|
|
115
150
|
: undefined
|
|
116
151
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
{!isExpanded && a.content.length > 200 && (
|
|
152
|
+
dangerouslySetInnerHTML={{ __html: a.content }}
|
|
153
|
+
/>
|
|
154
|
+
{!isExpanded && a.content.replace(/<[^>]*>/g, "").length > 200 && (
|
|
121
155
|
<Button
|
|
122
156
|
variant="link"
|
|
123
157
|
size="xs"
|
|
@@ -136,6 +170,15 @@ export function AnnouncementFeed({
|
|
|
136
170
|
</Card>
|
|
137
171
|
);
|
|
138
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
|
+
)}
|
|
139
182
|
</div>
|
|
140
183
|
);
|
|
141
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,8 +1,13 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { AlertCircle } from "lucide-react";
|
|
1
3
|
import { QuestionRenderer } from "../../questions";
|
|
2
4
|
import type { QuestionData, SessionAnswer } from "../../questions/types";
|
|
3
5
|
import { Badge } from "../../ui/badge";
|
|
4
6
|
import { Card, CardContent } from "../../ui/card";
|
|
5
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";
|
|
6
11
|
import { cn } from "../../lib/utils";
|
|
7
12
|
import type {
|
|
8
13
|
AssessmentReviewProps,
|
|
@@ -25,7 +30,7 @@ function ScoreHeader({
|
|
|
25
30
|
<Card className="mb-3">
|
|
26
31
|
<CardContent className="pt-6">
|
|
27
32
|
<div className="flex flex-wrap items-center gap-2">
|
|
28
|
-
<div>
|
|
33
|
+
<div className="flex items-baseline gap-2">
|
|
29
34
|
<span className="text-2xl font-bold leading-none text-foreground">{pct}%</span>
|
|
30
35
|
<span className="text-sm text-muted-foreground">
|
|
31
36
|
{score.correct} of {score.total} correct
|
|
@@ -56,6 +61,16 @@ function QuestionList({
|
|
|
56
61
|
sessionAnswers: SessionAnswer[];
|
|
57
62
|
showCorrectAnswers: boolean;
|
|
58
63
|
}) {
|
|
64
|
+
const answersByQuestion = useMemo(() => {
|
|
65
|
+
const map = new Map<string, SessionAnswer[]>();
|
|
66
|
+
for (const a of sessionAnswers) {
|
|
67
|
+
const list = map.get(a.uid);
|
|
68
|
+
if (list) list.push(a);
|
|
69
|
+
else map.set(a.uid, [a]);
|
|
70
|
+
}
|
|
71
|
+
return map;
|
|
72
|
+
}, [sessionAnswers]);
|
|
73
|
+
|
|
59
74
|
return (
|
|
60
75
|
<div className="flex flex-col gap-3">
|
|
61
76
|
{questions.map((question, idx) => (
|
|
@@ -69,7 +84,7 @@ function QuestionList({
|
|
|
69
84
|
<CardContent className="pt-4 pb-4">
|
|
70
85
|
<QuestionRenderer
|
|
71
86
|
question={question}
|
|
72
|
-
sessionAnswers={
|
|
87
|
+
sessionAnswers={answersByQuestion.get(question.uid) ?? []}
|
|
73
88
|
readOnly
|
|
74
89
|
showCorrectAnswers={showCorrectAnswers}
|
|
75
90
|
/>
|
|
@@ -129,9 +144,42 @@ export function AssessmentReview({
|
|
|
129
144
|
score,
|
|
130
145
|
questionGroups,
|
|
131
146
|
showCorrectAnswers = true,
|
|
147
|
+
isLoading,
|
|
148
|
+
error,
|
|
149
|
+
onRetry,
|
|
132
150
|
className,
|
|
133
151
|
style,
|
|
134
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
|
+
|
|
135
183
|
return (
|
|
136
184
|
<div className={cn(className)} style={style}>
|
|
137
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,14 +1,17 @@
|
|
|
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";
|
|
6
|
-
import {
|
|
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";
|
|
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 {
|
|
@@ -99,11 +135,12 @@ export function AssignmentSubmission({
|
|
|
99
135
|
</TabsList>
|
|
100
136
|
{submissionTypes.includes("text") && (
|
|
101
137
|
<TabsContent value="text">
|
|
102
|
-
<
|
|
138
|
+
<RichTextEditor
|
|
103
139
|
className="min-h-45"
|
|
104
140
|
placeholder="Type your submission..."
|
|
105
141
|
value={textContent}
|
|
106
|
-
onChange={(
|
|
142
|
+
onChange={(html) => setTextContent(html)}
|
|
143
|
+
variant="default"
|
|
107
144
|
/>
|
|
108
145
|
</TabsContent>
|
|
109
146
|
)}
|
|
@@ -132,11 +169,12 @@ export function AssignmentSubmission({
|
|
|
132
169
|
) : (
|
|
133
170
|
<>
|
|
134
171
|
{submissionTypes.includes("text") && (
|
|
135
|
-
<
|
|
172
|
+
<RichTextEditor
|
|
136
173
|
className="min-h-45 mb-2"
|
|
137
174
|
placeholder="Type your submission..."
|
|
138
175
|
value={textContent}
|
|
139
|
-
onChange={(
|
|
176
|
+
onChange={(html) => setTextContent(html)}
|
|
177
|
+
variant="default"
|
|
140
178
|
/>
|
|
141
179
|
)}
|
|
142
180
|
{submissionTypes.includes("file") && (
|
|
@@ -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 */
|