@tinyweb_dev/oe-exam-sdk 0.1.27 → 0.1.29

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 (809) hide show
  1. package/README.md +79 -6
  2. package/dist/api/contest-rooms.js +54 -1
  3. package/dist/api/contest-rooms.types.js +1 -1
  4. package/dist/api/exam-create.js +179 -1
  5. package/dist/api/exam-create.types.js +1 -1
  6. package/dist/api/exam-entry.js +68 -1
  7. package/dist/api/exam-entry.types.js +1 -1
  8. package/dist/api/exam-questions.js +149 -1
  9. package/dist/api/exam-questions.types.js +1 -1
  10. package/dist/api/exam-taking.js +84 -1
  11. package/dist/api/exam-taking.types.d.ts +7 -0
  12. package/dist/api/exam-taking.types.js +1 -1
  13. package/dist/api/index.js +8 -1
  14. package/dist/api/result-review.js +62 -1
  15. package/dist/api/result-review.types.js +16 -1
  16. package/dist/components/CueCardEditor.js +58 -1
  17. package/dist/components/common/ResolvedImage.js +15 -1
  18. package/dist/components/exams/CompletionDialog.js +8 -1
  19. package/dist/components/exams/CreateExamInfoForm.js +84 -1
  20. package/dist/components/exams/CreateExamPageContainer.js +110 -1
  21. package/dist/components/exams/ExamCreator.js +287 -1
  22. package/dist/components/exams/ExamPartTabs.js +138 -1
  23. package/dist/components/exams/ExamPreviewDialog.d.ts +6 -7
  24. package/dist/components/exams/ExamPreviewDialog.js +121 -1
  25. package/dist/components/exams/ExamPreviewPartContent.d.ts +18 -0
  26. package/dist/components/exams/ExamPreviewPartContent.js +27 -0
  27. package/dist/components/exams/ExamPreviewSidebar.d.ts +17 -0
  28. package/dist/components/exams/ExamPreviewSidebar.js +37 -0
  29. package/dist/components/exams/ExamQuestionGrid.js +37 -1
  30. package/dist/components/exams/ExamQuestionsPageContainer.js +112 -1
  31. package/dist/components/exams/ExamViewDialog.js +61 -1
  32. package/dist/components/exams/PartAudioUploader.js +9 -1
  33. package/dist/components/exams/TemplateSelector.js +76 -1
  34. package/dist/components/exams/UnsavedChangesDialog.js +7 -1
  35. package/dist/components/exams/api.js +3 -1
  36. package/dist/components/exams/create-exam.texts.js +1 -1
  37. package/dist/components/exams/create-exam.validation.js +18 -1
  38. package/dist/components/exams/entry/EntryRestartDialog.js +11 -1
  39. package/dist/components/exams/entry/StudentExamEntryPageContainer.js +358 -1
  40. package/dist/components/exams/entry/exam-entry.states.js +26 -1
  41. package/dist/components/exams/entry/exam-entry.utils.js +55 -1
  42. package/dist/components/exams/entry/fullscreen-loading.js +18 -1
  43. package/dist/components/exams/entry/index.js +4 -1
  44. package/dist/components/exams/exam-preview.types.d.ts +30 -0
  45. package/dist/components/exams/exam-preview.types.js +62 -0
  46. package/dist/components/exams/exam-question-status.js +45 -1
  47. package/dist/components/exams/exam-template.utils.js +18 -1
  48. package/dist/components/exams/index.d.ts +1 -0
  49. package/dist/components/exams/index.js +19 -1
  50. package/dist/components/exams/rooms/StudentContestRoomsPageContainer.js +183 -1
  51. package/dist/components/exams/rooms/contest-rooms.utils.js +93 -1
  52. package/dist/components/exams/rooms/index.js +2 -1
  53. package/dist/components/exams/take/ExamTakingApiContext.js +25 -1
  54. package/dist/components/exams/take/ExamTakingPageContainer.js +955 -1
  55. package/dist/components/exams/take/GenericQuestionRenderer.js +14 -1
  56. package/dist/components/exams/take/components/QuestionRenderer.js +207 -1
  57. package/dist/components/exams/take/components/index.js +11 -1
  58. package/dist/components/exams/take/components/question-renderers/ArticlesViewer.js +8 -1
  59. package/dist/components/exams/take/components/question-renderers/MoversAnswerTheQuestionRenderer.js +30 -1
  60. package/dist/components/exams/take/components/question-renderers/MoversChooseAdjectiveRenderer.js +63 -1
  61. package/dist/components/exams/take/components/question-renderers/MoversChooseBestAnswerRenderer.js +136 -1
  62. package/dist/components/exams/take/components/question-renderers/MoversColoringRenderer.js +379 -1
  63. package/dist/components/exams/take/components/question-renderers/MoversCrossOutWordGroupRenderer.d.ts +20 -0
  64. package/dist/components/exams/take/components/question-renderers/MoversCrossOutWordGroupRenderer.js +141 -0
  65. package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.js +131 -1
  66. package/dist/components/exams/take/components/question-renderers/MoversGridFillRenderer.js +76 -1
  67. package/dist/components/exams/take/components/question-renderers/MoversLabelThePictureRenderer.js +68 -1
  68. package/dist/components/exams/take/components/question-renderers/MoversListenAndWriteRenderer.js +42 -1
  69. package/dist/components/exams/take/components/question-renderers/MoversMatchByWritingAnswerRenderer.js +131 -1
  70. package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.js +76 -1
  71. package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.js +68 -1
  72. package/dist/components/exams/take/components/question-renderers/MoversReadingPassageRenderer.js +62 -1
  73. package/dist/components/exams/take/components/question-renderers/MoversTrueFalseRenderer.d.ts +1 -1
  74. package/dist/components/exams/take/components/question-renderers/MoversTrueFalseRenderer.js +51 -1
  75. package/dist/components/exams/take/components/question-renderers/MoversWordFillParagraphRenderer.js +171 -1
  76. package/dist/components/exams/take/components/question-renderers/MoversWordFillStructuredFormRenderer.js +198 -1
  77. package/dist/components/exams/take/components/question-renderers/MoversWordOrderAndMatchGroupRenderer.d.ts +15 -0
  78. package/dist/components/exams/take/components/question-renderers/MoversWordOrderAndMatchGroupRenderer.js +36 -0
  79. package/dist/components/exams/take/components/question-renderers/MoversWordOrderingRenderer.js +92 -1
  80. package/dist/components/exams/take/components/question-renderers/MoversWriteSentencesRenderer.js +11 -1
  81. package/dist/components/exams/take/components/question-renderers/MoversWritingRenderer.js +56 -1
  82. package/dist/components/exams/take/components/question-renderers/index.d.ts +2 -0
  83. package/dist/components/exams/take/components/question-renderers/index.js +25 -1
  84. package/dist/components/exams/take/components/speaking/SpeakingAudioPlayer.js +85 -1
  85. package/dist/components/exams/take/components/speaking/SpeakingContentFrame.js +10 -1
  86. package/dist/components/exams/take/components/speaking/SpeakingExamLayout.js +15 -1
  87. package/dist/components/exams/take/components/speaking/SpeakingHeader.js +6 -1
  88. package/dist/components/exams/take/components/speaking/SpeakingInstructionBar.js +8 -1
  89. package/dist/components/exams/take/components/speaking/SpeakingMicButton.js +59 -1
  90. package/dist/components/exams/take/components/speaking/SpeakingNavButton.js +7 -1
  91. package/dist/components/exams/take/components/speaking/SpeakingPartBadge.js +5 -1
  92. package/dist/components/exams/take/components/speaking/SpeakingReadDisplayedContent.module.css +120 -0
  93. package/dist/components/exams/take/components/speaking/SpeakingRecordingPreview.js +73 -1
  94. package/dist/components/exams/take/components/speaking/SpeakingReportErrorButton.js +8 -1
  95. package/dist/components/exams/take/components/speaking/SpeakingSpontaneousQA.module.css +344 -0
  96. package/dist/components/exams/take/components/speaking/SpeakingVideoIntro.js +32 -1
  97. package/dist/components/exams/take/components/speaking/index.js +1 -1
  98. package/dist/components/exams/take/components/speaking/renderers/SpeakingChooseAnswerGroupRenderer.js +63 -1
  99. package/dist/components/exams/take/components/speaking/renderers/SpeakingChooseObjectsRenderer.js +95 -1
  100. package/dist/components/exams/take/components/speaking/renderers/SpeakingCompareImagesRenderer.js +35 -1
  101. package/dist/components/exams/take/components/speaking/renderers/SpeakingConversationRenderer.js +147 -1
  102. package/dist/components/exams/take/components/speaking/renderers/SpeakingCueCardRenderer.js +232 -1
  103. package/dist/components/exams/take/components/speaking/renderers/SpeakingDescribeImageRenderer.js +193 -1
  104. package/dist/components/exams/take/components/speaking/renderers/SpeakingDragObjectsRenderer.js +155 -1
  105. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.js +212 -1
  106. package/dist/components/exams/take/components/speaking/renderers/SpeakingInfoExchangeRenderer.js +77 -1
  107. package/dist/components/exams/take/components/speaking/renderers/SpeakingListenAndSpeakAnswerRenderer.js +64 -1
  108. package/dist/components/exams/take/components/speaking/renderers/SpeakingOddOneOutRenderer.js +69 -1
  109. package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionListRenderer.js +64 -1
  110. package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionRenderer.js +92 -1
  111. package/dist/components/exams/take/components/speaking/renderers/SpeakingReadDisplayedContentRenderer.js +202 -1
  112. package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQARenderer.js +339 -1
  113. package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQaGroupRenderer.d.ts +19 -0
  114. package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQaGroupRenderer.js +76 -0
  115. package/dist/components/exams/take/components/speaking/renderers/SpeakingStoryImagesRenderer.js +56 -1
  116. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQACambridgeLayout.d.ts +24 -0
  117. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQACambridgeLayout.js +120 -0
  118. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQAInlineAudioPlayer.d.ts +8 -0
  119. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQAInlineAudioPlayer.js +94 -0
  120. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQASummerSkyAudioPlayer.d.ts +8 -0
  121. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQASummerSkyAudioPlayer.js +101 -0
  122. package/dist/components/exams/take/components/speaking/renderers/index.d.ts +1 -0
  123. package/dist/components/exams/take/components/speaking/renderers/index.js +14 -1
  124. package/dist/components/exams/take/exam-taking.utils.d.ts +13 -0
  125. package/dist/components/exams/take/exam-taking.utils.js +203 -1
  126. package/dist/components/exams/take/hooks/index.js +2 -1
  127. package/dist/components/exams/take/hooks/useAnswerAutosave.js +71 -1
  128. package/dist/components/exams/take/hooks/useAttemptSubmit.js +38 -1
  129. package/dist/components/exams/take/hooks/useAutoSave.js +83 -1
  130. package/dist/components/exams/take/hooks/useExamCountdown.js +35 -1
  131. package/dist/components/exams/take/hooks/useExamTimer.js +97 -1
  132. package/dist/components/exams/take/hooks/useProctoringCamera.js +118 -1
  133. package/dist/components/exams/take/hooks/useProctoringSnapshot.js +76 -1
  134. package/dist/components/exams/take/hooks/useSpeakingConversationAudio.js +178 -1
  135. package/dist/components/exams/take/hooks/useSpeakingNavigation.js +51 -1
  136. package/dist/components/exams/take/hooks/useSpeakingRecorder.js +406 -1
  137. package/dist/components/exams/take/hooks/useSpeakingRendererSetup.js +136 -1
  138. package/dist/components/exams/take/hooks/useSpontaneousQaGroupRecorder.d.ts +11 -0
  139. package/dist/components/exams/take/hooks/useSpontaneousQaGroupRecorder.js +352 -0
  140. package/dist/components/exams/take/hooks/useSpontaneousQaGroupRendererSetup.d.ts +42 -0
  141. package/dist/components/exams/take/hooks/useSpontaneousQaGroupRendererSetup.js +166 -0
  142. package/dist/components/exams/take/hooks/useToeicAudio.js +35 -1
  143. package/dist/components/exams/take/index.js +6 -1
  144. package/dist/components/exams/take/types/speaking-cue-card.types.js +1 -1
  145. package/dist/components/exams/take/types/speaking.types.js +7 -1
  146. package/dist/components/exams/take/types.js +4 -1
  147. package/dist/components/exams/take/utils/answer-transformers.js +452 -1
  148. package/dist/components/exams/take/utils/cambridge-yle-inline-speaking.d.ts +21 -0
  149. package/dist/components/exams/take/utils/cambridge-yle-inline-speaking.js +44 -0
  150. package/dist/components/exams/take/utils/coloring-assignments.js +40 -1
  151. package/dist/components/exams/take/utils/cross-out-word-group.transformer.d.ts +21 -0
  152. package/dist/components/exams/take/utils/cross-out-word-group.transformer.js +83 -0
  153. package/dist/components/exams/take/utils/question-transformers.d.ts +43 -2
  154. package/dist/components/exams/take/utils/question-transformers.js +1137 -1
  155. package/dist/components/features/exam-entry/entry-state-cards.js +19 -1
  156. package/dist/components/features/exam-entry/index.js +2 -1
  157. package/dist/components/features/exam-entry/themed/CambridgeYleWaitingRoom.js +121 -1
  158. package/dist/components/features/exam-entry/themed/ThemedWaitingRoom.js +37 -1
  159. package/dist/components/features/exam-entry/themed/ToeicWaitingRoom.js +15 -1
  160. package/dist/components/features/exam-entry/themed/cambridge-yle-waiting-parts.js +80 -1
  161. package/dist/components/features/exam-entry/themed/components/CloseButton.js +6 -1
  162. package/dist/components/features/exam-entry/themed/components/ExamInfoPanel.js +17 -1
  163. package/dist/components/features/exam-entry/themed/components/ModeSelector.js +6 -1
  164. package/dist/components/features/exam-entry/themed/components/StartButton.js +12 -1
  165. package/dist/components/features/exam-entry/themed/components/index.js +4 -1
  166. package/dist/components/features/exam-entry/themed/index.js +3 -1
  167. package/dist/components/index.d.ts +1 -0
  168. package/dist/components/index.js +43 -1
  169. package/dist/components/questions/_shared/config/question-types.config.js +362 -1
  170. package/dist/components/questions/_shared/constants/color-palette.js +21 -1
  171. package/dist/components/questions/_shared/hooks/useAnswerGroupQuestionGroup.js +105 -1
  172. package/dist/components/questions/_shared/hooks/useBasicQuestionGroup.js +222 -1
  173. package/dist/components/questions/_shared/hooks/useCompareImagesGroup.js +140 -1
  174. package/dist/components/questions/_shared/hooks/useImageQuestionGroup.js +123 -1
  175. package/dist/components/questions/_shared/hooks/useInfoExchangeGroup.js +50 -1
  176. package/dist/components/questions/_shared/hooks/useQuestionFormState.js +54 -1
  177. package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +123 -1
  178. package/dist/components/questions/_shared/hooks/useQuestionListGroup.js +121 -1
  179. package/dist/components/questions/_shared/hooks/useQuestionOnChange.js +70 -1
  180. package/dist/components/questions/_shared/hooks/useSceneQuestionGroup.js +194 -1
  181. package/dist/components/questions/_shared/index.js +7 -1
  182. package/dist/components/questions/_shared/types/answer-the-question.type.js +1 -1
  183. package/dist/components/questions/_shared/types/arrange-letters-into-words.type.js +1 -1
  184. package/dist/components/questions/_shared/types/choose-correct-adjective.type.js +3 -1
  185. package/dist/components/questions/_shared/types/choose-the-correct-answer-group.type.d.ts +1 -0
  186. package/dist/components/questions/_shared/types/choose-the-correct-answer-group.type.js +1 -1
  187. package/dist/components/questions/_shared/types/color-region.config.js +1 -1
  188. package/dist/components/questions/_shared/types/cross-out-word-group.type.d.ts +2 -0
  189. package/dist/components/questions/_shared/types/cross-out-word-group.type.js +1 -1
  190. package/dist/components/questions/_shared/types/fill-in-blank.type.js +1 -1
  191. package/dist/components/questions/_shared/types/fill-missing-words-in-grid.type.js +1 -1
  192. package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +1 -1
  193. package/dist/components/questions/_shared/types/index.d.ts +1 -0
  194. package/dist/components/questions/_shared/types/index.js +14 -1
  195. package/dist/components/questions/_shared/types/label-the-picture.type.js +1 -1
  196. package/dist/components/questions/_shared/types/listen-and-choose-from-answer-group.type.js +1 -1
  197. package/dist/components/questions/_shared/types/listen-and-choose-objects-in-scene.type.js +1 -1
  198. package/dist/components/questions/_shared/types/listen-and-drag-objects-into-scene.type.js +1 -1
  199. package/dist/components/questions/_shared/types/listen-and-speak-answer.type.js +1 -1
  200. package/dist/components/questions/_shared/types/listen-and-speak-compare-images.type.js +1 -1
  201. package/dist/components/questions/_shared/types/listen-and-speak-image-group.type.js +1 -1
  202. package/dist/components/questions/_shared/types/listen-and-speak-info-exchange.type.js +1 -1
  203. package/dist/components/questions/_shared/types/listen-and-speak-odd-one-out.type.js +3 -1
  204. package/dist/components/questions/_shared/types/listen-and-speak-question-list.type.js +1 -1
  205. package/dist/components/questions/_shared/types/listen-and-speak-with-story-images.type.js +3 -1
  206. package/dist/components/questions/_shared/types/listen-and-tick-answer.type.js +1 -1
  207. package/dist/components/questions/_shared/types/listening-drawline.type.js +1 -1
  208. package/dist/components/questions/_shared/types/look-and-choose-answer.type.js +1 -1
  209. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +1 -1
  210. package/dist/components/questions/_shared/types/match-by-writing-answer.type.js +1 -1
  211. package/dist/components/questions/_shared/types/multiple-choice.type.d.ts +5 -0
  212. package/dist/components/questions/_shared/types/multiple-choice.type.js +1 -1
  213. package/dist/components/questions/_shared/types/question-group.type.js +11 -1
  214. package/dist/components/questions/_shared/types/read-and-color-objects.type.js +1 -1
  215. package/dist/components/questions/_shared/types/read-displayed-content.type.js +1 -1
  216. package/dist/components/questions/_shared/types/read-passage-and-answer-questions.type.js +1 -1
  217. package/dist/components/questions/_shared/types/speaking-describe-image.type.js +1 -1
  218. package/dist/components/questions/_shared/types/spontaneous-qa-group.type.d.ts +37 -0
  219. package/dist/components/questions/_shared/types/spontaneous-qa-group.type.js +1 -0
  220. package/dist/components/questions/_shared/types/spontaneous-qa.type.js +1 -1
  221. package/dist/components/questions/_shared/types/true-false-group.type.js +1 -1
  222. package/dist/components/questions/_shared/types/word-fill-paragraph-with-options.type.js +1 -1
  223. package/dist/components/questions/_shared/types/word-fill-paragraph.type.js +1 -1
  224. package/dist/components/questions/_shared/types/word-fill-structured-form.type.d.ts +20 -11
  225. package/dist/components/questions/_shared/types/word-fill-structured-form.type.js +1 -1
  226. package/dist/components/questions/_shared/types/word-order-and-match-group.type.d.ts +38 -0
  227. package/dist/components/questions/_shared/types/word-order-and-match-group.type.js +1 -0
  228. package/dist/components/questions/_shared/types/word-ordering.type.js +1 -1
  229. package/dist/components/questions/_shared/types/write-a-short-letter.type.js +1 -1
  230. package/dist/components/questions/_shared/types/write-correct-verb-form.type.js +3 -1
  231. package/dist/components/questions/_shared/types/write-sentences.type.js +1 -1
  232. package/dist/components/questions/_shared/types/write-short-paragraph.type.js +1 -1
  233. package/dist/components/questions/_shared/utils/answer-builder.js +79 -1
  234. package/dist/components/questions/_shared/utils/createColorRegionRegistration.js +39 -1
  235. package/dist/components/questions/_shared/utils/id-generator.js +3 -1
  236. package/dist/components/questions/_shared/utils/parseFillBlankStem.d.ts +30 -0
  237. package/dist/components/questions/_shared/utils/parseFillBlankStem.js +69 -0
  238. package/dist/components/questions/_shared/utils/question-validation.js +21 -1
  239. package/dist/components/questions/components/ColorNode.js +85 -1
  240. package/dist/components/questions/components/ColorRegionClient.js +545 -1
  241. package/dist/components/questions/components/ColorRegionCreator.js +420 -1
  242. package/dist/components/questions/components/ColorRegionNode.js +59 -1
  243. package/dist/components/questions/components/CreatorGuide.js +103 -1
  244. package/dist/components/questions/components/NameNode.js +26 -1
  245. package/dist/components/questions/components/QuestionBankOption.js +46 -1
  246. package/dist/components/questions/components/QuestionBasicInfoForm.js +9 -1
  247. package/dist/components/questions/components/QuestionCommonFields.js +33 -1
  248. package/dist/components/questions/components/QuestionSearchDropdown.js +258 -1
  249. package/dist/components/questions/components/QuestionTypeSelector.js +32 -1
  250. package/dist/components/questions/components/QuestionTypeSpecificForms.js +6 -1
  251. package/dist/components/questions/components/RegionNode.js +94 -1
  252. package/dist/components/questions/components/index.js +14 -1
  253. package/dist/components/questions/creator/QuestionCreator.js +90 -1
  254. package/dist/components/questions/creator/QuestionGroupCreator.js +119 -1
  255. package/dist/components/questions/creator/dedicated-component-router.js +79 -1
  256. package/dist/components/questions/creator/index.js +3 -1
  257. package/dist/components/questions/creator/question-type-registry.js +90 -1
  258. package/dist/components/questions/groups/ArticlesGroupCard.js +121 -1
  259. package/dist/components/questions/groups/BasicQuestionGroupCard.js +50 -1
  260. package/dist/components/questions/groups/DocumentGroupCard.js +114 -1
  261. package/dist/components/questions/groups/MatchAnswerGroupCard.js +25 -1
  262. package/dist/components/questions/index.d.ts +1 -0
  263. package/dist/components/questions/index.js +11 -1
  264. package/dist/components/questions/question-bank/EditQuestionBankDialog.d.ts +2 -0
  265. package/dist/components/questions/question-bank/EditQuestionBankDialog.js +114 -0
  266. package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +23 -0
  267. package/dist/components/questions/question-bank/QuestionEditorRenderer.js +75 -0
  268. package/dist/components/questions/question-bank/difficulty.d.ts +15 -0
  269. package/dist/components/questions/question-bank/difficulty.js +33 -0
  270. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.d.ts +5 -0
  271. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.js +101 -0
  272. package/dist/components/questions/question-bank/editor-adapters/index.d.ts +5 -0
  273. package/dist/components/questions/question-bank/editor-adapters/index.js +4 -0
  274. package/dist/components/questions/question-bank/editor-adapters/media-adapters.d.ts +6 -0
  275. package/dist/components/questions/question-bank/editor-adapters/media-adapters.js +140 -0
  276. package/dist/components/questions/question-bank/editor-adapters/types.d.ts +1 -0
  277. package/dist/components/questions/question-bank/editor-adapters/types.js +1 -0
  278. package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.d.ts +8 -0
  279. package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.js +6 -0
  280. package/dist/components/questions/question-bank/editor-adapters/writing-adapters.d.ts +5 -0
  281. package/dist/components/questions/question-bank/editor-adapters/writing-adapters.js +114 -0
  282. package/dist/components/questions/question-bank/index.d.ts +4 -0
  283. package/dist/components/questions/question-bank/index.js +3 -0
  284. package/dist/components/questions/question-bank/types.d.ts +59 -0
  285. package/dist/components/questions/question-bank/types.js +1 -0
  286. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +37 -1
  287. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +351 -1
  288. package/dist/components/questions/types/answer-the-question/register.js +67 -1
  289. package/dist/components/questions/types/answer-the-question/transform.js +36 -1
  290. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +141 -1
  291. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +118 -1
  292. package/dist/components/questions/types/arrange-letters-into-words/index.js +2 -1
  293. package/dist/components/questions/types/arrange-letters-into-words/register.js +4 -1
  294. package/dist/components/questions/types/arrange-letters-into-words/transform.js +24 -1
  295. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +74 -1
  296. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +169 -1
  297. package/dist/components/questions/types/choose-correct-adjective/index.js +2 -1
  298. package/dist/components/questions/types/choose-correct-adjective/register.js +55 -1
  299. package/dist/components/questions/types/choose-correct-adjective/transform.js +16 -1
  300. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +149 -1
  301. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +568 -1
  302. package/dist/components/questions/types/choose-the-correct-answer/index.js +3 -1
  303. package/dist/components/questions/types/choose-the-correct-answer/normalize.d.ts +9 -0
  304. package/dist/components/questions/types/choose-the-correct-answer/normalize.js +88 -1
  305. package/dist/components/questions/types/choose-the-correct-answer/register.js +99 -1
  306. package/dist/components/questions/types/choose-the-correct-answer/transform.js +70 -1
  307. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +22 -1
  308. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupCreator.js +163 -1
  309. package/dist/components/questions/types/choose-the-correct-answer-group/index.d.ts +1 -0
  310. package/dist/components/questions/types/choose-the-correct-answer-group/index.js +3 -1
  311. package/dist/components/questions/types/choose-the-correct-answer-group/map-choose-the-correct-answer-group-data.d.ts +4 -0
  312. package/dist/components/questions/types/choose-the-correct-answer-group/map-choose-the-correct-answer-group-data.js +94 -0
  313. package/dist/components/questions/types/choose-the-correct-answer-group/register.js +36 -1
  314. package/dist/components/questions/types/choose-the-correct-answer-group/transform.js +55 -1
  315. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +27 -1
  316. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +231 -1
  317. package/dist/components/questions/types/cross-out-word-group/index.d.ts +1 -0
  318. package/dist/components/questions/types/cross-out-word-group/index.js +5 -1
  319. package/dist/components/questions/types/cross-out-word-group/map-cross-out-word-group-data.d.ts +6 -0
  320. package/dist/components/questions/types/cross-out-word-group/map-cross-out-word-group-data.js +110 -0
  321. package/dist/components/questions/types/cross-out-word-group/register.js +36 -1
  322. package/dist/components/questions/types/cross-out-word-group/transform.js +67 -1
  323. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +275 -1
  324. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +464 -1
  325. package/dist/components/questions/types/fill-in-blank/index.js +2 -1
  326. package/dist/components/questions/types/fill-in-blank/register.js +148 -1
  327. package/dist/components/questions/types/fill-in-blank/transform.js +88 -1
  328. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +117 -1
  329. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +671 -1
  330. package/dist/components/questions/types/fill-missing-words-in-grid/index.js +2 -1
  331. package/dist/components/questions/types/fill-missing-words-in-grid/register.js +37 -1
  332. package/dist/components/questions/types/fill-missing-words-in-grid/transform.js +15 -1
  333. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +262 -1
  334. package/dist/components/questions/types/gn-speaking-interview/register.js +121 -1
  335. package/dist/components/questions/types/gn-speaking-interview/transform.js +48 -1
  336. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingClient.js +16 -1
  337. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingCreator.js +27 -1
  338. package/dist/components/questions/types/image-object-matching/index.js +2 -1
  339. package/dist/components/questions/types/image-object-matching/register.js +3 -1
  340. package/dist/components/questions/types/image-object-matching/transform.js +2 -1
  341. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +102 -1
  342. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +344 -1
  343. package/dist/components/questions/types/label-the-picture/index.js +2 -1
  344. package/dist/components/questions/types/label-the-picture/register.js +54 -1
  345. package/dist/components/questions/types/label-the-picture/transform.js +28 -1
  346. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +78 -1
  347. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +232 -1
  348. package/dist/components/questions/types/listen-and-choose-from-answer-group/index.js +2 -1
  349. package/dist/components/questions/types/listen-and-choose-from-answer-group/register.js +82 -1
  350. package/dist/components/questions/types/listen-and-choose-from-answer-group/transform.js +27 -1
  351. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +70 -1
  352. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +386 -1
  353. package/dist/components/questions/types/listen-and-choose-objects-in-scene/index.js +2 -1
  354. package/dist/components/questions/types/listen-and-choose-objects-in-scene/register.js +100 -1
  355. package/dist/components/questions/types/listen-and-choose-objects-in-scene/transform.js +33 -1
  356. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +106 -1
  357. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +457 -1
  358. package/dist/components/questions/types/listen-and-drag-objects-into-scene/index.js +2 -1
  359. package/dist/components/questions/types/listen-and-drag-objects-into-scene/register.js +110 -1
  360. package/dist/components/questions/types/listen-and-drag-objects-into-scene/transform.js +35 -1
  361. package/dist/components/questions/types/listen-and-speak-answer/ArrowIndicator.js +8 -1
  362. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +22 -1
  363. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +291 -1
  364. package/dist/components/questions/types/listen-and-speak-answer/index.js +2 -1
  365. package/dist/components/questions/types/listen-and-speak-answer/register.js +84 -1
  366. package/dist/components/questions/types/listen-and-speak-answer/transform.js +27 -1
  367. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +32 -1
  368. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +295 -1
  369. package/dist/components/questions/types/listen-and-speak-compare-images/register.js +79 -1
  370. package/dist/components/questions/types/listen-and-speak-compare-images/transform.js +27 -1
  371. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +34 -1
  372. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +285 -1
  373. package/dist/components/questions/types/listen-and-speak-image-group/register.js +81 -1
  374. package/dist/components/questions/types/listen-and-speak-image-group/transform.js +26 -1
  375. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +38 -1
  376. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +385 -1
  377. package/dist/components/questions/types/listen-and-speak-info-exchange/register.js +87 -1
  378. package/dist/components/questions/types/listen-and-speak-info-exchange/transform.js +32 -1
  379. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +26 -1
  380. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +247 -1
  381. package/dist/components/questions/types/listen-and-speak-odd-one-out/register.js +59 -1
  382. package/dist/components/questions/types/listen-and-speak-odd-one-out/transform.js +18 -1
  383. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +26 -1
  384. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +256 -1
  385. package/dist/components/questions/types/listen-and-speak-question-list/register.js +81 -1
  386. package/dist/components/questions/types/listen-and-speak-question-list/transform.js +26 -1
  387. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +26 -1
  388. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +230 -1
  389. package/dist/components/questions/types/listen-and-speak-with-story-images/register.js +56 -1
  390. package/dist/components/questions/types/listen-and-speak-with-story-images/transform.js +16 -1
  391. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +72 -1
  392. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +230 -1
  393. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +1 -1
  394. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +66 -1
  395. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +42 -1
  396. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +96 -1
  397. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +238 -1
  398. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +2 -1
  399. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +76 -1
  400. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +19 -1
  401. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +134 -1
  402. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +617 -1
  403. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +2 -1
  404. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +82 -1
  405. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +29 -1
  406. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +38 -1
  407. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +359 -1
  408. package/dist/components/questions/types/match-by-writing-answer/index.js +3 -1
  409. package/dist/components/questions/types/match-by-writing-answer/register.js +58 -1
  410. package/dist/components/questions/types/match-by-writing-answer/transform.js +61 -1
  411. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsClient.js +16 -1
  412. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsCreator.js +23 -1
  413. package/dist/components/questions/types/read-and-color-objects/index.js +2 -1
  414. package/dist/components/questions/types/read-and-color-objects/register.js +3 -1
  415. package/dist/components/questions/types/read-and-color-objects/transform.js +36 -1
  416. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +125 -1
  417. package/dist/components/questions/types/read-displayed-content/register.js +64 -1
  418. package/dist/components/questions/types/read-displayed-content/transform.js +12 -1
  419. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +65 -1
  420. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +511 -1
  421. package/dist/components/questions/types/read-passage-and-answer-questions/index.js +2 -1
  422. package/dist/components/questions/types/read-passage-and-answer-questions/register.js +58 -1
  423. package/dist/components/questions/types/read-passage-and-answer-questions/transform.js +28 -1
  424. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +373 -1
  425. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +41 -1
  426. package/dist/components/questions/types/speaking-conversation/register.js +110 -1
  427. package/dist/components/questions/types/speaking-conversation/transform.js +34 -1
  428. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +74 -1
  429. package/dist/components/questions/types/speaking-cue-card/register.js +52 -1
  430. package/dist/components/questions/types/speaking-cue-card/transform.js +12 -1
  431. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +10 -1
  432. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +161 -1
  433. package/dist/components/questions/types/speaking-describe-image/index.js +2 -1
  434. package/dist/components/questions/types/speaking-describe-image/register.js +69 -1
  435. package/dist/components/questions/types/speaking-describe-image/transform.js +16 -1
  436. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +234 -1
  437. package/dist/components/questions/types/spontaneous-qa/register.js +93 -1
  438. package/dist/components/questions/types/spontaneous-qa/transform.js +34 -1
  439. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.d.ts +2 -0
  440. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.js +9 -0
  441. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.d.ts +2 -0
  442. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.js +179 -0
  443. package/dist/components/questions/types/spontaneous-qa-group/creator-helpers.d.ts +5 -0
  444. package/dist/components/questions/types/spontaneous-qa-group/creator-helpers.js +43 -0
  445. package/dist/components/questions/types/spontaneous-qa-group/flatten.d.ts +14 -0
  446. package/dist/components/questions/types/spontaneous-qa-group/flatten.js +69 -0
  447. package/dist/components/questions/types/spontaneous-qa-group/index.d.ts +2 -0
  448. package/dist/components/questions/types/spontaneous-qa-group/index.js +2 -0
  449. package/dist/components/questions/types/spontaneous-qa-group/register.d.ts +2 -0
  450. package/dist/components/questions/types/spontaneous-qa-group/register.js +139 -0
  451. package/dist/components/questions/types/spontaneous-qa-group/transform.d.ts +2 -0
  452. package/dist/components/questions/types/spontaneous-qa-group/transform.js +52 -0
  453. package/dist/components/questions/types/true-false/TrueFalseClient.js +75 -1
  454. package/dist/components/questions/types/true-false/TrueFalseCreator.js +244 -1
  455. package/dist/components/questions/types/true-false/index.js +2 -1
  456. package/dist/components/questions/types/true-false/register.js +77 -1
  457. package/dist/components/questions/types/true-false/transform.js +32 -1
  458. package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +25 -1
  459. package/dist/components/questions/types/true-false-group/TrueFalseGroupCreator.js +129 -1
  460. package/dist/components/questions/types/true-false-group/index.d.ts +1 -0
  461. package/dist/components/questions/types/true-false-group/index.js +5 -1
  462. package/dist/components/questions/types/true-false-group/map-true-false-group-data.d.ts +4 -0
  463. package/dist/components/questions/types/true-false-group/map-true-false-group-data.js +69 -0
  464. package/dist/components/questions/types/true-false-group/register.js +36 -1
  465. package/dist/components/questions/types/true-false-group/transform.js +43 -1
  466. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +233 -1
  467. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +631 -1
  468. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphWrapper.js +28 -1
  469. package/dist/components/questions/types/word-fill-paragraph/index.js +2 -1
  470. package/dist/components/questions/types/word-fill-paragraph/register.js +136 -1
  471. package/dist/components/questions/types/word-fill-paragraph/transform.js +56 -1
  472. package/dist/components/questions/types/word-fill-structured-form/WfsfWordBankEditor.js +86 -1
  473. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +246 -1
  474. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.d.ts +2 -0
  475. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +484 -1
  476. package/dist/components/questions/types/word-fill-structured-form/index.d.ts +1 -0
  477. package/dist/components/questions/types/word-fill-structured-form/index.js +3 -1
  478. package/dist/components/questions/types/word-fill-structured-form/map-wfsf-question-data.d.ts +7 -0
  479. package/dist/components/questions/types/word-fill-structured-form/map-wfsf-question-data.js +113 -0
  480. package/dist/components/questions/types/word-fill-structured-form/register.js +36 -1
  481. package/dist/components/questions/types/word-fill-structured-form/transform.js +77 -1
  482. package/dist/components/questions/types/word-fill-structured-form/wfsf-word-bank.utils.js +110 -1
  483. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.d.ts +3 -0
  484. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.js +190 -0
  485. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupCreator.d.ts +3 -0
  486. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupCreator.js +182 -0
  487. package/dist/components/questions/types/word-order-and-match-group/index.d.ts +4 -0
  488. package/dist/components/questions/types/word-order-and-match-group/index.js +4 -0
  489. package/dist/components/questions/types/word-order-and-match-group/register.d.ts +2 -0
  490. package/dist/components/questions/types/word-order-and-match-group/register.js +121 -0
  491. package/dist/components/questions/types/word-order-and-match-group/transform.d.ts +2 -0
  492. package/dist/components/questions/types/word-order-and-match-group/transform.js +70 -0
  493. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +84 -1
  494. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +192 -1
  495. package/dist/components/questions/types/word-ordering/index.js +2 -1
  496. package/dist/components/questions/types/word-ordering/register.js +46 -1
  497. package/dist/components/questions/types/word-ordering/transform.js +24 -1
  498. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +99 -1
  499. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +320 -1
  500. package/dist/components/questions/types/write-a-short-letter/index.js +2 -1
  501. package/dist/components/questions/types/write-a-short-letter/register.js +68 -1
  502. package/dist/components/questions/types/write-a-short-letter/transform.js +25 -1
  503. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +61 -1
  504. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +196 -1
  505. package/dist/components/questions/types/write-correct-verb-form/index.js +2 -1
  506. package/dist/components/questions/types/write-correct-verb-form/register.js +62 -1
  507. package/dist/components/questions/types/write-correct-verb-form/transform.js +17 -1
  508. package/dist/components/questions/types/write-sentences/WriteSentencesClient.js +22 -1
  509. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +174 -1
  510. package/dist/components/questions/types/write-sentences/index.js +2 -1
  511. package/dist/components/questions/types/write-sentences/register.js +58 -1
  512. package/dist/components/questions/types/write-sentences/transform.js +28 -1
  513. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +96 -1
  514. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +161 -1
  515. package/dist/components/questions/types/write-short-paragraph/index.js +2 -1
  516. package/dist/components/questions/types/write-short-paragraph/register.js +4 -1
  517. package/dist/components/questions/types/write-short-paragraph/transform.js +21 -1
  518. package/dist/components/questions/viewer/ChooseTheCorrectAnswerGroupViewer.d.ts +8 -0
  519. package/dist/components/questions/viewer/ChooseTheCorrectAnswerGroupViewer.js +11 -0
  520. package/dist/components/questions/viewer/CrossOutWordGroupViewer.d.ts +7 -0
  521. package/dist/components/questions/viewer/CrossOutWordGroupViewer.js +8 -0
  522. package/dist/components/questions/viewer/QuestionGroupViewer.js +36 -1
  523. package/dist/components/questions/viewer/QuestionViewer.js +692 -1
  524. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.d.ts +5 -0
  525. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.js +82 -0
  526. package/dist/components/questions/viewer/TrueFalseGroupViewer.d.ts +8 -0
  527. package/dist/components/questions/viewer/TrueFalseGroupViewer.js +11 -0
  528. package/dist/components/questions/viewer/WordFillStructuredFormViewer.d.ts +9 -0
  529. package/dist/components/questions/viewer/WordFillStructuredFormViewer.js +14 -0
  530. package/dist/components/questions/viewer/WordOrderAndMatchGroupViewer.d.ts +6 -0
  531. package/dist/components/questions/viewer/WordOrderAndMatchGroupViewer.js +95 -0
  532. package/dist/components/questions/viewer/index.d.ts +6 -0
  533. package/dist/components/questions/viewer/index.js +8 -1
  534. package/dist/components/results/ResultReviewPageContainer.js +220 -1
  535. package/dist/components/results/ReviewQuestionRenderer.js +55 -1
  536. package/dist/components/results/hooks/useResultReviewPolling.js +76 -1
  537. package/dist/components/results/index.js +22 -1
  538. package/dist/components/results/renderers/QuestionExplanations.js +19 -1
  539. package/dist/components/results/renderers/ReviewChooseAdjectiveRenderer.js +94 -1
  540. package/dist/components/results/renderers/ReviewChooseBestAnswerRenderer.js +75 -1
  541. package/dist/components/results/renderers/ReviewColoringRenderer.js +110 -1
  542. package/dist/components/results/renderers/ReviewListenAndWriteRenderer.js +48 -1
  543. package/dist/components/results/renderers/ReviewListenChooseAnswerGroupRenderer.js +87 -1
  544. package/dist/components/results/renderers/ReviewListenChooseObjectsRenderer.js +104 -1
  545. package/dist/components/results/renderers/ReviewListenDragObjectsRenderer.js +123 -1
  546. package/dist/components/results/renderers/ReviewListeningHeader.js +104 -1
  547. package/dist/components/results/renderers/ReviewSpeakingAIPanel.js +116 -1
  548. package/dist/components/results/renderers/ReviewSpeakingMedia.js +48 -1
  549. package/dist/components/results/renderers/ReviewSpeakingQuestionItem.js +42 -1
  550. package/dist/components/results/renderers/ReviewSpeakingRenderer.js +12 -1
  551. package/dist/components/results/renderers/ReviewSpeakingRenderer.types.js +85 -1
  552. package/dist/components/results/renderers/review-question-body-dedicated.js +104 -1
  553. package/dist/components/results/renderers/review-question-body-movers.js +148 -1
  554. package/dist/components/results/renderers/review-speaking-map.js +226 -1
  555. package/dist/components/results/renderers/review-spontaneous-qa-group.d.ts +4 -0
  556. package/dist/components/results/renderers/review-spontaneous-qa-group.js +74 -0
  557. package/dist/components/results/report/BadgeTitle.js +5 -1
  558. package/dist/components/results/report/ExamSkillRow.js +20 -1
  559. package/dist/components/results/report/OceanEduExamReport.js +58 -1
  560. package/dist/components/results/report/OceanEduSpeakingReport.js +62 -1
  561. package/dist/components/results/report/OverallAssessmentSection.js +17 -1
  562. package/dist/components/results/report/ResultErrorState.js +18 -1
  563. package/dist/components/results/report/ResultLoadingState.js +10 -1
  564. package/dist/components/results/report/ResultNotPublicState.js +27 -1
  565. package/dist/components/results/report/SpeakingReportParts.js +14 -1
  566. package/dist/components/results/report/SpeakingReportShared.js +86 -1
  567. package/dist/components/results/report/SpeakingRubricSection.js +171 -1
  568. package/dist/components/results/report/speaking-report.utils.js +198 -1
  569. package/dist/components/results/result-review.utils.js +6 -1
  570. package/dist/components/results/review-data.js +222 -1
  571. package/dist/components/results/review-renderer.utils.js +97 -1
  572. package/dist/components/results/review-status.utils.js +36 -1
  573. package/dist/components/results/review-types.js +1 -1
  574. package/dist/components/results/speaking/SpeakingResultAudioPlayer.js +77 -1
  575. package/dist/components/results/speaking/SpeakingReviewResult.js +48 -1
  576. package/dist/components/results/utils/review-score.utils.js +72 -1
  577. package/dist/components/results/utils/speaking-summary.js +224 -1
  578. package/dist/components/results/utils/transform-speaking-review.js +183 -1
  579. package/dist/components/shared/RichTextEditor.d.ts +9 -0
  580. package/dist/components/shared/RichTextEditor.js +47 -0
  581. package/dist/components/shared/audio/SpeakingAudioPlayer.js +100 -1
  582. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +140 -1
  583. package/dist/components/themes/cambridge-yle/CambridgeYleBottomNav.js +10 -1
  584. package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.d.ts +1 -1
  585. package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.js +18 -1
  586. package/dist/components/themes/cambridge-yle/CambridgeYleExampleText.js +11 -1
  587. package/dist/components/themes/cambridge-yle/CambridgeYleHeader.js +11 -1
  588. package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.d.ts +2 -1
  589. package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.js +19 -1
  590. package/dist/components/themes/cambridge-yle/CambridgeYleNavButton.js +13 -1
  591. package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.d.ts +1 -1
  592. package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.js +3 -1
  593. package/dist/components/themes/cambridge-yle/CambridgeYleProgressBar.js +6 -1
  594. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCard.js +5 -1
  595. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCardFullWidth.js +9 -1
  596. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.d.ts +2 -1
  597. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.js +61 -1
  598. package/dist/components/themes/cambridge-yle/CambridgeYleTimer.d.ts +1 -1
  599. package/dist/components/themes/cambridge-yle/CambridgeYleTimer.js +25 -1
  600. package/dist/components/themes/cambridge-yle/index.js +13 -1
  601. package/dist/components/themes/english-certification/EcAnswerTheQuestion.js +25 -1
  602. package/dist/components/themes/english-certification/EcMatchByWritingAnswer.js +89 -1
  603. package/dist/components/themes/english-certification/EcWordFillParagraph.js +56 -1
  604. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +192 -1
  605. package/dist/components/themes/english-certification/EcWritingTask.js +60 -1
  606. package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +108 -1
  607. package/dist/components/themes/english-certification/EnglishCertificationBrand.js +11 -1
  608. package/dist/components/themes/english-certification/EnglishCertificationExamLayout.js +34 -1
  609. package/dist/components/themes/english-certification/EnglishCertificationFlagButton.js +15 -1
  610. package/dist/components/themes/english-certification/EnglishCertificationFooterBar.js +19 -1
  611. package/dist/components/themes/english-certification/EnglishCertificationGroupedQuestion.js +166 -1
  612. package/dist/components/themes/english-certification/EnglishCertificationHeader.js +31 -1
  613. package/dist/components/themes/english-certification/EnglishCertificationInstructionBanner.js +79 -1
  614. package/dist/components/themes/english-certification/EnglishCertificationNavigationPanel.js +71 -1
  615. package/dist/components/themes/english-certification/EnglishCertificationOptionSelector.js +79 -1
  616. package/dist/components/themes/english-certification/EnglishCertificationPartHeader.js +47 -1
  617. package/dist/components/themes/english-certification/EnglishCertificationPassagePanel.js +77 -1
  618. package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.js +25 -1
  619. package/dist/components/themes/english-certification/EnglishCertificationQuestionItem.js +36 -1
  620. package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +81 -1
  621. package/dist/components/themes/english-certification/EnglishCertificationReadingSection.js +95 -1
  622. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestion.js +18 -1
  623. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js +32 -1
  624. package/dist/components/themes/english-certification/english-cert-scroll.js +163 -1
  625. package/dist/components/themes/english-certification/english-certification-theme.js +58 -1
  626. package/dist/components/themes/english-certification/grouped-question-utils.js +243 -1
  627. package/dist/components/themes/english-certification/index.js +24 -1
  628. package/dist/components/themes/index.js +25 -1
  629. package/dist/components/themes/summer-sky/SummerSkyAudioPlayer.js +170 -1
  630. package/dist/components/themes/summer-sky/SummerSkyExitModal.js +21 -1
  631. package/dist/components/themes/summer-sky/SummerSkyLayout.js +221 -1
  632. package/dist/components/themes/summer-sky/SummerSkySubmitModal.js +18 -1
  633. package/dist/components/themes/summer-sky/SummerSkyWaitingRoom.js +193 -1
  634. package/dist/components/themes/types.js +1 -1
  635. package/dist/components/ui/alert-dialog.js +26 -1
  636. package/dist/components/ui/alert.js +22 -1
  637. package/dist/components/ui/avatar.js +12 -1
  638. package/dist/components/ui/badge.js +38 -1
  639. package/dist/components/ui/button.js +35 -1
  640. package/dist/components/ui/calendar.js +76 -1
  641. package/dist/components/ui/card.js +16 -1
  642. package/dist/components/ui/carousel.js +91 -1
  643. package/dist/components/ui/checkbox.js +9 -1
  644. package/dist/components/ui/collapsible.js +13 -1
  645. package/dist/components/ui/command.js +34 -1
  646. package/dist/components/ui/confirm-dialog.js +49 -1
  647. package/dist/components/ui/dialog.js +23 -1
  648. package/dist/components/ui/drawer.js +35 -1
  649. package/dist/components/ui/dropdown-menu.js +33 -1
  650. package/dist/components/ui/file-upload.js +155 -1
  651. package/dist/components/ui/form.js +62 -1
  652. package/dist/components/ui/hover-card.js +10 -1
  653. package/dist/components/ui/image-preview.js +29 -1
  654. package/dist/components/ui/input.js +8 -1
  655. package/dist/components/ui/label.js +9 -1
  656. package/dist/components/ui/language-switcher.js +15 -1
  657. package/dist/components/ui/multi-select.js +90 -1
  658. package/dist/components/ui/points-input.js +33 -1
  659. package/dist/components/ui/popover.js +26 -1
  660. package/dist/components/ui/progress.js +16 -1
  661. package/dist/components/ui/radio-group.js +12 -1
  662. package/dist/components/ui/scroll-area.js +11 -1
  663. package/dist/components/ui/select.js +38 -1
  664. package/dist/components/ui/separator.js +8 -1
  665. package/dist/components/ui/sheet.js +40 -1
  666. package/dist/components/ui/single-select.js +76 -1
  667. package/dist/components/ui/skeleton.js +6 -1
  668. package/dist/components/ui/switch.js +8 -1
  669. package/dist/components/ui/table.js +20 -1
  670. package/dist/components/ui/tabs.js +17 -1
  671. package/dist/components/ui/textarea.js +8 -1
  672. package/dist/components/ui/tooltip.js +11 -1
  673. package/dist/index.d.ts +3 -1
  674. package/dist/index.js +24 -1
  675. package/dist/shared/constants/ApiConstant.d.ts +1 -0
  676. package/dist/shared/constants/ApiConstant.js +278 -1
  677. package/dist/shared/constants/QueryKeyConstant.js +112 -1
  678. package/dist/shared/constants/exam-level.enum.js +41 -1
  679. package/dist/shared/constants/landings/placement-test.constants.js +431 -1
  680. package/dist/shared/constants/question-skills.js +277 -1
  681. package/dist/shared/constants/routes.js +81 -1
  682. package/dist/shared/index.js +10 -1
  683. package/dist/shared/lib/hooks/index.js +10 -1
  684. package/dist/shared/lib/hooks/useAlertDialog.js +57 -1
  685. package/dist/shared/lib/hooks/useAudioPlayCounter.js +30 -1
  686. package/dist/shared/lib/hooks/useAudioPreview.js +81 -1
  687. package/dist/shared/lib/hooks/useAudioUpload.js +24 -1
  688. package/dist/shared/lib/hooks/useConfirmDialog.js +89 -1
  689. package/dist/shared/lib/hooks/useDebouncedCallback.js +32 -1
  690. package/dist/shared/lib/hooks/useDelayedAutoPlay.js +12 -1
  691. package/dist/shared/lib/hooks/useFileUpload.js +24 -1
  692. package/dist/shared/lib/hooks/useImageUrl.js +5 -1
  693. package/dist/shared/lib/hooks/usePresignedFileUrl.js +108 -1
  694. package/dist/shared/lib/hooks/useReactHookForm.js +31 -1
  695. package/dist/shared/lib/hooks/useToast.js +62 -1
  696. package/dist/shared/lib/html-content.js +22 -1
  697. package/dist/shared/lib/i18n/index.js +54 -1
  698. package/dist/shared/lib/i18n/messages/en/admin.d.ts +11 -4
  699. package/dist/shared/lib/i18n/messages/en/admin.js +2111 -1
  700. package/dist/shared/lib/i18n/messages/en/auth.js +86 -1
  701. package/dist/shared/lib/i18n/messages/en/common.js +140 -1
  702. package/dist/shared/lib/i18n/messages/en/file-upload.js +18 -1
  703. package/dist/shared/lib/i18n/messages/en/student.js +133 -1
  704. package/dist/shared/lib/i18n/messages/en/terms.js +37 -1
  705. package/dist/shared/lib/i18n/messages/en.d.ts +11 -4
  706. package/dist/shared/lib/i18n/messages/en.js +15 -1
  707. package/dist/shared/lib/i18n/messages/vi/admin.d.ts +11 -4
  708. package/dist/shared/lib/i18n/messages/vi/admin.js +2111 -1
  709. package/dist/shared/lib/i18n/messages/vi/auth.js +86 -1
  710. package/dist/shared/lib/i18n/messages/vi/common.js +140 -1
  711. package/dist/shared/lib/i18n/messages/vi/file-upload.js +18 -1
  712. package/dist/shared/lib/i18n/messages/vi/student.js +133 -1
  713. package/dist/shared/lib/i18n/messages/vi/terms.js +37 -1
  714. package/dist/shared/lib/i18n/messages/vi.d.ts +11 -4
  715. package/dist/shared/lib/i18n/messages/vi.js +15 -1
  716. package/dist/shared/lib/i18n/types.js +1 -1
  717. package/dist/shared/lib/stores/examQuestionStore.js +212 -1
  718. package/dist/shared/lib/stores/examTakingStore.js +240 -1
  719. package/dist/shared/lib/stores/gradingTransientStore.js +39 -1
  720. package/dist/shared/lib/stores/localeStore.js +10 -1
  721. package/dist/shared/lib/themes/config/starters.config.js +71 -1
  722. package/dist/shared/lib/themes/index.js +49 -1
  723. package/dist/shared/lib/themes/types.js +2 -1
  724. package/dist/shared/lib/toast.js +43 -1
  725. package/dist/shared/lib/utils/public-page.js +44 -1
  726. package/dist/shared/lib/utils/question-reverse-transform.js +438 -1
  727. package/dist/shared/lib/utils/question-transform-types.js +1 -1
  728. package/dist/shared/lib/utils/question-transform-validation.js +194 -1
  729. package/dist/shared/lib/utils/question-transform.js +199 -1
  730. package/dist/shared/lib/utils.js +168 -1
  731. package/dist/shared/lib/validation/choose-correct-answer.validation.js +111 -1
  732. package/dist/shared/lib/validation/label-the-picture.validation.js +19 -1
  733. package/dist/shared/lib/validation/listen-and-choose-from-answer-group.validation.js +46 -1
  734. package/dist/shared/lib/validation/listen-and-choose-objects-in-scene.validation.js +21 -1
  735. package/dist/shared/lib/validation/listen-and-drag-objects-into-scene.validation.js +34 -1
  736. package/dist/shared/lib/validation/listen-and-speak-answer.validation.js +40 -1
  737. package/dist/shared/lib/validation/listen-and-speak-compare-images.validation.js +45 -1
  738. package/dist/shared/lib/validation/listen-and-speak-image-group.validation.js +19 -1
  739. package/dist/shared/lib/validation/listen-and-speak-info-exchange.validation.js +39 -1
  740. package/dist/shared/lib/validation/listen-and-speak-odd-one-out.validation.js +32 -1
  741. package/dist/shared/lib/validation/listen-and-speak-question-list.validation.js +19 -1
  742. package/dist/shared/lib/validation/listen-and-speak-with-story-images.validation.js +28 -1
  743. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +12 -1
  744. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +47 -1
  745. package/dist/shared/lib/validation/read-and-color-objects.validation.js +29 -1
  746. package/dist/shared/lib/validation/read-displayed-content.validation.js +28 -1
  747. package/dist/shared/lib/validation/speaking-conversation.validation.js +75 -1
  748. package/dist/shared/lib/validation/speaking-describe-image.validation.js +31 -1
  749. package/dist/shared/lib/validation/spontaneous-qa.validation.js +48 -1
  750. package/dist/shared/lib/validation/word-fill-paragraph-default.validation.js +38 -1
  751. package/dist/shared/lib/validation/word-fill-paragraph-with-options.validation.js +39 -1
  752. package/dist/shared/lib/validation/word-fill-paragraph.validation.js +43 -1
  753. package/dist/shared/lib/validation/word-ordering.validation.js +33 -1
  754. package/dist/shared/types/branch.types.js +6 -1
  755. package/dist/shared/types/common.types.d.ts +1 -1
  756. package/dist/shared/types/common.types.js +49 -1
  757. package/dist/shared/types/entities/exam-schedule.types.js +5 -1
  758. package/dist/shared/types/entities/exam.types.js +1 -1
  759. package/dist/shared/types/entities/question.types.js +4 -1
  760. package/dist/shared/types/entities/result.types.js +4 -1
  761. package/dist/shared/types/entities/user.types.js +4 -1
  762. package/dist/shared/types/exam-taking.types.js +2 -1
  763. package/dist/shared/types/history.types.js +34 -1
  764. package/dist/shared/types/index.js +1 -1
  765. package/dist/shared/types/questions/choose-correct-adjective.js +4 -1
  766. package/dist/shared/types/questions/choose-the-correct-answer-group.d.ts +1 -0
  767. package/dist/shared/types/questions/choose-the-correct-answer-group.js +1 -1
  768. package/dist/shared/types/questions/choose-the-correct-answer.js +9 -1
  769. package/dist/shared/types/questions/cross-out-word-group.d.ts +2 -0
  770. package/dist/shared/types/questions/cross-out-word-group.js +1 -1
  771. package/dist/shared/types/questions/fill-missing-words-in-grid.js +11 -1
  772. package/dist/shared/types/questions/index.d.ts +1 -0
  773. package/dist/shared/types/questions/index.js +46 -1
  774. package/dist/shared/types/questions/listen-and-speak-answer.js +1 -1
  775. package/dist/shared/types/questions/listen-and-speak-compare-images.js +1 -1
  776. package/dist/shared/types/questions/listen-and-speak-image-group.js +1 -1
  777. package/dist/shared/types/questions/listen-and-speak-info-exchange.js +3 -1
  778. package/dist/shared/types/questions/listen-and-speak-odd-one-out.js +3 -1
  779. package/dist/shared/types/questions/listen-and-speak-question-list.js +1 -1
  780. package/dist/shared/types/questions/listen-and-speak-with-story-images.js +3 -1
  781. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +5 -1
  782. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +9 -1
  783. package/dist/shared/types/questions/look-picture-fill-word-hint.js +7 -1
  784. package/dist/shared/types/questions/read-displayed-content.js +1 -1
  785. package/dist/shared/types/questions/speaking-conversation.js +7 -1
  786. package/dist/shared/types/questions/speaking-describe-image.js +1 -1
  787. package/dist/shared/types/questions/spontaneous-qa.js +1 -1
  788. package/dist/shared/types/questions/word-order-and-match-group.d.ts +33 -0
  789. package/dist/shared/types/questions/word-order-and-match-group.js +1 -0
  790. package/dist/shared/types/questions/write-correct-verb-form.js +12 -1
  791. package/dist/shared/types/role.types.js +1 -1
  792. package/dist/shared/types/structure-management.js +28 -1
  793. package/dist/shared/types/student.types.js +57 -1
  794. package/dist/shared/types/teacher-dashboard-rooms.js +2 -1
  795. package/package.json +184 -165
  796. package/dist/mcp/config.d.ts +0 -12
  797. package/dist/mcp/config.js +0 -1
  798. package/dist/mcp/http-client.d.ts +0 -6
  799. package/dist/mcp/http-client.js +0 -1
  800. package/dist/mcp/index.d.ts +0 -6
  801. package/dist/mcp/index.js +0 -2
  802. package/dist/mcp/tools/_helpers.d.ts +0 -4
  803. package/dist/mcp/tools/_helpers.js +0 -1
  804. package/dist/mcp/tools/exams.d.ts +0 -2
  805. package/dist/mcp/tools/exams.js +0 -1
  806. package/dist/mcp/tools/index.d.ts +0 -2
  807. package/dist/mcp/tools/index.js +0 -1
  808. package/dist/mcp/tools/results.d.ts +0 -2
  809. package/dist/mcp/tools/results.js +0 -1
@@ -1 +1,26 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{ArrowLeft as a,Clock as l,Loader2 as n,Trophy as s}from"lucide-react";import{Button as i}from"../../../components/ui/button";export function EntryLoading(){return e("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-gradient-to-b from-sky-100 via-blue-50 to-indigo-100",children:e(n,{className:"h-10 w-10 animate-spin text-sky-600"})})}export function EntryMessage({emoji:a,title:l,description:n,room:s,tone:i,onBack:r}){const o="red"===i?"text-red-600":"text-sky-700",m="red"===i?"border-red-600 bg-red-500":"border-sky-600 bg-sky-500";return e("div",{className:`fixed inset-0 z-50 flex items-center justify-center overflow-hidden bg-gradient-to-b ${"red"===i?"from-red-50 via-orange-50 to-amber-50":"from-sky-100 via-blue-50 to-indigo-100"}`,children:t("div",{className:"mx-auto max-w-md p-4 text-center",children:[e("div",{className:"mb-6 text-7xl",children:a}),e("h2",{className:`mb-3 text-2xl font-bold ${o}`,children:l}),s&&t("div",{className:"mb-4 rounded-xl bg-white/60 px-5 py-3 text-left text-sm text-sky-700 shadow-sm backdrop-blur-sm",children:[e("p",{className:"text-base font-semibold",children:s.name}),s.code&&t("p",{className:"mt-1 text-sky-500",children:["Mã phòng: ",e("span",{className:"font-mono font-medium",children:s.code})]})]}),e("p",{className:"mb-6 text-base "+("red"===i?"text-red-500":"text-sky-600"),children:n}),e("button",{className:`mx-auto inline-flex items-center gap-2 rounded-2xl border-b-4 px-8 py-3 text-lg font-bold text-white shadow-md transition-all hover:brightness-110 active:translate-y-0.5 active:border-b-2 active:shadow-sm ${m}`,onClick:r,children:"← Quay lại"})]})})}export function EntryWaitingRoom({room:o,state:m,countdownLabel:d,attempt:c,isStarting:x,onStart:b,onBack:h}){return e("div",{className:"fixed inset-0 z-50 flex items-center justify-center overflow-hidden bg-gradient-to-b from-sky-100 via-blue-50 to-indigo-100",children:t("div",{className:"relative z-10 w-[88%] max-w-lg rounded-3xl bg-white p-6 shadow-2xl md:p-8",children:[t("div",{className:"mb-4 rounded-2xl bg-slate-50 px-4 py-3.5 text-center",children:[e("h2",{className:"text-base font-extrabold tracking-wide text-slate-800",children:o.name}),o.exam?.name&&e("p",{className:"mt-0.5 text-xs font-medium text-slate-500",children:o.exam.name})]}),(o.exam?.durationMinutes||o.exam?.totalScore)&&t("div",{className:"mb-4 grid grid-cols-2 gap-3",children:[null!=o.exam?.durationMinutes&&e(r,{icon:e(l,{className:"h-4 w-4"}),value:o.exam.durationMinutes,label:"Phút"}),null!=o.exam?.totalScore&&e(r,{icon:e(s,{className:"h-4 w-4"}),value:o.exam.totalScore,label:"Thang điểm"})]}),"BEFORE_START"===m&&t("p",{className:"mb-5 text-center text-sm font-semibold text-slate-600",children:["Bài thi sẽ bắt đầu sau ",d]}),"EXPIRED"===m&&e("p",{className:"mb-5 text-center text-sm font-semibold text-red-500",children:"Bài thi đã kết thúc."}),"ALREADY_SUBMITTED"===m&&t("p",{className:"mb-5 text-center text-sm font-semibold text-slate-600",children:["Bạn đã hoàn thành bài thi này",null!=c?.totalScore?` • Điểm: ${c.totalScore}`:"","."]}),t("div",{className:"flex flex-col items-center gap-3",children:["WITHIN_WINDOW"===m&&t(i,{onClick:b,disabled:x,className:"min-w-[180px] bg-blue-600 hover:bg-blue-700",children:[x?e(n,{className:"mr-2 h-4 w-4 animate-spin"}):null,"Bắt đầu làm bài"]}),t(i,{variant:"outline",onClick:h,children:[e(a,{className:"mr-2 h-4 w-4"}),"Quay lại"]})]})]})})}function r({icon:a,value:l,label:n}){return t("div",{className:"flex flex-col items-center rounded-2xl bg-slate-50 px-2 py-4",children:[e("div",{className:"mb-2 flex h-8 w-8 items-center justify-center rounded-full bg-slate-100 text-slate-600",children:a}),e("span",{className:"text-2xl font-extrabold text-slate-800",children:l}),e("span",{className:"text-xs font-semibold text-slate-500",children:n})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { ArrowLeft, Clock, Loader2, Trophy } from 'lucide-react';
4
+ import { Button } from '../../../components/ui/button';
5
+ /** Fullscreen loader used while room/attempt data is being fetched. */
6
+ export function EntryLoading() {
7
+ return (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-gradient-to-b from-sky-100 via-blue-50 to-indigo-100", children: _jsx(Loader2, { className: "h-10 w-10 animate-spin text-sky-600" }) }));
8
+ }
9
+ /** Child-friendly message screen shared by NO_EXAM / NO_ACCESS states. */
10
+ export function EntryMessage({ emoji, title, description, room, tone, onBack, }) {
11
+ const toneClass = tone === 'red' ? 'text-red-600' : 'text-sky-700';
12
+ const buttonClass = tone === 'red'
13
+ ? 'border-red-600 bg-red-500'
14
+ : 'border-sky-600 bg-sky-500';
15
+ const bg = tone === 'red'
16
+ ? 'from-red-50 via-orange-50 to-amber-50'
17
+ : 'from-sky-100 via-blue-50 to-indigo-100';
18
+ return (_jsx("div", { className: `fixed inset-0 z-50 flex items-center justify-center overflow-hidden bg-gradient-to-b ${bg}`, children: _jsxs("div", { className: "mx-auto max-w-md p-4 text-center", children: [_jsx("div", { className: "mb-6 text-7xl", children: emoji }), _jsx("h2", { className: `mb-3 text-2xl font-bold ${toneClass}`, children: title }), room && (_jsxs("div", { className: "mb-4 rounded-xl bg-white/60 px-5 py-3 text-left text-sm text-sky-700 shadow-sm backdrop-blur-sm", children: [_jsx("p", { className: "text-base font-semibold", children: room.name }), room.code && (_jsxs("p", { className: "mt-1 text-sky-500", children: ["M\u00E3 ph\u00F2ng: ", _jsx("span", { className: "font-mono font-medium", children: room.code })] }))] })), _jsx("p", { className: `mb-6 text-base ${tone === 'red' ? 'text-red-500' : 'text-sky-600'}`, children: description }), _jsx("button", { className: `mx-auto inline-flex items-center gap-2 rounded-2xl border-b-4 px-8 py-3 text-lg font-bold text-white shadow-md transition-all hover:brightness-110 active:translate-y-0.5 active:border-b-2 active:shadow-sm ${buttonClass}`, onClick: onBack, children: "\u2190 Quay l\u1EA1i" })] }) }));
19
+ }
20
+ /** Default (non-themed) waiting room card with exam stats and a start button. */
21
+ export function EntryWaitingRoom({ room, state, countdownLabel, attempt, isStarting, onStart, onBack, }) {
22
+ return (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center overflow-hidden bg-gradient-to-b from-sky-100 via-blue-50 to-indigo-100", children: _jsxs("div", { className: "relative z-10 w-[88%] max-w-lg rounded-3xl bg-white p-6 shadow-2xl md:p-8", children: [_jsxs("div", { className: "mb-4 rounded-2xl bg-slate-50 px-4 py-3.5 text-center", children: [_jsx("h2", { className: "text-base font-extrabold tracking-wide text-slate-800", children: room.name }), room.exam?.name && _jsx("p", { className: "mt-0.5 text-xs font-medium text-slate-500", children: room.exam.name })] }), (room.exam?.durationMinutes || room.exam?.totalScore) && (_jsxs("div", { className: "mb-4 grid grid-cols-2 gap-3", children: [room.exam?.durationMinutes != null && (_jsx(Stat, { icon: _jsx(Clock, { className: "h-4 w-4" }), value: room.exam.durationMinutes, label: "Ph\u00FAt" })), room.exam?.totalScore != null && (_jsx(Stat, { icon: _jsx(Trophy, { className: "h-4 w-4" }), value: room.exam.totalScore, label: "Thang \u0111i\u1EC3m" }))] })), state === 'BEFORE_START' && (_jsxs("p", { className: "mb-5 text-center text-sm font-semibold text-slate-600", children: ["B\u00E0i thi s\u1EBD b\u1EAFt \u0111\u1EA7u sau ", countdownLabel] })), state === 'EXPIRED' && (_jsx("p", { className: "mb-5 text-center text-sm font-semibold text-red-500", children: "B\u00E0i thi \u0111\u00E3 k\u1EBFt th\u00FAc." })), state === 'ALREADY_SUBMITTED' && (_jsxs("p", { className: "mb-5 text-center text-sm font-semibold text-slate-600", children: ["B\u1EA1n \u0111\u00E3 ho\u00E0n th\u00E0nh b\u00E0i thi n\u00E0y", attempt?.totalScore != null ? ` • Điểm: ${attempt.totalScore}` : '', "."] })), _jsxs("div", { className: "flex flex-col items-center gap-3", children: [state === 'WITHIN_WINDOW' && (_jsxs(Button, { onClick: onStart, disabled: isStarting, className: "min-w-[180px] bg-blue-600 hover:bg-blue-700", children: [isStarting ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null, "B\u1EAFt \u0111\u1EA7u l\u00E0m b\u00E0i"] })), _jsxs(Button, { variant: "outline", onClick: onBack, children: [_jsx(ArrowLeft, { className: "mr-2 h-4 w-4" }), "Quay l\u1EA1i"] })] })] }) }));
23
+ }
24
+ function Stat({ icon, value, label }) {
25
+ return (_jsxs("div", { className: "flex flex-col items-center rounded-2xl bg-slate-50 px-2 py-4", children: [_jsx("div", { className: "mb-2 flex h-8 w-8 items-center justify-center rounded-full bg-slate-100 text-slate-600", children: icon }), _jsx("span", { className: "text-2xl font-extrabold text-slate-800", children: value }), _jsx("span", { className: "text-xs font-semibold text-slate-500", children: label })] }));
26
+ }
@@ -1 +1,55 @@
1
- const t=new Set(["SUBMITTED","GRADED","PUBLISHED"]);export function unwrap(t){return function(t){return"object"==typeof t&&null!==t&&"data"in t}(t)?t.data:t}export function unwrapAttempt(t){return"attempt"in t?t.attempt:t}export function determineEntryState(e,n){const r=!0===e.allowMultipleSubmissions;if(n){const e=n.status??"";if(t.has(e))return r?"WITHIN_WINDOW":"ALREADY_SUBMITTED";if("IN_PROGRESS"===e)return"WITHIN_WINDOW"}const a=Date.now(),o=e.startDatetime?new Date(e.startDatetime).getTime():null,i=e.endDatetime?new Date(e.endDatetime).getTime():null;return!r&&i&&a>i?"EXPIRED":o&&a<o?"BEFORE_START":"WITHIN_WINDOW"}export function getSecondsUntilStart(t){if(!t.startDatetime)return 0;const e=new Date(t.startDatetime).getTime();return Math.max(0,Math.floor((e-Date.now())/1e3))}export function formatCountdown(t){const e=Math.floor(t/86400),n=Math.floor(t%86400/3600),r=Math.floor(t%3600/60),a=t%60;return e>0?`${e} ngày ${n} giờ ${r} phút`:n>0?`${n} giờ ${r} phút ${a} giây`:r>0?`${r} phút ${a} giây`:`${a} giây`}
1
+ /** Attempt statuses that mean the student already finished this attempt. */
2
+ const SUBMITTED_STATUSES = new Set(['SUBMITTED', 'GRADED', 'PUBLISHED']);
3
+ export function unwrap(value) {
4
+ return hasData(value) ? value.data : value;
5
+ }
6
+ export function unwrapAttempt(value) {
7
+ return 'attempt' in value ? value.attempt : value;
8
+ }
9
+ function hasData(value) {
10
+ return typeof value === 'object' && value !== null && 'data' in value;
11
+ }
12
+ /**
13
+ * Determine the entry state from room + attempt data. Ported from the host
14
+ * app's `determineState`, including the `allowMultipleSubmissions` handling.
15
+ */
16
+ export function determineEntryState(room, attempt) {
17
+ const canMultipleSubmit = room.allowMultipleSubmissions === true;
18
+ if (attempt) {
19
+ const status = attempt.status ?? '';
20
+ if (SUBMITTED_STATUSES.has(status)) {
21
+ return canMultipleSubmit ? 'WITHIN_WINDOW' : 'ALREADY_SUBMITTED';
22
+ }
23
+ if (status === 'IN_PROGRESS')
24
+ return 'WITHIN_WINDOW';
25
+ }
26
+ const now = Date.now();
27
+ const start = room.startDatetime ? new Date(room.startDatetime).getTime() : null;
28
+ const end = room.endDatetime ? new Date(room.endDatetime).getTime() : null;
29
+ if (!canMultipleSubmit && end && now > end)
30
+ return 'EXPIRED';
31
+ if (start && now < start)
32
+ return 'BEFORE_START';
33
+ return 'WITHIN_WINDOW';
34
+ }
35
+ /** Seconds remaining until a room's start time (clamped at 0). */
36
+ export function getSecondsUntilStart(room) {
37
+ if (!room.startDatetime)
38
+ return 0;
39
+ const start = new Date(room.startDatetime).getTime();
40
+ return Math.max(0, Math.floor((start - Date.now()) / 1000));
41
+ }
42
+ /** Human-friendly countdown string (days/hours/minutes/seconds). */
43
+ export function formatCountdown(seconds) {
44
+ const days = Math.floor(seconds / 86400);
45
+ const hours = Math.floor((seconds % 86400) / 3600);
46
+ const mins = Math.floor((seconds % 3600) / 60);
47
+ const secs = seconds % 60;
48
+ if (days > 0)
49
+ return `${days} ngày ${hours} giờ ${mins} phút`;
50
+ if (hours > 0)
51
+ return `${hours} giờ ${mins} phút ${secs} giây`;
52
+ if (mins > 0)
53
+ return `${mins} phút ${secs} giây`;
54
+ return `${secs} giây`;
55
+ }
@@ -1 +1,18 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{Loader2 as s}from"lucide-react";import{getTheme as a}from"../../../shared/lib/themes";export function FullscreenLoading(){const r=a(),i=r.visuals?.backgroundFilter??"grayscale(1) blur(8px) saturate(0.85) brightness(0.9)",l=r.visuals?.backgroundOverlay??"linear-gradient(to bottom, rgba(0, 0, 0, 0.1), transparent, rgba(0, 0, 0, 0.2))";return t("div",{className:"fixed inset-0 z-50 flex items-center justify-center overflow-hidden",children:[e("div",{className:"absolute inset-0 scale-110 bg-cover bg-center",style:{backgroundImage:`url(${r.assets.background})`,filter:i}}),e("div",{className:"absolute inset-0",style:{background:l}}),t("div",{className:"relative z-10 flex flex-col items-center gap-4",children:[e(s,{className:"h-12 w-12 animate-spin text-white drop-shadow-lg"}),e("p",{className:"text-lg font-medium text-white drop-shadow-lg",children:"Đang tải..."})]})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Loader2 } from 'lucide-react';
4
+ import { getTheme } from '../../../shared/lib/themes';
5
+ /**
6
+ * Fullscreen loading component for exam entry/taking.
7
+ * Matches the themed waiting-room visual style with a blurred background.
8
+ */
9
+ export function FullscreenLoading() {
10
+ const theme = getTheme();
11
+ const backgroundFilter = theme.visuals?.backgroundFilter ?? 'grayscale(1) blur(8px) saturate(0.85) brightness(0.9)';
12
+ const backgroundOverlay = theme.visuals?.backgroundOverlay ??
13
+ 'linear-gradient(to bottom, rgba(0, 0, 0, 0.1), transparent, rgba(0, 0, 0, 0.2))';
14
+ return (_jsxs("div", { className: "fixed inset-0 z-50 flex items-center justify-center overflow-hidden", children: [_jsx("div", { className: "absolute inset-0 scale-110 bg-cover bg-center", style: {
15
+ backgroundImage: `url(${theme.assets.background})`,
16
+ filter: backgroundFilter,
17
+ } }), _jsx("div", { className: "absolute inset-0", style: { background: backgroundOverlay } }), _jsxs("div", { className: "relative z-10 flex flex-col items-center gap-4", children: [_jsx(Loader2, { className: "h-12 w-12 animate-spin text-white drop-shadow-lg" }), _jsx("p", { className: "text-lg font-medium text-white drop-shadow-lg", children: "\u0110ang t\u1EA3i..." })] })] }));
18
+ }
@@ -1 +1,4 @@
1
- export{StudentExamEntryPageContainer}from"./StudentExamEntryPageContainer";export{EntryRestartDialog}from"./EntryRestartDialog";export{EntryLoading,EntryMessage,EntryWaitingRoom}from"./exam-entry.states";export{determineEntryState,formatCountdown,getSecondsUntilStart,unwrap,unwrapAttempt}from"./exam-entry.utils";
1
+ export { StudentExamEntryPageContainer } from './StudentExamEntryPageContainer';
2
+ export { EntryRestartDialog } from './EntryRestartDialog';
3
+ export { EntryLoading, EntryMessage, EntryWaitingRoom } from './exam-entry.states';
4
+ export { determineEntryState, formatCountdown, getSecondsUntilStart, unwrap, unwrapAttempt, } from './exam-entry.utils';
@@ -0,0 +1,30 @@
1
+ import type { ExamPart, ExamQuestion } from '../../shared/types/entities/exam.types';
2
+ export type PreviewQuestion = Partial<ExamQuestion> & {
3
+ id?: string;
4
+ partId?: string;
5
+ questionNumber?: number;
6
+ points?: number | string;
7
+ difficulty?: string;
8
+ };
9
+ export interface QuestionsByPart {
10
+ partId: string;
11
+ partNo: number;
12
+ partName: string;
13
+ questions: PreviewQuestion[];
14
+ }
15
+ export interface OrderedPart {
16
+ id: string;
17
+ name?: string;
18
+ }
19
+ /** SDK fetch helpers return either `{ data: T }` or the bare value. */
20
+ export declare function unwrapApiData<T>(res: {
21
+ data: T;
22
+ } | T): T;
23
+ export declare function sumQuestionPoints(questions: PreviewQuestion[]): number;
24
+ export declare function getQuestionPoints(question: PreviewQuestion): number;
25
+ export declare function getQuestionNumber(question: PreviewQuestion): number;
26
+ export declare function getQuestionDifficulty(question: PreviewQuestion): string;
27
+ export declare function difficultyBadgeClass(difficulty?: string): string;
28
+ export declare function groupQuestionsByPart(questions: PreviewQuestion[], templateParts?: Array<ExamPart & {
29
+ title?: string;
30
+ }>): QuestionsByPart[];
@@ -0,0 +1,62 @@
1
+ /** SDK fetch helpers return either `{ data: T }` or the bare value. */
2
+ export function unwrapApiData(res) {
3
+ if (res && typeof res === 'object' && 'data' in res) {
4
+ return res.data;
5
+ }
6
+ return res;
7
+ }
8
+ export function sumQuestionPoints(questions) {
9
+ return questions.reduce((total, q) => total + (Number(q.points) || 0), 0);
10
+ }
11
+ export function getQuestionPoints(question) {
12
+ return Number(question.points) || 1;
13
+ }
14
+ export function getQuestionNumber(question) {
15
+ return question.questionNumber ?? 0;
16
+ }
17
+ export function getQuestionDifficulty(question) {
18
+ return question.difficulty ?? 'N/A';
19
+ }
20
+ export function difficultyBadgeClass(difficulty) {
21
+ switch (difficulty?.toUpperCase()) {
22
+ case 'EASY':
23
+ return 'bg-green-100 text-green-800';
24
+ case 'MEDIUM':
25
+ return 'bg-orange-100 text-orange-800';
26
+ case 'HARD':
27
+ return 'bg-red-100 text-red-800';
28
+ default:
29
+ return 'bg-gray-100 text-gray-800';
30
+ }
31
+ }
32
+ export function groupQuestionsByPart(questions, templateParts = []) {
33
+ const grouped = questions.reduce((acc, q) => {
34
+ const qPartId = q.partId || q.part_id || '';
35
+ const qPartNo = q.partNo ?? q.part_no ?? 0;
36
+ const existingPart = acc.find((p) => (qPartId && p.partId === qPartId) || (qPartNo > 0 && p.partNo === qPartNo));
37
+ if (existingPart) {
38
+ existingPart.questions.push(q);
39
+ }
40
+ else {
41
+ const templatePart = templateParts.find((tp) => tp.id === qPartId) ||
42
+ templateParts.find((_tp, index) => index + 1 === qPartNo) ||
43
+ (qPartNo > 0 ? templateParts[qPartNo - 1] : undefined);
44
+ const resolvedPartNo = qPartNo > 0
45
+ ? qPartNo
46
+ : templatePart
47
+ ? templateParts.indexOf(templatePart) + 1
48
+ : acc.length + 1;
49
+ const partName = templatePart?.name ||
50
+ templatePart?.title ||
51
+ `Part ${resolvedPartNo}`;
52
+ acc.push({
53
+ partId: qPartId || `part-${resolvedPartNo}`,
54
+ partNo: resolvedPartNo,
55
+ partName,
56
+ questions: [q],
57
+ });
58
+ }
59
+ return acc;
60
+ }, []);
61
+ return grouped.sort((a, b) => a.partNo - b.partNo);
62
+ }
@@ -1 +1,45 @@
1
- export var QuestionStatus;!function(t){t.NEW="new",t.SAVED="saved",t.UNSAVED="unsaved"}(QuestionStatus||(QuestionStatus={}));const t=t=>`oe-exam-question-status:${t}`;function e(e){if("undefined"==typeof window)return{};const n=window.localStorage.getItem(t(e));if(!n)return{};try{return JSON.parse(n)}catch{return{}}}function n(e,n){"undefined"!=typeof window&&window.localStorage.setItem(t(e),JSON.stringify(n))}export function getQuestionStatus(t,n){return e(t)[n]??QuestionStatus.NEW}export function setQuestionStatus(t,o,i){n(t,{...e(t),[o]:i})}export function clearQuestionStatuses(e){"undefined"!=typeof window&&window.localStorage.removeItem(t(e))}export function initializeQuestionStatusesFromAPI(t,e,o){const i=new Set(e.filter(t=>Number.isFinite(t))),u={};for(let t=1;t<=o;t+=1)u[t]=i.has(t)?QuestionStatus.SAVED:QuestionStatus.NEW;n(t,u)}
1
+ export var QuestionStatus;
2
+ (function (QuestionStatus) {
3
+ QuestionStatus["NEW"] = "new";
4
+ QuestionStatus["SAVED"] = "saved";
5
+ QuestionStatus["UNSAVED"] = "unsaved";
6
+ })(QuestionStatus || (QuestionStatus = {}));
7
+ const STORAGE_PREFIX = 'oe-exam-question-status';
8
+ const getStorageKey = (examId) => `${STORAGE_PREFIX}:${examId}`;
9
+ function readStatusMap(examId) {
10
+ if (typeof window === 'undefined')
11
+ return {};
12
+ const raw = window.localStorage.getItem(getStorageKey(examId));
13
+ if (!raw)
14
+ return {};
15
+ try {
16
+ return JSON.parse(raw);
17
+ }
18
+ catch {
19
+ return {};
20
+ }
21
+ }
22
+ function writeStatusMap(examId, map) {
23
+ if (typeof window === 'undefined')
24
+ return;
25
+ window.localStorage.setItem(getStorageKey(examId), JSON.stringify(map));
26
+ }
27
+ export function getQuestionStatus(examId, questionNumber) {
28
+ return readStatusMap(examId)[questionNumber] ?? QuestionStatus.NEW;
29
+ }
30
+ export function setQuestionStatus(examId, questionNumber, status) {
31
+ writeStatusMap(examId, { ...readStatusMap(examId), [questionNumber]: status });
32
+ }
33
+ export function clearQuestionStatuses(examId) {
34
+ if (typeof window === 'undefined')
35
+ return;
36
+ window.localStorage.removeItem(getStorageKey(examId));
37
+ }
38
+ export function initializeQuestionStatusesFromAPI(examId, savedQuestionNumbers, totalQuestions) {
39
+ const saved = new Set(savedQuestionNumbers.filter((value) => Number.isFinite(value)));
40
+ const next = {};
41
+ for (let questionNumber = 1; questionNumber <= totalQuestions; questionNumber += 1) {
42
+ next[questionNumber] = saved.has(questionNumber) ? QuestionStatus.SAVED : QuestionStatus.NEW;
43
+ }
44
+ writeStatusMap(examId, next);
45
+ }
@@ -1 +1,18 @@
1
- export function normalizeTemplateGroupConfig(e){return{...e,groups:e.groups??e.group?.items,groupConfig:e.groupConfig??e.group?.config,groupType:e.groupType??e.group?.type}}export function getPartByQuestionIndex(e,n){return e.parts.find(e=>n>=e.startIndex&&n<=e.endIndex)}export function getSectionForQuestionIndex(e,n){return e.sections?.find(e=>n>=e.startIndex&&n<=e.endIndex)}export function getEffectiveQuestionConfig(e,n){const o=getSectionForQuestionIndex(e,n);return o?{...normalizeTemplateGroupConfig(o),sectionName:o.name}:normalizeTemplateGroupConfig(e)}
1
+ export function normalizeTemplateGroupConfig(config) {
2
+ return {
3
+ ...config,
4
+ groups: config.groups ?? config.group?.items,
5
+ groupConfig: config.groupConfig ?? config.group?.config,
6
+ groupType: config.groupType ?? config.group?.type,
7
+ };
8
+ }
9
+ export function getPartByQuestionIndex(template, questionIndex) {
10
+ return template.parts.find((part) => questionIndex >= part.startIndex && questionIndex <= part.endIndex);
11
+ }
12
+ export function getSectionForQuestionIndex(part, questionIndex) {
13
+ return part.sections?.find((section) => questionIndex >= section.startIndex && questionIndex <= section.endIndex);
14
+ }
15
+ export function getEffectiveQuestionConfig(part, questionIndex) {
16
+ const section = getSectionForQuestionIndex(part, questionIndex);
17
+ return section ? { ...normalizeTemplateGroupConfig(section), sectionName: section.name } : normalizeTemplateGroupConfig(part);
18
+ }
@@ -32,4 +32,5 @@ export { EXAM_DIFFICULTY, ExamViewDialog } from './ExamViewDialog';
32
32
  export type { ExamDifficulty, ExamViewDialogExam, ExamViewDialogProps } from './ExamViewDialog';
33
33
  export { ExamPreviewDialog } from './ExamPreviewDialog';
34
34
  export type { ExamPreviewDialogProps } from './ExamPreviewDialog';
35
+ export type { PreviewQuestion, QuestionsByPart } from './exam-preview.types';
35
36
  export { QuestionStatus, clearQuestionStatuses, getQuestionStatus, initializeQuestionStatusesFromAPI, setQuestionStatus } from './exam-question-status';
@@ -1 +1,19 @@
1
- export*from"./take";export{StudentExamEntryPageContainer}from"./entry";export{EntryRestartDialog,EntryLoading,EntryMessage,EntryWaitingRoom}from"./entry";export{determineEntryState,formatCountdown,getSecondsUntilStart}from"./entry";export{StudentContestRoomsPageContainer}from"./rooms";export{CreateExamPageContainer}from"./CreateExamPageContainer";export{CreateExamInfoForm}from"./CreateExamInfoForm";export{TemplateSelector}from"./TemplateSelector";export{ExamCreator}from"./ExamCreator";export{ExamPartTabs}from"./ExamPartTabs";export{ExamQuestionGrid}from"./ExamQuestionGrid";export{PartAudioUploader}from"./PartAudioUploader";export{createExamQuestionsApi,createExamQuestionsFetchApi,createMockExamQuestionsApi}from"./api";export{ExamQuestionsPageContainer}from"./ExamQuestionsPageContainer";export{UnsavedChangesDialog}from"./UnsavedChangesDialog";export{CompletionDialog}from"./CompletionDialog";export{EXAM_DIFFICULTY,ExamViewDialog}from"./ExamViewDialog";export{ExamPreviewDialog}from"./ExamPreviewDialog";export{QuestionStatus,clearQuestionStatuses,getQuestionStatus,initializeQuestionStatusesFromAPI,setQuestionStatus}from"./exam-question-status";
1
+ export * from './take';
2
+ export { StudentExamEntryPageContainer } from './entry';
3
+ export { EntryRestartDialog, EntryLoading, EntryMessage, EntryWaitingRoom } from './entry';
4
+ export { determineEntryState, formatCountdown, getSecondsUntilStart } from './entry';
5
+ export { StudentContestRoomsPageContainer } from './rooms';
6
+ export { CreateExamPageContainer } from './CreateExamPageContainer';
7
+ export { CreateExamInfoForm } from './CreateExamInfoForm';
8
+ export { TemplateSelector } from './TemplateSelector';
9
+ export { ExamCreator } from './ExamCreator';
10
+ export { ExamPartTabs } from './ExamPartTabs';
11
+ export { ExamQuestionGrid } from './ExamQuestionGrid';
12
+ export { PartAudioUploader } from './PartAudioUploader';
13
+ export { createExamQuestionsApi, createExamQuestionsFetchApi, createMockExamQuestionsApi } from './api';
14
+ export { ExamQuestionsPageContainer } from './ExamQuestionsPageContainer';
15
+ export { UnsavedChangesDialog } from './UnsavedChangesDialog';
16
+ export { CompletionDialog } from './CompletionDialog';
17
+ export { EXAM_DIFFICULTY, ExamViewDialog } from './ExamViewDialog';
18
+ export { ExamPreviewDialog } from './ExamPreviewDialog';
19
+ export { QuestionStatus, clearQuestionStatuses, getQuestionStatus, initializeQuestionStatusesFromAPI, setQuestionStatus } from './exam-question-status';
@@ -1 +1,183 @@
1
- "use client";import{jsx as e,jsxs as t,Fragment as a}from"react/jsx-runtime";import{useEffect as r,useMemo as s,useState as l}from"react";import{Building2 as n,CheckCircle2 as i,ClipboardCheck as d,Clock as o,DoorOpen as c,Globe as m,Loader2 as h,Lock as x,PlayCircle as g,Search as b,Timer as f,Users as p,XCircle as u}from"lucide-react";import{Badge as N}from"../../../components/ui/badge";import{Button as y}from"../../../components/ui/button";import{Input as w}from"../../../components/ui/input";import{Skeleton as v}from"../../../components/ui/skeleton";import{useConfirm as k}from"../../../components/ui/confirm-dialog";import{canCancelRegistration as C,canEnterRoom as D,canRetakeRoom as S,getCountdownParts as E,getRoomSortPriority as R,hasRegistrationStatus as P,isUserEngaged as T,needsRegisterAndEnter as j,needsRegistration as A,unwrap as I}from"./contest-rooms.utils";function L({targetDate:s}){const[n,i]=l(()=>E(s));return r(()=>{const e=setInterval(()=>i(E(s)),1e3);return()=>clearInterval(e)},[s]),n?t("div",{className:"flex items-center gap-1.5",children:[n.days>0&&t(a,{children:[t("div",{className:"flex min-w-[36px] flex-col items-center rounded-md bg-white/80 px-1.5 py-1 shadow-sm",children:[e("span",{className:"text-sm font-bold leading-none text-[#07aff0]",children:n.days}),e("span",{className:"text-[9px] text-gray-400",children:"ngày"})]}),e("span",{className:"text-sm font-bold text-[#07aff0]/40",children:":"})]}),t("div",{className:"flex min-w-[36px] flex-col items-center rounded-md bg-white/80 px-1.5 py-1 shadow-sm",children:[e("span",{className:"text-sm font-bold leading-none text-[#07aff0]",children:String(n.hours).padStart(2,"0")}),e("span",{className:"text-[9px] text-gray-400",children:"giờ"})]}),e("span",{className:"text-sm font-bold text-[#07aff0]/40",children:":"}),t("div",{className:"flex min-w-[36px] flex-col items-center rounded-md bg-white/80 px-1.5 py-1 shadow-sm",children:[e("span",{className:"text-sm font-bold leading-none text-[#07aff0]",children:String(n.minutes).padStart(2,"0")}),e("span",{className:"text-[9px] text-gray-400",children:"phút"})]}),e("span",{className:"text-sm font-bold text-[#07aff0]/40",children:":"}),t("div",{className:"flex min-w-[36px] flex-col items-center rounded-md bg-white/80 px-1.5 py-1 shadow-sm",children:[e("span",{className:"text-sm font-bold leading-none text-[#07aff0]",children:String(n.seconds).padStart(2,"0")}),e("span",{className:"text-[9px] text-gray-400",children:"giây"})]})]}):null}function O({room:a,contest:r}){const s=function(e,t){const a=S(e,t);return e.isCompleted&&!a?{label:"Đã hoàn thành",icon:i,className:"border-emerald-200 bg-emerald-50 text-emerald-700"}:e.hasAttempt&&"COMPLETED"===e.status?{label:"Đã thi",icon:i,className:"border-emerald-200 bg-emerald-50 text-emerald-700"}:e.hasAttempt&&"IN_PROGRESS"===e.attemptStatus?{label:"Đang thi",icon:g,className:"border-blue-200 bg-blue-50 text-blue-700"}:"internal"===e.visibility?"PENDING"===e.registrationStatus?{label:"Chờ phê duyệt",icon:o,className:"border-yellow-200 bg-yellow-50 text-yellow-700"}:"REJECTED"===e.registrationStatus?{label:"Bị từ chối",icon:u,className:"border-red-200 bg-red-50 text-red-700"}:"APPROVED"===e.registrationStatus?{label:"Đã đăng ký",icon:i,className:"border-teal-200 bg-teal-50 text-teal-700"}:{label:"Chưa đăng ký",icon:d,className:"border-gray-200 bg-gray-50 text-gray-500"}:e.hasAttempt&&!a?{label:"Đã thi",icon:i,className:"border-emerald-200 bg-emerald-50 text-emerald-700"}:e.hasAttempt&&a?{label:"Có thể thi lại",icon:c,className:"border-sky-200 bg-sky-50 text-sky-700"}:{label:"Sẵn sàng",icon:c,className:"border-sky-200 bg-sky-50 text-sky-700"}}(a,r),l=s.icon;return t(N,{variant:"outline",className:`rounded-full border-2 px-3 py-1 text-xs font-extrabold uppercase tracking-wide ${s.className}`,children:[e(l,{className:"mr-1 h-3.5 w-3.5"}),s.label]})}export function StudentContestRoomsPageContainer({contestId:g,api:N,onNavigate:E,enterPath:z=e=>`/student/exams/${e}/enter`}){const[G,M]=l(null),[V,B]=l(),[H,J]=l(!0),[$,_]=l(""),[F,K]=l("all"),[Q,U]=l(null),{confirm:X,ConfirmDialog:q}=k(),W=e=>E?E(e):window.location.href=e,Y=async()=>{J(!0);try{const[e,t]=await Promise.all([N.getContestRooms(g),N.getContestDetail(g)]);M(I(e)),B(I(t))}finally{J(!1)}};r(()=>{Y()},[g]);const Z=s(()=>{if(!G||0===G.length)return[];let e=[...G];if($.trim()){const t=$.trim().toLowerCase();e=e.filter(e=>e.name.toLowerCase().includes(t))}return"registered"===F&&(e=e.filter(e=>"APPROVED"===e.registrationStatus||"PENDING"===e.registrationStatus||e.hasAttempt)),e.sort((e,t)=>R(e)-R(t))},[G,$,F]);return H?e("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3",children:[1,2,3,4,5,6].map(a=>t("div",{className:"overflow-hidden rounded-xl border bg-white shadow-sm",children:[e(v,{className:"h-2 w-full"}),t("div",{className:"space-y-3 p-4",children:[e(v,{className:"h-5 w-3/4"}),e(v,{className:"h-4 w-1/2"}),e(v,{className:"h-4 w-2/3"}),t("div",{className:"flex gap-2 pt-2",children:[e(v,{className:"h-8 w-20"}),e(v,{className:"h-8 w-20"})]})]})]},a))}):G&&0!==G.length?t(a,{children:[t("div",{className:"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",children:[t("div",{className:"relative w-full sm:max-w-xs",children:[e(b,{className:"absolute left-3.5 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400"}),e(w,{type:"text",placeholder:"Tìm kiếm phòng thi...",value:$,onChange:e=>_(e.target.value),className:"h-11 rounded-2xl border-2 border-gray-200 pl-10 text-sm font-semibold focus-visible:border-[#07aff0]/40 focus-visible:ring-0"})]}),t("div",{className:"flex items-center gap-1.5 rounded-2xl border-2 border-gray-100 bg-gray-50 p-1.5",children:[e("button",{type:"button",onClick:()=>K("all"),className:"rounded-xl px-4 py-2 text-xs font-extrabold uppercase tracking-wide transition-all "+("all"===F?"border-2 border-b-4 border-[#07aff0]/30 bg-white text-[#07aff0]":"border-2 border-transparent text-gray-400 hover:text-[#07aff0]"),children:"Tất cả"}),e("button",{type:"button",onClick:()=>K("registered"),className:"rounded-xl px-4 py-2 text-xs font-extrabold uppercase tracking-wide transition-all "+("registered"===F?"border-2 border-b-4 border-[#07aff0]/30 bg-white text-[#07aff0]":"border-2 border-transparent text-gray-400 hover:text-[#07aff0]"),children:"Đã đăng ký"})]})]}),0===Z.length&&t("div",{className:"flex flex-col items-center justify-center rounded-2xl border-2 border-dashed border-gray-200 bg-gradient-to-br from-gray-50 to-white py-12",children:[e("div",{className:"flex h-16 w-16 items-center justify-center rounded-full bg-gray-100",children:e(b,{className:"h-8 w-8 text-gray-300"})}),e("p",{className:"mt-3 text-base font-medium text-gray-400",children:"Không tìm thấy phòng thi phù hợp"}),e("p",{className:"mt-1 text-sm text-gray-400",children:"Thử thay đổi từ khóa tìm kiếm hoặc bộ lọc"})]}),e("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3",children:Z.map(r=>{!function(e){if(e.isLocked)return{label:"Đã khóa",className:"border-red-200 bg-red-50 text-red-700",icon:x,canEnter:!1};switch(e.status){case"IN_PROGRESS":return{label:"Đang mở",className:"border-green-200 bg-green-50 text-green-700",icon:c,canEnter:!0};case"COMPLETED":return{label:"Đã kết thúc",className:"border-gray-200 bg-gray-50 text-gray-700",icon:o,canEnter:!1};default:;}}(r);const s=S(r,V),l=!(r.isLocked||"UPCOMING"!==r.status&&r.status),b=D(r,V),w=A(r),v=P(r),k=T(r),E=Q===r.id;return e("div",{className:"group relative flex flex-col overflow-hidden rounded-3xl border-2 border-b-[6px] bg-white shadow-sm transition-all duration-150 hover:-translate-y-0.5 "+(k?"border-[#07aff0]/30":"border-gray-200"),children:t("div",{className:"flex flex-1 flex-col p-5",children:[t("div",{className:"mb-3 flex items-start justify-between gap-3",children:[t("div",{className:"min-w-0 flex-1",children:[t("h3",{className:"flex items-center gap-2 truncate text-lg font-extrabold tracking-tight text-gray-800 group-hover:text-[#07aff0]",children:[(!r.visibility||"public"===r.visibility)&&e("span",{className:"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-xl bg-emerald-100",children:e(m,{className:"h-4 w-4 text-emerald-500"})}),"internal"===r.visibility&&e("span",{className:"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-xl bg-amber-100",children:e(n,{className:"h-4 w-4 text-amber-500"})}),"private"===r.visibility&&e("span",{className:"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-xl bg-gray-100",children:e(x,{className:"h-4 w-4 text-gray-400"})}),e("span",{className:"truncate",children:r.name})]}),t("p",{className:"mt-1 text-xs font-medium text-gray-400",children:["Mã: ",e("span",{className:"font-mono font-bold text-gray-500",children:r.code})]})]}),t("div",{className:"flex flex-shrink-0 items-start gap-2",children:[null!=r.registrationCount&&t("div",{className:"flex items-center gap-1 rounded-full bg-gray-100 px-2.5 py-1 text-gray-500",children:[e(p,{className:"h-3.5 w-3.5"}),e("span",{className:"text-xs font-bold",children:r.registrationCount})]}),r.startDatetime&&e("div",{className:"flex flex-col items-center overflow-hidden rounded-2xl border-2 border-gray-200 bg-white",children:t("div",{className:"flex flex-col items-center px-3 py-1.5",children:[e("span",{className:"text-xl font-extrabold leading-tight text-gray-800",children:new Date(r.startDatetime).getDate()}),t("span",{className:"text-[10px] font-bold uppercase text-gray-400",children:["Th ",new Date(r.startDatetime).getMonth()+1]})]})})]})]}),t("div",{className:"mb-3 space-y-2",children:[t("div",{className:"flex items-center gap-2 text-xs font-semibold text-gray-500",children:[e(d,{className:"h-4 w-4 flex-shrink-0 text-gray-400"}),e("span",{className:"truncate",children:r.examName})]}),r.startDatetime&&t("div",{className:"flex items-center gap-2 text-xs font-semibold text-gray-500",children:[e(o,{className:"h-4 w-4 flex-shrink-0 text-gray-400"}),t("span",{children:[new Date(r.startDatetime).toLocaleTimeString("vi-VN",{hour:"2-digit",minute:"2-digit"}),r.endDatetime&&t(a,{children:[" - ",new Date(r.endDatetime).toLocaleTimeString("vi-VN",{hour:"2-digit",minute:"2-digit"})]})]})]})]}),e("div",{className:"mb-3 flex flex-wrap items-center gap-1.5",children:e(O,{room:r,contest:V})}),!b&&!w&&!v&&l&&r.startDatetime&&t("div",{className:"mb-3 flex items-center gap-2 rounded-2xl border-2 border-[#07aff0]/15 bg-[#07aff0]/5 px-3 py-2",children:[e(f,{className:"h-4 w-4 flex-shrink-0 text-[#07aff0]"}),e("span",{className:"text-xs font-bold text-[#07aff0]",children:"Mở sau"}),e("div",{className:"ml-auto",children:e(L,{targetDate:r.startDatetime})})]}),e("div",{className:"mt-auto pt-3"}),t("div",{className:"flex flex-col gap-2",children:[t("div",{className:"flex flex-wrap items-center gap-2",children:[r.isCompleted&&!s&&t(y,{size:"sm",className:"h-11 flex-1 cursor-default rounded-2xl border-2 border-emerald-200 bg-emerald-50 text-sm font-extrabold uppercase tracking-wide text-emerald-600 hover:bg-emerald-50",disabled:!0,children:[e(i,{className:"mr-2 h-4 w-4"}),"Đã hoàn thành"]}),j(r,V)&&t(y,{size:"sm",className:"h-11 flex-1 rounded-2xl border-b-4 border-[#003d87] bg-[#07aff0] text-sm font-extrabold uppercase tracking-wide text-white shadow-none transition-all hover:bg-[#0060d0] active:translate-y-0.5 active:border-b-2",onClick:()=>(async e=>{U(e);try{await N.registerRoom(g,e),W(z(e))}finally{U(null)}})(r.id),disabled:E,children:[E?e(h,{className:"mr-2 h-4 w-4 animate-spin"}):e(c,{className:"mr-2 h-4 w-4"}),r.hasAttempt?"Đăng kí và Thi lại":"Vào phòng thi"]}),b&&!j(r,V)&&t("div",{className:"flex-1 flex flex-col gap-1.5",children:[r.hasAttempt&&e("div",{className:"rounded-xl border-2 border-amber-200/60 bg-amber-50 px-2 py-1 text-center text-xs font-bold text-amber-600",children:"Bạn đã hoàn thành bài thi"}),t(y,{size:"sm",className:"h-11 w-full rounded-2xl border-0 border-b-4 border-[#05a0db] bg-[#07aff0] text-sm font-extrabold uppercase tracking-wide text-white shadow-none transition-all hover:bg-[#06a0e0] active:translate-y-0.5 active:border-b-2",onClick:()=>W(z(r.id)),children:[e(c,{className:"mr-2 h-4 w-4"}),r.hasAttempt?"Thi lại":"Vào phòng thi"]})]}),w&&t(y,{size:"sm",className:"h-11 flex-1 rounded-2xl border-2 border-b-4 border-[#07aff0]/30 bg-white text-sm font-extrabold uppercase tracking-wide text-[#07aff0] shadow-none transition-all hover:bg-[#07aff0]/5 active:translate-y-0.5 active:border-b-2",onClick:()=>(async e=>{U(e);try{await N.registerRoom(g,e),await Y()}finally{U(null)}})(r.id),disabled:E,children:[E?e(h,{className:"mr-2 h-4 w-4 animate-spin"}):e(d,{className:"mr-2 h-4 w-4"}),"Đăng ký"]}),v&&"PENDING"===r.registrationStatus&&t(y,{size:"sm",className:"h-11 flex-1 cursor-default rounded-2xl border-2 border-yellow-200 bg-yellow-50 text-sm font-extrabold uppercase tracking-wide text-yellow-600 hover:bg-yellow-50",disabled:!0,children:[e(o,{className:"mr-2 h-4 w-4"}),"Đã gửi đăng ký"]}),v&&"REJECTED"===r.registrationStatus&&t(y,{size:"sm",className:"h-11 flex-1 cursor-default rounded-2xl border-2 border-red-200 bg-red-50 text-sm font-extrabold uppercase tracking-wide text-red-600 hover:bg-red-50",disabled:!0,children:[e(u,{className:"mr-2 h-4 w-4"}),"Bị từ chối"]})]}),C(r)&&e("button",{type:"button",className:"self-center text-[11px] font-semibold text-gray-400 underline-offset-2 hover:text-red-500 hover:underline transition-colors disabled:opacity-50",onClick:()=>(async e=>{if(await X({title:"Xác nhận hủy đăng ký",description:"Bạn có chắc chắn muốn hủy đăng ký phòng thi này không? Hành động này không thể hoàn tác.",confirmText:"Hủy đăng ký",cancelText:"Quay lại",variant:"destructive"})&&N.cancelRoomRegistration){U(e);try{await N.cancelRoomRegistration(g,e),await Y()}finally{U(null)}}})(r.id),disabled:E,children:E?"Đang hủy...":"Hủy đăng ký"})]})]})},r.id)})}),e(q,{})]}):t("div",{className:"flex flex-col items-center justify-center rounded-2xl border-2 border-dashed border-gray-200 bg-gradient-to-br from-gray-50 to-white py-16",children:[e("div",{className:"flex h-20 w-20 items-center justify-center rounded-full bg-gray-100",children:e(c,{className:"h-10 w-10 text-gray-300"})}),e("p",{className:"mt-4 text-lg font-medium text-gray-400",children:"Chưa có phòng thi nào trong kỳ thi này"})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useEffect, useMemo, useState } from 'react';
4
+ import { Building2, CheckCircle2, ClipboardCheck, Clock, DoorOpen, Globe, Loader2, Lock, PlayCircle, Search, Timer, Users, XCircle, } from 'lucide-react';
5
+ import { Badge } from '../../../components/ui/badge';
6
+ import { Button } from '../../../components/ui/button';
7
+ import { Input } from '../../../components/ui/input';
8
+ import { Skeleton } from '../../../components/ui/skeleton';
9
+ import { useConfirm } from '../../../components/ui/confirm-dialog';
10
+ import { canCancelRegistration, canEnterRoom, canRetakeRoom, getCountdownParts, getRoomSortPriority, hasRegistrationStatus, isUserEngaged, needsRegisterAndEnter, needsRegistration, unwrap, } from './contest-rooms.utils';
11
+ function CountdownTimer({ targetDate }) {
12
+ const [parts, setParts] = useState(() => getCountdownParts(targetDate));
13
+ useEffect(() => {
14
+ const interval = setInterval(() => setParts(getCountdownParts(targetDate)), 1000);
15
+ return () => clearInterval(interval);
16
+ }, [targetDate]);
17
+ if (!parts)
18
+ return null;
19
+ return (_jsxs("div", { className: "flex items-center gap-1.5", children: [parts.days > 0 && (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex min-w-[36px] flex-col items-center rounded-md bg-white/80 px-1.5 py-1 shadow-sm", children: [_jsx("span", { className: "text-sm font-bold leading-none text-[#07aff0]", children: parts.days }), _jsx("span", { className: "text-[9px] text-gray-400", children: "ng\u00E0y" })] }), _jsx("span", { className: "text-sm font-bold text-[#07aff0]/40", children: ":" })] })), _jsxs("div", { className: "flex min-w-[36px] flex-col items-center rounded-md bg-white/80 px-1.5 py-1 shadow-sm", children: [_jsx("span", { className: "text-sm font-bold leading-none text-[#07aff0]", children: String(parts.hours).padStart(2, '0') }), _jsx("span", { className: "text-[9px] text-gray-400", children: "gi\u1EDD" })] }), _jsx("span", { className: "text-sm font-bold text-[#07aff0]/40", children: ":" }), _jsxs("div", { className: "flex min-w-[36px] flex-col items-center rounded-md bg-white/80 px-1.5 py-1 shadow-sm", children: [_jsx("span", { className: "text-sm font-bold leading-none text-[#07aff0]", children: String(parts.minutes).padStart(2, '0') }), _jsx("span", { className: "text-[9px] text-gray-400", children: "ph\u00FAt" })] }), _jsx("span", { className: "text-sm font-bold text-[#07aff0]/40", children: ":" }), _jsxs("div", { className: "flex min-w-[36px] flex-col items-center rounded-md bg-white/80 px-1.5 py-1 shadow-sm", children: [_jsx("span", { className: "text-sm font-bold leading-none text-[#07aff0]", children: String(parts.seconds).padStart(2, '0') }), _jsx("span", { className: "text-[9px] text-gray-400", children: "gi\u00E2y" })] })] }));
20
+ }
21
+ function getStatusInfo(room) {
22
+ if (room.isLocked) {
23
+ return { label: 'Đã khóa', className: 'border-red-200 bg-red-50 text-red-700', icon: Lock, canEnter: false };
24
+ }
25
+ switch (room.status) {
26
+ case 'IN_PROGRESS':
27
+ return { label: 'Đang mở', className: 'border-green-200 bg-green-50 text-green-700', icon: DoorOpen, canEnter: true };
28
+ case 'COMPLETED':
29
+ return { label: 'Đã kết thúc', className: 'border-gray-200 bg-gray-50 text-gray-700', icon: Clock, canEnter: false };
30
+ case 'UPCOMING':
31
+ default:
32
+ return { label: 'Phòng thi chưa mở', className: 'border-blue-200 bg-blue-50 text-blue-700', icon: Clock, canEnter: false };
33
+ }
34
+ }
35
+ function getUserStatus(room, contest) {
36
+ const retake = canRetakeRoom(room, contest);
37
+ if (room.isCompleted && !retake) {
38
+ return { label: 'Đã hoàn thành', icon: CheckCircle2, className: 'border-emerald-200 bg-emerald-50 text-emerald-700' };
39
+ }
40
+ if (room.hasAttempt && room.status === 'COMPLETED') {
41
+ return { label: 'Đã thi', icon: CheckCircle2, className: 'border-emerald-200 bg-emerald-50 text-emerald-700' };
42
+ }
43
+ if (room.hasAttempt && room.attemptStatus === 'IN_PROGRESS') {
44
+ return { label: 'Đang thi', icon: PlayCircle, className: 'border-blue-200 bg-blue-50 text-blue-700' };
45
+ }
46
+ if (room.visibility === 'internal') {
47
+ if (room.registrationStatus === 'PENDING') {
48
+ return { label: 'Chờ phê duyệt', icon: Clock, className: 'border-yellow-200 bg-yellow-50 text-yellow-700' };
49
+ }
50
+ if (room.registrationStatus === 'REJECTED') {
51
+ return { label: 'Bị từ chối', icon: XCircle, className: 'border-red-200 bg-red-50 text-red-700' };
52
+ }
53
+ if (room.registrationStatus === 'APPROVED') {
54
+ return { label: 'Đã đăng ký', icon: CheckCircle2, className: 'border-teal-200 bg-teal-50 text-teal-700' };
55
+ }
56
+ return { label: 'Chưa đăng ký', icon: ClipboardCheck, className: 'border-gray-200 bg-gray-50 text-gray-500' };
57
+ }
58
+ if (room.hasAttempt && !retake) {
59
+ return { label: 'Đã thi', icon: CheckCircle2, className: 'border-emerald-200 bg-emerald-50 text-emerald-700' };
60
+ }
61
+ if (room.hasAttempt && retake) {
62
+ return { label: 'Có thể thi lại', icon: DoorOpen, className: 'border-sky-200 bg-sky-50 text-sky-700' };
63
+ }
64
+ return { label: 'Sẵn sàng', icon: DoorOpen, className: 'border-sky-200 bg-sky-50 text-sky-700' };
65
+ }
66
+ function UserStatusBadge({ room, contest }) {
67
+ const status = getUserStatus(room, contest);
68
+ const Icon = status.icon;
69
+ return (_jsxs(Badge, { variant: "outline", className: `rounded-full border-2 px-3 py-1 text-xs font-extrabold uppercase tracking-wide ${status.className}`, children: [_jsx(Icon, { className: "mr-1 h-3.5 w-3.5" }), status.label] }));
70
+ }
71
+ /**
72
+ * Renders the student contest rooms-list screen
73
+ * (`/student/contests/:contestId/rooms`). Fetches rooms + contest detail via
74
+ * the injected `api`, handles search/filter, room registration and
75
+ * cancellation, and navigates to the exam-entry screen on room selection.
76
+ */
77
+ export function StudentContestRoomsPageContainer({ contestId, api, onNavigate, enterPath = (roomId) => `/student/exams/${roomId}/enter`, }) {
78
+ const [rooms, setRooms] = useState(null);
79
+ const [contest, setContest] = useState();
80
+ const [isLoading, setIsLoading] = useState(true);
81
+ const [searchQuery, setSearchQuery] = useState('');
82
+ const [roomFilter, setRoomFilter] = useState('all');
83
+ const [pendingRoomId, setPendingRoomId] = useState(null);
84
+ const { confirm, ConfirmDialog } = useConfirm();
85
+ const navigate = (href) => (onNavigate ? onNavigate(href) : (window.location.href = href));
86
+ const loadData = async () => {
87
+ setIsLoading(true);
88
+ try {
89
+ const [roomsResult, contestResult] = await Promise.all([
90
+ api.getContestRooms(contestId),
91
+ api.getContestDetail(contestId),
92
+ ]);
93
+ setRooms(unwrap(roomsResult));
94
+ setContest(unwrap(contestResult));
95
+ }
96
+ finally {
97
+ setIsLoading(false);
98
+ }
99
+ };
100
+ useEffect(() => {
101
+ loadData();
102
+ // eslint-disable-next-line react-hooks/exhaustive-deps
103
+ }, [contestId]);
104
+ const handleRegister = async (roomId) => {
105
+ setPendingRoomId(roomId);
106
+ try {
107
+ await api.registerRoom(contestId, roomId);
108
+ await loadData();
109
+ }
110
+ finally {
111
+ setPendingRoomId(null);
112
+ }
113
+ };
114
+ const handleRegisterAndEnter = async (roomId) => {
115
+ setPendingRoomId(roomId);
116
+ try {
117
+ await api.registerRoom(contestId, roomId);
118
+ navigate(enterPath(roomId));
119
+ }
120
+ finally {
121
+ setPendingRoomId(null);
122
+ }
123
+ };
124
+ const handleCancelRegistration = async (roomId) => {
125
+ const confirmed = await confirm({
126
+ title: 'Xác nhận hủy đăng ký',
127
+ description: 'Bạn có chắc chắn muốn hủy đăng ký phòng thi này không? Hành động này không thể hoàn tác.',
128
+ confirmText: 'Hủy đăng ký',
129
+ cancelText: 'Quay lại',
130
+ variant: 'destructive',
131
+ });
132
+ if (!confirmed || !api.cancelRoomRegistration)
133
+ return;
134
+ setPendingRoomId(roomId);
135
+ try {
136
+ await api.cancelRoomRegistration(contestId, roomId);
137
+ await loadData();
138
+ }
139
+ finally {
140
+ setPendingRoomId(null);
141
+ }
142
+ };
143
+ const filteredAndSortedRooms = useMemo(() => {
144
+ if (!rooms || rooms.length === 0)
145
+ return [];
146
+ let filtered = [...rooms];
147
+ if (searchQuery.trim()) {
148
+ const query = searchQuery.trim().toLowerCase();
149
+ filtered = filtered.filter((room) => room.name.toLowerCase().includes(query));
150
+ }
151
+ if (roomFilter === 'registered') {
152
+ filtered = filtered.filter((room) => room.registrationStatus === 'APPROVED' || room.registrationStatus === 'PENDING' || room.hasAttempt);
153
+ }
154
+ return filtered.sort((a, b) => getRoomSortPriority(a) - getRoomSortPriority(b));
155
+ }, [rooms, searchQuery, roomFilter]);
156
+ if (isLoading) {
157
+ return (_jsx("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3", children: [1, 2, 3, 4, 5, 6].map((i) => (_jsxs("div", { className: "overflow-hidden rounded-xl border bg-white shadow-sm", children: [_jsx(Skeleton, { className: "h-2 w-full" }), _jsxs("div", { className: "space-y-3 p-4", children: [_jsx(Skeleton, { className: "h-5 w-3/4" }), _jsx(Skeleton, { className: "h-4 w-1/2" }), _jsx(Skeleton, { className: "h-4 w-2/3" }), _jsxs("div", { className: "flex gap-2 pt-2", children: [_jsx(Skeleton, { className: "h-8 w-20" }), _jsx(Skeleton, { className: "h-8 w-20" })] })] })] }, i))) }));
158
+ }
159
+ if (!rooms || rooms.length === 0) {
160
+ return (_jsxs("div", { className: "flex flex-col items-center justify-center rounded-2xl border-2 border-dashed border-gray-200 bg-gradient-to-br from-gray-50 to-white py-16", children: [_jsx("div", { className: "flex h-20 w-20 items-center justify-center rounded-full bg-gray-100", children: _jsx(DoorOpen, { className: "h-10 w-10 text-gray-300" }) }), _jsx("p", { className: "mt-4 text-lg font-medium text-gray-400", children: "Ch\u01B0a c\u00F3 ph\u00F2ng thi n\u00E0o trong k\u1EF3 thi n\u00E0y" })] }));
161
+ }
162
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "relative w-full sm:max-w-xs", children: [_jsx(Search, { className: "absolute left-3.5 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400" }), _jsx(Input, { type: "text", placeholder: "T\u00ECm ki\u1EBFm ph\u00F2ng thi...", value: searchQuery, onChange: (e) => setSearchQuery(e.target.value), className: "h-11 rounded-2xl border-2 border-gray-200 pl-10 text-sm font-semibold focus-visible:border-[#07aff0]/40 focus-visible:ring-0" })] }), _jsxs("div", { className: "flex items-center gap-1.5 rounded-2xl border-2 border-gray-100 bg-gray-50 p-1.5", children: [_jsx("button", { type: "button", onClick: () => setRoomFilter('all'), className: `rounded-xl px-4 py-2 text-xs font-extrabold uppercase tracking-wide transition-all ${roomFilter === 'all'
163
+ ? 'border-2 border-b-4 border-[#07aff0]/30 bg-white text-[#07aff0]'
164
+ : 'border-2 border-transparent text-gray-400 hover:text-[#07aff0]'}`, children: "T\u1EA5t c\u1EA3" }), _jsx("button", { type: "button", onClick: () => setRoomFilter('registered'), className: `rounded-xl px-4 py-2 text-xs font-extrabold uppercase tracking-wide transition-all ${roomFilter === 'registered'
165
+ ? 'border-2 border-b-4 border-[#07aff0]/30 bg-white text-[#07aff0]'
166
+ : 'border-2 border-transparent text-gray-400 hover:text-[#07aff0]'}`, children: "\u0110\u00E3 \u0111\u0103ng k\u00FD" })] })] }), filteredAndSortedRooms.length === 0 && (_jsxs("div", { className: "flex flex-col items-center justify-center rounded-2xl border-2 border-dashed border-gray-200 bg-gradient-to-br from-gray-50 to-white py-12", children: [_jsx("div", { className: "flex h-16 w-16 items-center justify-center rounded-full bg-gray-100", children: _jsx(Search, { className: "h-8 w-8 text-gray-300" }) }), _jsx("p", { className: "mt-3 text-base font-medium text-gray-400", children: "Kh\u00F4ng t\u00ECm th\u1EA5y ph\u00F2ng thi ph\u00F9 h\u1EE3p" }), _jsx("p", { className: "mt-1 text-sm text-gray-400", children: "Th\u1EED thay \u0111\u1ED5i t\u1EEB kh\u00F3a t\u00ECm ki\u1EBFm ho\u1EB7c b\u1ED9 l\u1ECDc" })] })), _jsx("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3", children: filteredAndSortedRooms.map((room) => {
167
+ const statusInfo = getStatusInfo(room);
168
+ const retake = canRetakeRoom(room, contest);
169
+ const isUpcoming = !room.isLocked && (room.status === 'UPCOMING' || !room.status);
170
+ const canEnter = canEnterRoom(room, contest);
171
+ const showRegisterBtn = needsRegistration(room);
172
+ const showRegStatus = hasRegistrationStatus(room);
173
+ const engaged = isUserEngaged(room);
174
+ const isPending = pendingRoomId === room.id;
175
+ return (_jsx("div", { className: `group relative flex flex-col overflow-hidden rounded-3xl border-2 border-b-[6px] bg-white shadow-sm transition-all duration-150 hover:-translate-y-0.5 ${engaged ? 'border-[#07aff0]/30' : 'border-gray-200'}`, children: _jsxs("div", { className: "flex flex-1 flex-col p-5", children: [_jsxs("div", { className: "mb-3 flex items-start justify-between gap-3", children: [_jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("h3", { className: "flex items-center gap-2 truncate text-lg font-extrabold tracking-tight text-gray-800 group-hover:text-[#07aff0]", children: [(!room.visibility || room.visibility === 'public') && (_jsx("span", { className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-xl bg-emerald-100", children: _jsx(Globe, { className: "h-4 w-4 text-emerald-500" }) })), room.visibility === 'internal' && (_jsx("span", { className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-xl bg-amber-100", children: _jsx(Building2, { className: "h-4 w-4 text-amber-500" }) })), room.visibility === 'private' && (_jsx("span", { className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-xl bg-gray-100", children: _jsx(Lock, { className: "h-4 w-4 text-gray-400" }) })), _jsx("span", { className: "truncate", children: room.name })] }), _jsxs("p", { className: "mt-1 text-xs font-medium text-gray-400", children: ["M\u00E3: ", _jsx("span", { className: "font-mono font-bold text-gray-500", children: room.code })] })] }), _jsxs("div", { className: "flex flex-shrink-0 items-start gap-2", children: [room.registrationCount != null && (_jsxs("div", { className: "flex items-center gap-1 rounded-full bg-gray-100 px-2.5 py-1 text-gray-500", children: [_jsx(Users, { className: "h-3.5 w-3.5" }), _jsx("span", { className: "text-xs font-bold", children: room.registrationCount })] })), room.startDatetime && (_jsx("div", { className: "flex flex-col items-center overflow-hidden rounded-2xl border-2 border-gray-200 bg-white", children: _jsxs("div", { className: "flex flex-col items-center px-3 py-1.5", children: [_jsx("span", { className: "text-xl font-extrabold leading-tight text-gray-800", children: new Date(room.startDatetime).getDate() }), _jsxs("span", { className: "text-[10px] font-bold uppercase text-gray-400", children: ["Th ", new Date(room.startDatetime).getMonth() + 1] })] }) }))] })] }), _jsxs("div", { className: "mb-3 space-y-2", children: [_jsxs("div", { className: "flex items-center gap-2 text-xs font-semibold text-gray-500", children: [_jsx(ClipboardCheck, { className: "h-4 w-4 flex-shrink-0 text-gray-400" }), _jsx("span", { className: "truncate", children: room.examName })] }), room.startDatetime && (_jsxs("div", { className: "flex items-center gap-2 text-xs font-semibold text-gray-500", children: [_jsx(Clock, { className: "h-4 w-4 flex-shrink-0 text-gray-400" }), _jsxs("span", { children: [new Date(room.startDatetime).toLocaleTimeString('vi-VN', {
176
+ hour: '2-digit',
177
+ minute: '2-digit',
178
+ }), room.endDatetime && (_jsxs(_Fragment, { children: [' - ', new Date(room.endDatetime).toLocaleTimeString('vi-VN', {
179
+ hour: '2-digit',
180
+ minute: '2-digit',
181
+ })] }))] })] }))] }), _jsx("div", { className: "mb-3 flex flex-wrap items-center gap-1.5", children: _jsx(UserStatusBadge, { room: room, contest: contest }) }), !canEnter && !showRegisterBtn && !showRegStatus && isUpcoming && room.startDatetime && (_jsxs("div", { className: "mb-3 flex items-center gap-2 rounded-2xl border-2 border-[#07aff0]/15 bg-[#07aff0]/5 px-3 py-2", children: [_jsx(Timer, { className: "h-4 w-4 flex-shrink-0 text-[#07aff0]" }), _jsx("span", { className: "text-xs font-bold text-[#07aff0]", children: "M\u1EDF sau" }), _jsx("div", { className: "ml-auto", children: _jsx(CountdownTimer, { targetDate: room.startDatetime }) })] })), _jsx("div", { className: "mt-auto pt-3" }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [room.isCompleted && !retake && (_jsxs(Button, { size: "sm", className: "h-11 flex-1 cursor-default rounded-2xl border-2 border-emerald-200 bg-emerald-50 text-sm font-extrabold uppercase tracking-wide text-emerald-600 hover:bg-emerald-50", disabled: true, children: [_jsx(CheckCircle2, { className: "mr-2 h-4 w-4" }), "\u0110\u00E3 ho\u00E0n th\u00E0nh"] })), needsRegisterAndEnter(room, contest) && (_jsxs(Button, { size: "sm", className: "h-11 flex-1 rounded-2xl border-b-4 border-[#003d87] bg-[#07aff0] text-sm font-extrabold uppercase tracking-wide text-white shadow-none transition-all hover:bg-[#0060d0] active:translate-y-0.5 active:border-b-2", onClick: () => handleRegisterAndEnter(room.id), disabled: isPending, children: [isPending ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(DoorOpen, { className: "mr-2 h-4 w-4" })), room.hasAttempt ? 'Đăng kí và Thi lại' : 'Vào phòng thi'] })), canEnter && !needsRegisterAndEnter(room, contest) && (_jsxs("div", { className: "flex-1 flex flex-col gap-1.5", children: [room.hasAttempt && (_jsx("div", { className: "rounded-xl border-2 border-amber-200/60 bg-amber-50 px-2 py-1 text-center text-xs font-bold text-amber-600", children: "B\u1EA1n \u0111\u00E3 ho\u00E0n th\u00E0nh b\u00E0i thi" })), _jsxs(Button, { size: "sm", className: "h-11 w-full rounded-2xl border-0 border-b-4 border-[#05a0db] bg-[#07aff0] text-sm font-extrabold uppercase tracking-wide text-white shadow-none transition-all hover:bg-[#06a0e0] active:translate-y-0.5 active:border-b-2", onClick: () => navigate(enterPath(room.id)), children: [_jsx(DoorOpen, { className: "mr-2 h-4 w-4" }), room.hasAttempt ? 'Thi lại' : 'Vào phòng thi'] })] })), showRegisterBtn && (_jsxs(Button, { size: "sm", className: "h-11 flex-1 rounded-2xl border-2 border-b-4 border-[#07aff0]/30 bg-white text-sm font-extrabold uppercase tracking-wide text-[#07aff0] shadow-none transition-all hover:bg-[#07aff0]/5 active:translate-y-0.5 active:border-b-2", onClick: () => handleRegister(room.id), disabled: isPending, children: [isPending ? (_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" })) : (_jsx(ClipboardCheck, { className: "mr-2 h-4 w-4" })), "\u0110\u0103ng k\u00FD"] })), showRegStatus && room.registrationStatus === 'PENDING' && (_jsxs(Button, { size: "sm", className: "h-11 flex-1 cursor-default rounded-2xl border-2 border-yellow-200 bg-yellow-50 text-sm font-extrabold uppercase tracking-wide text-yellow-600 hover:bg-yellow-50", disabled: true, children: [_jsx(Clock, { className: "mr-2 h-4 w-4" }), "\u0110\u00E3 g\u1EEDi \u0111\u0103ng k\u00FD"] })), showRegStatus && room.registrationStatus === 'REJECTED' && (_jsxs(Button, { size: "sm", className: "h-11 flex-1 cursor-default rounded-2xl border-2 border-red-200 bg-red-50 text-sm font-extrabold uppercase tracking-wide text-red-600 hover:bg-red-50", disabled: true, children: [_jsx(XCircle, { className: "mr-2 h-4 w-4" }), "B\u1ECB t\u1EEB ch\u1ED1i"] }))] }), canCancelRegistration(room) && (_jsx("button", { type: "button", className: "self-center text-[11px] font-semibold text-gray-400 underline-offset-2 hover:text-red-500 hover:underline transition-colors disabled:opacity-50", onClick: () => handleCancelRegistration(room.id), disabled: isPending, children: isPending ? 'Đang hủy...' : 'Hủy đăng ký' }))] })] }) }, room.id));
182
+ }) }), _jsx(ConfirmDialog, {})] }));
183
+ }