@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.
Files changed (344) hide show
  1. package/dist/StudentProfile-BVfZMbnV.cjs +1 -0
  2. package/dist/StudentProfile-DeMxdrL3.js +3275 -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/timer-display.d.ts +1 -1
  9. package/dist/assessment-toolbar/types.d.ts +52 -4
  10. package/dist/assessment-toolbar/use-countdown.d.ts +43 -0
  11. package/dist/common/index.d.ts +3 -1
  12. package/dist/common/pagination.d.ts +26 -0
  13. package/dist/common/stepper.d.ts +6 -0
  14. package/dist/common/types.d.ts +38 -0
  15. package/dist/components.css +1 -1
  16. package/dist/content/attachment-list.d.ts +6 -0
  17. package/dist/content/audio-player.d.ts +22 -0
  18. package/dist/content/code-block.d.ts +30 -0
  19. package/dist/content/content-block.d.ts +1 -1
  20. package/dist/content/embed-block.d.ts +28 -0
  21. package/dist/content/index.d.ts +8 -1
  22. package/dist/content/types.d.ts +63 -0
  23. package/dist/curriculum/course-card.d.ts +51 -0
  24. package/dist/curriculum/curriculum-item.d.ts +1 -1
  25. package/dist/curriculum/index.d.ts +2 -0
  26. package/dist/curriculum/types.d.ts +2 -2
  27. package/dist/index.cjs +1 -1
  28. package/dist/index.d.ts +1 -0
  29. package/dist/index.js +597 -308
  30. package/dist/license/HydraContext.d.ts +16 -0
  31. package/dist/license/ProBadge.d.ts +6 -0
  32. package/dist/license/index.d.ts +7 -0
  33. package/dist/license/tiers.d.ts +3 -0
  34. package/dist/license/useHydraLicense.d.ts +6 -0
  35. package/dist/license/validate.d.ts +13 -0
  36. package/dist/license/withProGate.d.ts +6 -0
  37. package/dist/modules/AssignmentModule/AssignmentModule.d.ts +5 -0
  38. package/dist/modules/AssignmentModule/types.d.ts +69 -0
  39. package/dist/modules/CertificateModule/CertificateModule.d.ts +5 -0
  40. package/dist/modules/CertificateModule/types.d.ts +51 -0
  41. package/dist/modules/CourseCatalogModule/CourseCatalogModule.d.ts +5 -0
  42. package/dist/modules/CourseCatalogModule/types.d.ts +43 -0
  43. package/dist/modules/CoursePlayer/CoursePlayer.d.ts +4 -1
  44. package/dist/modules/DiscussionModule/DiscussionModule.d.ts +5 -0
  45. package/dist/modules/DiscussionModule/types.d.ts +47 -0
  46. package/dist/modules/ExamModule/ExamModule.d.ts +5 -0
  47. package/dist/modules/ExamModule/types.d.ts +55 -0
  48. package/dist/modules/FlashcardLab/FlashcardLab.d.ts +4 -1
  49. package/dist/modules/FlashcardLab/types.d.ts +2 -0
  50. package/dist/modules/GradeCenterModule/GradeCenterModule.d.ts +5 -0
  51. package/dist/modules/GradeCenterModule/types.d.ts +56 -0
  52. package/dist/modules/QuizModule/QuizModule.d.ts +4 -1
  53. package/dist/modules/QuizModule/types.d.ts +10 -14
  54. package/dist/modules/StudentDashboardModule/StudentDashboardModule.d.ts +5 -0
  55. package/dist/modules/StudentDashboardModule/types.d.ts +54 -0
  56. package/dist/modules/StudentProfileModule/StudentProfileModule.d.ts +5 -0
  57. package/dist/modules/StudentProfileModule/types.d.ts +43 -0
  58. package/dist/modules/SurveyModule/SurveyModule.d.ts +5 -0
  59. package/dist/modules/SurveyModule/types.d.ts +51 -0
  60. package/dist/modules/_shared/assessment-intro.d.ts +16 -0
  61. package/dist/modules/_shared/assessment-results.d.ts +23 -0
  62. package/dist/modules/_shared/types.d.ts +10 -0
  63. package/dist/modules/_shared/use-timer.d.ts +9 -0
  64. package/dist/modules/index.d.ts +18 -0
  65. package/dist/modules.cjs +1 -0
  66. package/dist/modules.js +1834 -0
  67. package/dist/progress/achievement-badge.d.ts +6 -0
  68. package/dist/progress/activity-timeline.d.ts +6 -0
  69. package/dist/progress/index.d.ts +4 -1
  70. package/dist/progress/stat-card.d.ts +1 -1
  71. package/dist/progress/streak-badge.d.ts +6 -0
  72. package/dist/progress/types.d.ts +99 -0
  73. package/dist/provider/HydraProvider.d.ts +5 -1
  74. package/dist/questions/choice.d.ts +1 -1
  75. package/dist/questions/confidence-indicator.d.ts +37 -0
  76. package/dist/questions/essay.d.ts +2 -2
  77. package/dist/questions/fill-in-the-blank.d.ts +1 -1
  78. package/dist/questions/hotspot.d.ts +21 -0
  79. package/dist/questions/index.d.ts +11 -1
  80. package/dist/questions/inline-choice.d.ts +21 -0
  81. package/dist/questions/matching.d.ts +22 -0
  82. package/dist/questions/multiple-choice.d.ts +1 -1
  83. package/dist/questions/numeric.d.ts +11 -0
  84. package/dist/questions/ordering.d.ts +12 -0
  85. package/dist/questions/question-renderer.d.ts +1 -1
  86. package/dist/questions/scenario.d.ts +23 -0
  87. package/dist/questions/scoring.d.ts +22 -0
  88. package/dist/questions/spreadsheet.d.ts +29 -0
  89. package/dist/questions/true-false.d.ts +1 -1
  90. package/dist/questions/types.d.ts +106 -1
  91. package/dist/questions/use-drag-reorder.d.ts +17 -0
  92. package/dist/sections/AnnouncementFeed/AnnouncementFeed.d.ts +1 -1
  93. package/dist/sections/AnnouncementFeed/types.d.ts +15 -1
  94. package/dist/sections/AssessmentReview/AssessmentReview.d.ts +1 -1
  95. package/dist/sections/AssessmentReview/types.d.ts +6 -0
  96. package/dist/sections/AssignmentSubmission/AssignmentSubmission.d.ts +1 -1
  97. package/dist/sections/AssignmentSubmission/types.d.ts +6 -0
  98. package/dist/sections/CertificateViewer/CertificateViewer.d.ts +1 -1
  99. package/dist/sections/CertificateViewer/certificate-variants.d.ts +42 -0
  100. package/dist/sections/CertificateViewer/types.d.ts +13 -5
  101. package/dist/sections/CourseCatalog/CourseCatalog.d.ts +2 -0
  102. package/dist/sections/CourseCatalog/types.d.ts +80 -0
  103. package/dist/sections/CourseOutline/CourseOutline.d.ts +1 -1
  104. package/dist/sections/CourseOutline/types.d.ts +6 -0
  105. package/dist/sections/DiscussionThread/DiscussionThread.d.ts +1 -1
  106. package/dist/sections/DiscussionThread/types.d.ts +6 -0
  107. package/dist/sections/EnrollmentWizard/EnrollmentWizard.d.ts +2 -0
  108. package/dist/sections/EnrollmentWizard/types.d.ts +66 -0
  109. package/dist/sections/ExamSession/ExamSession.d.ts +1 -1
  110. package/dist/sections/ExamSession/types.d.ts +12 -1
  111. package/dist/sections/FlashcardStudySession/FlashcardStudySession.d.ts +1 -1
  112. package/dist/sections/FlashcardStudySession/types.d.ts +6 -0
  113. package/dist/sections/ForumBoard/ForumBoard.d.ts +8 -0
  114. package/dist/sections/ForumBoard/types.d.ts +78 -0
  115. package/dist/sections/GradebookTable/GradebookTable.d.ts +1 -1
  116. package/dist/sections/GradebookTable/types.d.ts +14 -0
  117. package/dist/sections/LecturePlayer/LecturePlayer.d.ts +1 -1
  118. package/dist/sections/LecturePlayer/types.d.ts +8 -0
  119. package/dist/sections/LessonPage/LessonPage.d.ts +1 -1
  120. package/dist/sections/LessonPage/types.d.ts +6 -0
  121. package/dist/sections/PracticeQuiz/PracticeQuiz.d.ts +1 -1
  122. package/dist/sections/PracticeQuiz/types.d.ts +6 -0
  123. package/dist/sections/ProgressDashboard/ProgressDashboard.d.ts +1 -1
  124. package/dist/sections/ProgressDashboard/types.d.ts +6 -0
  125. package/dist/sections/QuizSession/QuizSession.d.ts +1 -1
  126. package/dist/sections/QuizSession/types.d.ts +12 -1
  127. package/dist/sections/RequirementsChecklist/RequirementsChecklist.d.ts +8 -0
  128. package/dist/sections/RequirementsChecklist/types.d.ts +43 -0
  129. package/dist/sections/ResourceLibrary/ResourceLibrary.d.ts +1 -1
  130. package/dist/sections/ResourceLibrary/types.d.ts +15 -1
  131. package/dist/sections/RubricView/RubricView.d.ts +9 -0
  132. package/dist/sections/RubricView/types.d.ts +56 -0
  133. package/dist/sections/ScrollableQuiz/ScrollableQuiz.d.ts +1 -1
  134. package/dist/sections/ScrollableQuiz/types.d.ts +6 -0
  135. package/dist/sections/StudentProfile/StudentProfile.d.ts +2 -0
  136. package/dist/sections/StudentProfile/types.d.ts +98 -0
  137. package/dist/sections/SurveyForm/SurveyForm.d.ts +1 -1
  138. package/dist/sections/SurveyForm/types.d.ts +6 -0
  139. package/dist/sections/_shared/merge-answers.d.ts +9 -0
  140. package/dist/sections/_shared/section-shell.d.ts +20 -0
  141. package/dist/sections/_shared/use-assessment-session.d.ts +30 -0
  142. package/dist/sections/index.d.ts +13 -1
  143. package/dist/sections.cjs +1 -1
  144. package/dist/sections.js +282 -1786
  145. package/dist/social/post-card.d.ts +1 -1
  146. package/dist/tabs-BsfVo2Bl.cjs +173 -0
  147. package/dist/tabs-BuY1iNJE.js +22305 -0
  148. package/dist/ui/alert.d.ts +1 -1
  149. package/dist/ui/badge.d.ts +1 -1
  150. package/dist/ui/button.d.ts +1 -1
  151. package/dist/ui/drawer.d.ts +84 -0
  152. package/dist/ui/index.d.ts +5 -0
  153. package/dist/ui/progress.d.ts +1 -1
  154. package/dist/ui/rich-text-editor.d.ts +32 -0
  155. package/dist/ui/rich-text-toolbar.d.ts +8 -0
  156. package/dist/ui/toast.d.ts +43 -0
  157. package/dist/utils/array-utils.d.ts +4 -0
  158. package/dist/utils/debounce.d.ts +5 -1
  159. package/dist/utils/flatten-leaves.d.ts +6 -0
  160. package/dist/utils/format-file-size.d.ts +1 -0
  161. package/dist/utils/format-timestamp.d.ts +1 -0
  162. package/dist/utils/is-empty-html.d.ts +5 -0
  163. package/dist/utils/pick-palette-color.d.ts +19 -0
  164. package/dist/utils/shuffle.d.ts +1 -0
  165. package/dist/utils/string-utils.d.ts +12 -0
  166. package/dist/video/types.d.ts +15 -0
  167. package/dist/video/video-bookmark.d.ts +1 -1
  168. package/dist/video/video-player.d.ts +1 -1
  169. package/dist/video/video-playlist-item.d.ts +1 -1
  170. package/dist/withProGate-BWqcKdPM.js +137 -0
  171. package/dist/withProGate-DX6XqKLp.cjs +1 -0
  172. package/package.json +40 -137
  173. package/src/assessment-toolbar/assessment-toolbar.tsx +54 -49
  174. package/src/assessment-toolbar/index.ts +6 -0
  175. package/src/assessment-toolbar/question-header-bar.tsx +61 -0
  176. package/src/assessment-toolbar/question-materials-drawer.tsx +55 -0
  177. package/src/assessment-toolbar/question-navigator.tsx +13 -36
  178. package/src/assessment-toolbar/timer-display.tsx +6 -5
  179. package/src/assessment-toolbar/types.ts +54 -4
  180. package/src/assessment-toolbar/use-countdown.ts +153 -0
  181. package/src/common/empty-state.tsx +1 -0
  182. package/src/common/index.ts +5 -0
  183. package/src/common/pagination.tsx +135 -0
  184. package/src/common/search-input.tsx +8 -6
  185. package/src/common/stepper.tsx +100 -0
  186. package/src/common/types.ts +41 -0
  187. package/src/content/attachment-list.tsx +92 -0
  188. package/src/content/audio-player.tsx +196 -0
  189. package/src/content/code-block.tsx +113 -0
  190. package/src/content/content-block.tsx +68 -2
  191. package/src/content/embed-block.tsx +78 -0
  192. package/src/content/file-upload-zone.tsx +11 -6
  193. package/src/content/index.ts +9 -0
  194. package/src/content/types.ts +46 -0
  195. package/src/curriculum/course-card.tsx +199 -0
  196. package/src/curriculum/curriculum-item.tsx +9 -5
  197. package/src/curriculum/curriculum-tree.tsx +20 -13
  198. package/src/curriculum/index.ts +2 -0
  199. package/src/curriculum/types.ts +2 -2
  200. package/src/feedback/feedback-banner.tsx +12 -14
  201. package/src/flashcards/flashcard-deck.tsx +1 -9
  202. package/src/flashcards/flashcard.tsx +29 -9
  203. package/src/index.ts +3 -0
  204. package/src/license/HydraContext.tsx +62 -0
  205. package/src/license/ProBadge.tsx +43 -0
  206. package/src/license/index.ts +7 -0
  207. package/src/license/tiers.ts +24 -0
  208. package/src/license/useHydraLicense.ts +10 -0
  209. package/src/license/validate.ts +90 -0
  210. package/src/license/withProGate.tsx +21 -0
  211. package/src/modules/AssignmentModule/AssignmentModule.tsx +314 -0
  212. package/src/modules/AssignmentModule/types.ts +77 -0
  213. package/src/modules/CertificateModule/CertificateModule.tsx +173 -0
  214. package/src/modules/CertificateModule/types.ts +49 -0
  215. package/src/modules/CourseCatalogModule/CourseCatalogModule.tsx +126 -0
  216. package/src/modules/CourseCatalogModule/types.ts +47 -0
  217. package/src/modules/CoursePlayer/CoursePlayer.tsx +80 -69
  218. package/src/modules/DiscussionModule/DiscussionModule.tsx +145 -0
  219. package/src/modules/DiscussionModule/types.ts +54 -0
  220. package/src/modules/ExamModule/ExamModule.tsx +151 -0
  221. package/src/modules/ExamModule/types.ts +57 -0
  222. package/src/modules/FlashcardLab/FlashcardLab.tsx +39 -21
  223. package/src/modules/FlashcardLab/types.ts +2 -0
  224. package/src/modules/GradeCenterModule/GradeCenterModule.tsx +174 -0
  225. package/src/modules/GradeCenterModule/types.ts +65 -0
  226. package/src/modules/QuizModule/QuizModule.tsx +58 -178
  227. package/src/modules/QuizModule/types.ts +10 -15
  228. package/src/modules/StudentDashboardModule/StudentDashboardModule.tsx +117 -0
  229. package/src/modules/StudentDashboardModule/types.ts +56 -0
  230. package/src/modules/StudentProfileModule/StudentProfileModule.tsx +289 -0
  231. package/src/modules/StudentProfileModule/types.ts +45 -0
  232. package/src/modules/SurveyModule/SurveyModule.tsx +185 -0
  233. package/src/modules/SurveyModule/types.ts +53 -0
  234. package/src/modules/_shared/assessment-intro.tsx +75 -0
  235. package/src/modules/_shared/assessment-results.tsx +133 -0
  236. package/src/modules/_shared/types.ts +11 -0
  237. package/src/modules/_shared/use-timer.ts +49 -0
  238. package/src/modules/index.ts +33 -0
  239. package/src/progress/achievement-badge.tsx +52 -0
  240. package/src/progress/activity-timeline.tsx +84 -0
  241. package/src/progress/grade-indicator.tsx +9 -1
  242. package/src/progress/index.ts +7 -0
  243. package/src/progress/progress-ring.tsx +2 -1
  244. package/src/progress/stat-card.tsx +37 -18
  245. package/src/progress/streak-badge.tsx +35 -0
  246. package/src/progress/types.ts +103 -0
  247. package/src/provider/HydraProvider.tsx +15 -6
  248. package/src/questions/choice.tsx +19 -14
  249. package/src/questions/confidence-indicator.tsx +107 -0
  250. package/src/questions/essay.tsx +28 -28
  251. package/src/questions/fill-in-the-blank.tsx +20 -19
  252. package/src/questions/hotspot.tsx +154 -0
  253. package/src/questions/index.ts +18 -0
  254. package/src/questions/inline-choice.tsx +152 -0
  255. package/src/questions/matching.tsx +229 -0
  256. package/src/questions/multiple-choice.tsx +19 -14
  257. package/src/questions/numeric.tsx +106 -0
  258. package/src/questions/ordering.tsx +167 -0
  259. package/src/questions/question-renderer.tsx +24 -2
  260. package/src/questions/scenario.tsx +140 -0
  261. package/src/questions/scoring.ts +201 -0
  262. package/src/questions/spreadsheet.tsx +260 -0
  263. package/src/questions/true-false.tsx +19 -14
  264. package/src/questions/types.ts +123 -1
  265. package/src/questions/use-drag-reorder.ts +80 -0
  266. package/src/sections/AnnouncementFeed/AnnouncementFeed.tsx +66 -23
  267. package/src/sections/AnnouncementFeed/types.ts +15 -1
  268. package/src/sections/AssessmentReview/AssessmentReview.tsx +50 -2
  269. package/src/sections/AssessmentReview/types.ts +6 -0
  270. package/src/sections/AssignmentSubmission/AssignmentSubmission.tsx +44 -6
  271. package/src/sections/AssignmentSubmission/types.ts +6 -0
  272. package/src/sections/CertificateViewer/CertificateViewer.tsx +215 -60
  273. package/src/sections/CertificateViewer/certificate-variants.tsx +170 -0
  274. package/src/sections/CertificateViewer/types.ts +19 -5
  275. package/src/sections/CourseCatalog/CourseCatalog.tsx +220 -0
  276. package/src/sections/CourseCatalog/types.ts +76 -0
  277. package/src/sections/CourseOutline/CourseOutline.tsx +45 -14
  278. package/src/sections/CourseOutline/types.ts +6 -0
  279. package/src/sections/DiscussionThread/DiscussionThread.tsx +55 -11
  280. package/src/sections/DiscussionThread/types.ts +6 -0
  281. package/src/sections/EnrollmentWizard/EnrollmentWizard.tsx +343 -0
  282. package/src/sections/EnrollmentWizard/types.ts +65 -0
  283. package/src/sections/ExamSession/ExamSession.tsx +125 -82
  284. package/src/sections/ExamSession/types.ts +12 -1
  285. package/src/sections/FlashcardStudySession/FlashcardStudySession.tsx +53 -36
  286. package/src/sections/FlashcardStudySession/types.ts +6 -0
  287. package/src/sections/ForumBoard/ForumBoard.tsx +342 -0
  288. package/src/sections/ForumBoard/types.ts +81 -0
  289. package/src/sections/GradebookTable/GradebookTable.tsx +55 -2
  290. package/src/sections/GradebookTable/types.ts +14 -0
  291. package/src/sections/LecturePlayer/LecturePlayer.tsx +63 -37
  292. package/src/sections/LecturePlayer/types.ts +8 -0
  293. package/src/sections/LessonPage/LessonPage.tsx +40 -13
  294. package/src/sections/LessonPage/types.ts +6 -0
  295. package/src/sections/PracticeQuiz/PracticeQuiz.tsx +119 -98
  296. package/src/sections/PracticeQuiz/types.ts +6 -0
  297. package/src/sections/ProgressDashboard/ProgressDashboard.tsx +121 -67
  298. package/src/sections/ProgressDashboard/types.ts +6 -0
  299. package/src/sections/QuizSession/QuizSession.tsx +115 -67
  300. package/src/sections/QuizSession/types.ts +12 -1
  301. package/src/sections/RequirementsChecklist/RequirementsChecklist.tsx +147 -0
  302. package/src/sections/RequirementsChecklist/types.ts +44 -0
  303. package/src/sections/ResourceLibrary/ResourceLibrary.tsx +68 -17
  304. package/src/sections/ResourceLibrary/types.ts +15 -1
  305. package/src/sections/RubricView/RubricView.tsx +174 -0
  306. package/src/sections/RubricView/types.ts +58 -0
  307. package/src/sections/ScrollableQuiz/ScrollableQuiz.tsx +58 -23
  308. package/src/sections/ScrollableQuiz/types.ts +6 -0
  309. package/src/sections/StudentProfile/StudentProfile.tsx +279 -0
  310. package/src/sections/StudentProfile/types.ts +99 -0
  311. package/src/sections/SurveyForm/SurveyForm.tsx +40 -10
  312. package/src/sections/SurveyForm/types.ts +6 -0
  313. package/src/sections/_shared/merge-answers.ts +22 -0
  314. package/src/sections/_shared/section-shell.tsx +64 -0
  315. package/src/sections/_shared/use-assessment-session.ts +125 -0
  316. package/src/sections/index.ts +42 -1
  317. package/src/social/post-card.tsx +8 -19
  318. package/src/social/user-avatar.tsx +10 -5
  319. package/src/styles/globals.css +52 -41
  320. package/src/ui/badge.tsx +8 -0
  321. package/src/ui/drawer.tsx +600 -0
  322. package/src/ui/index.ts +21 -0
  323. package/src/ui/progress.tsx +4 -0
  324. package/src/ui/rich-text-editor.tsx +119 -0
  325. package/src/ui/rich-text-toolbar.tsx +157 -0
  326. package/src/ui/toast.tsx +170 -0
  327. package/src/utils/array-utils.ts +17 -0
  328. package/src/utils/debounce.ts +8 -2
  329. package/src/utils/flatten-leaves.ts +17 -0
  330. package/src/utils/format-file-size.ts +5 -0
  331. package/src/utils/format-timestamp.ts +13 -0
  332. package/src/utils/is-empty-html.ts +7 -0
  333. package/src/utils/pick-palette-color.ts +33 -0
  334. package/src/utils/shuffle.ts +8 -0
  335. package/src/utils/string-utils.ts +30 -0
  336. package/src/video/types.ts +16 -0
  337. package/src/video/video-bookmark.tsx +4 -3
  338. package/src/video/video-chapter-list.tsx +9 -4
  339. package/src/video/video-player.tsx +24 -5
  340. package/src/video/video-playlist-item.tsx +8 -3
  341. package/src/video/video-thumbnail-card.tsx +4 -0
  342. package/src/video/video-transcript.tsx +8 -5
  343. package/dist/table-BrS5cDQu.js +0 -2510
  344. package/dist/table-D6AkBBEo.cjs +0 -1
@@ -1,14 +1,123 @@
1
+ import { useMemo } from "react";
1
2
  import { Award, Download, Printer } from "lucide-react";
3
+ import { cva } from "class-variance-authority";
4
+ import { Skeleton } from "../../ui/skeleton";
2
5
  import { Button } from "../../ui/button";
3
- import { Separator } from "../../ui/separator";
4
- import type { CertificateViewerProps } from "./types";
6
+ import type { CertificateViewerProps, CertificateVariant } from "./types";
5
7
  import { cn } from "../../lib/utils";
8
+ import { SectionShell } from "../_shared/section-shell";
9
+ import {
10
+ CORNER_CONFIGS,
11
+ CORNER_POSITIONS,
12
+ DIVIDER_CONFIGS,
13
+ BACKGROUND_GLOW,
14
+ ICON_COLORS,
15
+ HEADING_STYLES,
16
+ COURSE_TITLE_COLORS,
17
+ SERIF_VARIANTS,
18
+ } from "./certificate-variants";
6
19
 
7
- const VARIANT_CLASSES: Record<string, string> = {
8
- classic: "border-[3px] border-double border-warning bg-linear-to-br from-[#fffbe6] to-[#fff8e1]",
9
- modern: "border border-primary bg-linear-to-br from-[#fff5f5] to-[#fee2e2]",
10
- minimal: "border border-border",
11
- };
20
+ // ─── Frame CVAs ────────────────────────────────────────────────
21
+
22
+ const outerFrameVariants = cva("mx-auto max-w-4xl", {
23
+ variants: {
24
+ variant: {
25
+ classic:
26
+ "border-[3px] border-double border-warning/50 rounded-lg p-1.5",
27
+ modern: "rounded-lg",
28
+ elegant:
29
+ "border border-foreground/15 rounded-lg p-2.5 dark:border-foreground/10",
30
+ academic: "border-2 border-info/30 rounded-lg p-1.5",
31
+ minimal: "rounded-lg",
32
+ bold: "rounded-lg",
33
+ },
34
+ },
35
+ defaultVariants: { variant: "classic" },
36
+ });
37
+
38
+ const innerFrameVariants = cva(
39
+ "relative text-center p-8 sm:p-12 md:p-16 overflow-hidden",
40
+ {
41
+ variants: {
42
+ variant: {
43
+ classic: "border border-warning/25 rounded bg-warning/5",
44
+ modern:
45
+ "border border-primary/20 rounded-lg bg-linear-to-br from-primary/5 to-primary/8 border-t-[3px] border-t-primary/60",
46
+ elegant:
47
+ "border border-foreground/8 rounded bg-linear-to-b from-background to-muted/20 dark:border-foreground/5 dark:to-muted/10",
48
+ academic: "border border-info/15 rounded bg-info/3",
49
+ minimal: "border border-border rounded-lg bg-background",
50
+ bold: "border-[3px] border-primary rounded-lg bg-linear-to-br from-primary/8 to-palette-0/8",
51
+ },
52
+ },
53
+ defaultVariants: { variant: "classic" },
54
+ },
55
+ );
56
+
57
+ // ─── Sub-components ─────────────────────────────────────────────
58
+
59
+ function CornerOrnaments({ variant }: { variant: CertificateVariant }) {
60
+ const config = CORNER_CONFIGS[variant];
61
+ if (!config) return null;
62
+
63
+ return (
64
+ <>
65
+ {CORNER_POSITIONS.map(({ key, pos, transform }) => (
66
+ <svg
67
+ key={key}
68
+ className={cn("absolute pointer-events-none", pos, config.color)}
69
+ width={config.size}
70
+ height={config.size}
71
+ viewBox={`0 0 ${config.size} ${config.size}`}
72
+ fill="none"
73
+ stroke="currentColor"
74
+ style={transform ? { transform } : undefined}
75
+ aria-hidden="true"
76
+ >
77
+ {config.paths.map((p, i) => (
78
+ <path key={i} d={p.d} strokeWidth={p.strokeWidth} />
79
+ ))}
80
+ {config.dots?.map((dot, i) => (
81
+ <circle
82
+ key={`d${i}`}
83
+ cx={dot.cx}
84
+ cy={dot.cy}
85
+ r={dot.r}
86
+ fill="currentColor"
87
+ stroke="none"
88
+ />
89
+ ))}
90
+ </svg>
91
+ ))}
92
+ </>
93
+ );
94
+ }
95
+
96
+ function OrnamentalDivider({
97
+ variant,
98
+ className,
99
+ }: {
100
+ variant: CertificateVariant;
101
+ className?: string;
102
+ }) {
103
+ const config = DIVIDER_CONFIGS[variant];
104
+ return (
105
+ <div
106
+ className={cn(
107
+ "flex items-center justify-center gap-3 my-5 mx-auto max-w-70",
108
+ className,
109
+ )}
110
+ role="separator"
111
+ aria-hidden="true"
112
+ >
113
+ <span className={cn("flex-1 h-px", config.lineColor)} />
114
+ {config.motif}
115
+ <span className={cn("flex-1 h-px", config.lineColor)} />
116
+ </div>
117
+ );
118
+ }
119
+
120
+ // ─── Main component ────────────────────────────────────────────
12
121
 
13
122
  export function CertificateViewer({
14
123
  recipientName,
@@ -22,10 +131,13 @@ export function CertificateViewer({
22
131
  showActions = true,
23
132
  onPrint,
24
133
  onDownload,
134
+ isLoading,
135
+ error,
136
+ onRetry,
25
137
  className,
26
138
  style,
27
139
  }: CertificateViewerProps) {
28
- const formattedDate = (() => {
140
+ const formattedDate = useMemo(() => {
29
141
  try {
30
142
  return new Date(completionDate).toLocaleDateString("en-US", {
31
143
  year: "numeric",
@@ -35,7 +147,7 @@ export function CertificateViewer({
35
147
  } catch {
36
148
  return completionDate;
37
149
  }
38
- })();
150
+ }, [completionDate]);
39
151
 
40
152
  function handlePrint() {
41
153
  if (onPrint) {
@@ -45,63 +157,105 @@ export function CertificateViewer({
45
157
  }
46
158
  }
47
159
 
160
+ const glowStyle = BACKGROUND_GLOW[variant];
161
+
48
162
  return (
163
+ <SectionShell
164
+ isLoading={isLoading}
165
+ error={error}
166
+ onRetry={onRetry}
167
+ className={className}
168
+ style={style}
169
+ skeleton={<Skeleton className="h-80 w-full rounded-lg border" />}
170
+ >
49
171
  <div className={className} style={style}>
50
- <div
51
- className={cn(
52
- "p-3 sm:p-5 md:p-6 text-center max-w-200 mx-auto rounded-md",
53
- VARIANT_CLASSES[variant],
54
- )}
55
- >
56
- {/* Logo or icon */}
57
- {organizationLogo ? (
58
- <img
59
- src={organizationLogo}
60
- alt={organizationName}
61
- className="h-15 mb-2 mx-auto block"
62
- />
63
- ) : (
64
- <Award
65
- size={48}
66
- className={cn("mx-auto mb-4", variant === "classic" && "text-warning")}
67
- />
68
- )}
69
-
70
- <p className="uppercase tracking-[3px] text-sm text-foreground/70">
71
- Certificate of Completion
72
- </p>
73
-
74
- <Separator className="my-2 mx-auto max-w-50" />
75
-
76
- <p className="text-sm text-foreground mb-1">This is to certify that</p>
77
- <p className={cn("text-2xl font-bold mb-2 text-foreground", variant === "classic" && "font-serif")}>
78
- {recipientName}
79
- </p>
80
- <p className="text-sm text-foreground mb-1">has successfully completed</p>
81
- <p className="text-xl font-bold mb-2 text-primary">{courseTitle}</p>
82
-
83
- <p className="text-sm text-foreground mb-3">
84
- Issued by {organizationName} on {formattedDate}
85
- </p>
86
-
87
- {signatory && (
88
- <div className="mt-4 mb-2">
89
- <Separator className="my-2 mx-auto max-w-50" />
90
- <p className="font-semibold text-sm text-foreground">{signatory.name}</p>
91
- <p className="text-xs text-muted-foreground">{signatory.title}</p>
92
- </div>
93
- )}
172
+ {/* Outer certificate frame */}
173
+ <div className={outerFrameVariants({ variant })}>
174
+ {/* Inner certificate frame */}
175
+ <div className={innerFrameVariants({ variant })}>
176
+ <CornerOrnaments variant={variant} />
177
+
178
+ {glowStyle && (
179
+ <div
180
+ className="absolute inset-0 pointer-events-none rounded-[inherit]"
181
+ style={glowStyle}
182
+ aria-hidden="true"
183
+ />
184
+ )}
185
+
186
+ <div className="relative">
187
+ {organizationLogo ? (
188
+ <img
189
+ src={organizationLogo}
190
+ alt={organizationName}
191
+ className="h-20 mb-4 mx-auto block"
192
+ />
193
+ ) : (
194
+ <Award
195
+ size={64}
196
+ className={cn("mx-auto mb-6", ICON_COLORS[variant])}
197
+ />
198
+ )}
199
+
200
+ <p className={cn("mb-2", HEADING_STYLES[variant])}>
201
+ Certificate of Completion
202
+ </p>
203
+
204
+ <OrnamentalDivider variant={variant} />
205
+
206
+ <p className="text-base text-foreground/70 mb-2">
207
+ This is to certify that
208
+ </p>
94
209
 
95
- {certificateId && (
96
- <span className="block text-xs text-muted-foreground mt-2">
97
- Certificate ID: {certificateId}
98
- </span>
99
- )}
210
+ <p
211
+ className={cn(
212
+ "text-4xl font-bold mb-4 text-foreground",
213
+ SERIF_VARIANTS.has(variant) && "font-serif",
214
+ )}
215
+ >
216
+ {recipientName}
217
+ </p>
218
+
219
+ <p className="text-base text-foreground/70 mb-2">
220
+ has successfully completed
221
+ </p>
222
+
223
+ <p
224
+ className={cn(
225
+ "text-2xl font-semibold mb-4",
226
+ COURSE_TITLE_COLORS[variant],
227
+ )}
228
+ >
229
+ {courseTitle}
230
+ </p>
231
+
232
+ <p className="text-base text-foreground/60 mb-6">
233
+ Issued by {organizationName} on {formattedDate}
234
+ </p>
235
+
236
+ {signatory && (
237
+ <div className="mt-6 mb-4">
238
+ <OrnamentalDivider variant={variant} />
239
+ <p className="font-semibold text-base text-foreground">
240
+ {signatory.name}
241
+ </p>
242
+ <p className="text-sm text-muted-foreground">
243
+ {signatory.title}
244
+ </p>
245
+ </div>
246
+ )}
247
+
248
+ {certificateId && (
249
+ <span className="block text-xs text-muted-foreground mt-4">
250
+ Certificate ID: {certificateId}
251
+ </span>
252
+ )}
253
+ </div>
254
+ </div>
100
255
  </div>
101
256
 
102
- {/* Actions */}
103
257
  {showActions && (
104
- <div className="flex justify-center gap-2 mt-3">
258
+ <div className="flex justify-center gap-3 mt-6">
105
259
  <Button variant="outline" onClick={handlePrint}>
106
260
  <Printer size={16} /> Print
107
261
  </Button>
@@ -113,5 +267,6 @@ export function CertificateViewer({
113
267
  </div>
114
268
  )}
115
269
  </div>
270
+ </SectionShell>
116
271
  );
117
272
  }
@@ -0,0 +1,170 @@
1
+ import type { CertificateVariant } from "./types";
2
+
3
+ // ─── Corner ornaments ──────────────────────────────────────────
4
+
5
+ export interface CornerConfig {
6
+ size: number;
7
+ paths: { d: string; strokeWidth: number }[];
8
+ dots?: { cx: number; cy: number; r: number }[];
9
+ color: string;
10
+ }
11
+
12
+ export const CORNER_CONFIGS: Record<CertificateVariant, CornerConfig | null> = {
13
+ classic: {
14
+ size: 40,
15
+ paths: [
16
+ { d: "M 2 38 L 2 10 Q 2 2 10 2 L 38 2", strokeWidth: 1.5 },
17
+ { d: "M 7 38 L 7 13 Q 7 7 13 7 L 38 7", strokeWidth: 0.75 },
18
+ ],
19
+ color: "text-warning/50",
20
+ },
21
+ modern: null,
22
+ elegant: {
23
+ size: 32,
24
+ paths: [{ d: "M 0 30 L 0 0 L 30 0", strokeWidth: 0.5 }],
25
+ dots: [{ cx: 2.5, cy: 2.5, r: 1.5 }],
26
+ color: "text-foreground/20",
27
+ },
28
+ academic: {
29
+ size: 36,
30
+ paths: [
31
+ { d: "M 0 36 L 0 0 L 36 0", strokeWidth: 2 },
32
+ { d: "M 5 36 L 5 5 L 36 5", strokeWidth: 1 },
33
+ ],
34
+ color: "text-info/35",
35
+ },
36
+ minimal: null,
37
+ bold: {
38
+ size: 20,
39
+ paths: [],
40
+ dots: [{ cx: 5, cy: 5, r: 5 }],
41
+ color: "text-primary/40",
42
+ },
43
+ };
44
+
45
+ export const CORNER_POSITIONS = [
46
+ { key: "tl", pos: "top-3 left-3", transform: undefined },
47
+ { key: "tr", pos: "top-3 right-3", transform: "scaleX(-1)" },
48
+ { key: "bl", pos: "bottom-3 left-3", transform: "scaleY(-1)" },
49
+ { key: "br", pos: "bottom-3 right-3", transform: "scale(-1)" },
50
+ ] as const;
51
+
52
+ // ─── Divider configs ────────────────────────────────────────────
53
+
54
+ export interface DividerConfig {
55
+ lineColor: string;
56
+ motif: React.ReactNode;
57
+ }
58
+
59
+ export const DIVIDER_CONFIGS: Record<CertificateVariant, DividerConfig> = {
60
+ classic: {
61
+ lineColor: "bg-warning/30",
62
+ motif: (
63
+ <svg width="12" height="12" viewBox="0 0 12 12" className="text-warning/50 shrink-0" aria-hidden="true">
64
+ <path d="M6 0 L12 6 L6 12 L0 6 Z" fill="currentColor" />
65
+ </svg>
66
+ ),
67
+ },
68
+ modern: {
69
+ lineColor: "bg-primary/20",
70
+ motif: (
71
+ <svg width="8" height="8" viewBox="0 0 8 8" className="text-primary/40 shrink-0" aria-hidden="true">
72
+ <circle cx="4" cy="4" r="3" fill="currentColor" />
73
+ </svg>
74
+ ),
75
+ },
76
+ elegant: {
77
+ lineColor: "bg-foreground/10",
78
+ motif: (
79
+ <svg width="10" height="10" viewBox="0 0 10 10" className="text-foreground/15 shrink-0" aria-hidden="true">
80
+ <path d="M5 0 L6 4 L10 5 L6 6 L5 10 L4 6 L0 5 L4 4 Z" fill="currentColor" />
81
+ </svg>
82
+ ),
83
+ },
84
+ academic: {
85
+ lineColor: "bg-info/25",
86
+ motif: (
87
+ <svg width="10" height="10" viewBox="0 0 10 10" className="text-info/40 shrink-0" aria-hidden="true">
88
+ <path d="M4 0 L6 0 L6 4 L10 4 L10 6 L6 6 L6 10 L4 10 L4 6 L0 6 L0 4 L4 4 Z" fill="currentColor" />
89
+ </svg>
90
+ ),
91
+ },
92
+ minimal: {
93
+ lineColor: "bg-border",
94
+ motif: null,
95
+ },
96
+ bold: {
97
+ lineColor: "bg-primary/40",
98
+ motif: (
99
+ <svg width="10" height="10" viewBox="0 0 10 10" className="text-primary/50 shrink-0" aria-hidden="true">
100
+ <rect x="1" y="1" width="8" height="8" fill="currentColor" />
101
+ </svg>
102
+ ),
103
+ },
104
+ };
105
+
106
+ // ─── Background glow ────────────────────────────────────────────
107
+
108
+ export const BACKGROUND_GLOW: Record<CertificateVariant, React.CSSProperties | null> = {
109
+ classic: {
110
+ background:
111
+ "radial-gradient(ellipse at center, var(--color-warning) 0%, transparent 65%)",
112
+ opacity: 0.06,
113
+ },
114
+ modern: null,
115
+ elegant: {
116
+ background:
117
+ "radial-gradient(ellipse at center, var(--color-foreground) 0%, transparent 70%)",
118
+ opacity: 0.03,
119
+ },
120
+ academic: {
121
+ background:
122
+ "radial-gradient(ellipse at center, var(--color-info) 0%, transparent 65%)",
123
+ opacity: 0.05,
124
+ },
125
+ minimal: null,
126
+ bold: {
127
+ background:
128
+ "radial-gradient(ellipse at 30% 50%, var(--color-primary) 0%, transparent 50%), radial-gradient(ellipse at 70% 50%, var(--color-palette-0) 0%, transparent 50%)",
129
+ opacity: 0.06,
130
+ },
131
+ };
132
+
133
+ // ─── Typography maps ────────────────────────────────────────────
134
+
135
+ export const ICON_COLORS: Record<CertificateVariant, string> = {
136
+ classic: "text-warning",
137
+ modern: "text-primary",
138
+ elegant: "text-foreground/60",
139
+ academic: "text-info",
140
+ minimal: "text-muted-foreground",
141
+ bold: "text-primary",
142
+ };
143
+
144
+ export const HEADING_STYLES: Record<CertificateVariant, string> = {
145
+ classic:
146
+ "uppercase tracking-[4px] text-base font-medium text-foreground/80 font-serif",
147
+ modern: "uppercase tracking-[3px] text-base font-medium text-primary/80",
148
+ elegant:
149
+ "uppercase tracking-[5px] text-sm font-light text-foreground/50",
150
+ academic:
151
+ "uppercase tracking-[3px] text-base font-semibold text-info/80",
152
+ minimal:
153
+ "uppercase tracking-[2px] text-sm font-normal text-muted-foreground",
154
+ bold: "uppercase tracking-[3px] text-lg font-black text-primary",
155
+ };
156
+
157
+ export const COURSE_TITLE_COLORS: Record<CertificateVariant, string> = {
158
+ classic: "text-warning",
159
+ modern: "text-primary",
160
+ elegant: "text-foreground/80",
161
+ academic: "text-info",
162
+ minimal: "text-foreground",
163
+ bold: "text-primary",
164
+ };
165
+
166
+ export const SERIF_VARIANTS: ReadonlySet<CertificateVariant> = new Set([
167
+ "classic",
168
+ "elegant",
169
+ "academic",
170
+ ]);
@@ -1,10 +1,18 @@
1
+ /** Available certificate visual styles */
2
+ export type CertificateVariant =
3
+ | "classic"
4
+ | "modern"
5
+ | "elegant"
6
+ | "academic"
7
+ | "minimal"
8
+ | "bold";
1
9
 
2
10
  /**
3
11
  * CertificateViewer section — a printable completion certificate.
4
12
  *
5
13
  * Displays a certificate with recipient details, course information,
6
- * signatory, and verification ID. Supports three visual variants:
7
- * classic, modern, and minimal.
14
+ * signatory, and verification ID. Supports six visual variants:
15
+ * classic, modern, elegant, academic, minimal, and bold.
8
16
  *
9
17
  * @example
10
18
  * <CertificateViewer
@@ -12,7 +20,7 @@
12
20
  * courseTitle="Advanced React"
13
21
  * completionDate="2025-03-01"
14
22
  * organizationName="HydraLMS Academy"
15
- * variant="modern"
23
+ * variant="elegant"
16
24
  * />
17
25
  */
18
26
  export interface CertificateViewerProps {
@@ -30,14 +38,20 @@ export interface CertificateViewerProps {
30
38
  signatory?: { name: string; title: string };
31
39
  /** Unique certificate ID */
32
40
  certificateId?: string;
33
- /** Certificate template variant */
34
- variant?: "classic" | "modern" | "minimal";
41
+ /** Certificate template variant @default "classic" */
42
+ variant?: CertificateVariant;
35
43
  /** Whether to show print/download actions */
36
44
  showActions?: boolean;
37
45
  /** Called when print is triggered */
38
46
  onPrint?: () => void;
39
47
  /** Called when download is triggered */
40
48
  onDownload?: () => void;
49
+ /** Render skeleton placeholders instead of content */
50
+ isLoading?: boolean;
51
+ /** Error message — renders an error state with optional retry */
52
+ error?: string | null;
53
+ /** Called when the user clicks retry in the error state */
54
+ onRetry?: () => void;
41
55
  /** CSS class name for the root element */
42
56
  className?: string;
43
57
  /** Inline styles for the root element */