@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,76 @@
1
- "use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import{useEffect as t,useState as l}from"react";import{BookOpen as s,ChevronLeft as n,ChevronRight as i,Clock as r,Loader2 as c,Search as o,Users as m}from"lucide-react";import{Button as d}from"../../components/ui/button";import{Card as p,CardContent as g,CardDescription as h,CardHeader as x,CardTitle as u}from"../../components/ui/card";import{Input as N}from"../../components/ui/input";import{useT as f}from"../../shared/lib/i18n";import{mapTemplateDetailToExamTemplate as v}from"../../api/exam-create";export function TemplateSelector({api:s,onTemplateSelect:r,onCancel:m}){const p=f(),[g,h]=l(""),[x,u]=l(1),[b,y]=l([]),[k,j]=l(1),[C,E]=l(0),[T,L]=l(!0),[S,D]=l(),[P,q]=l();t(()=>{u(1)},[g]),t(()=>{let e=!1;return L(!0),s.getExamTemplates({page:x,search:g||void 0}).then(a=>{e||(y(a.items),j(a.totalPages),E(a.totalItems))}).catch(a=>{e||D(a instanceof Error?a.message:p("admin.exams.questions.loadError"))}).finally(()=>{e||L(!1)}),()=>{e=!0}},[s,x,g,p]);const z=async e=>{D(void 0),q(e.id);try{const t="data"in(a=await s.getExamTemplateDetail(e.id))?a.data:a;r(v(t))}catch(e){D(e instanceof Error?e.message:p("admin.exams.questions.loadError"))}finally{q(void 0)}var a},A=x>1&&!T,I=x<k&&!T;return a("div",{className:"flex min-h-0 flex-1 flex-col gap-4",children:[a("div",{className:"relative",children:[e(o,{className:"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400"}),e(N,{value:g,onChange:e=>h(e.target.value),placeholder:p("admin.templates.searchPlaceholder"),className:"pl-9"})]}),T?e("div",{className:"flex flex-1 items-center justify-center",children:e(c,{className:"h-6 w-6 animate-spin text-blue-600"})}):null,!T&&S?e("p",{className:"text-sm text-red-600",children:S}):null,T||S?null:e("div",{className:"grid flex-1 content-start gap-3 overflow-y-auto md:grid-cols-2 lg:grid-cols-3",children:b.map((a,t)=>e(w,{template:a,index:t,onSelect:z,isLoading:P===a.id,disabled:!!P},a.id))}),T||S||0!==b.length?null:e("p",{className:"flex-1 text-center text-sm text-gray-500",children:p("admin.templates.table.empty")}),!T&&!S&&C>0?a("div",{className:"flex shrink-0 items-center justify-between border-t border-gray-100 pt-3",children:[e("span",{className:"text-sm text-gray-500",children:p("admin.templates.table.showing",{count:b.length,total:C})}),a("div",{className:"flex items-center gap-2",children:[a(d,{variant:"outline",size:"sm",disabled:!A,onClick:()=>u(e=>Math.max(1,e-1)),children:[e(n,{className:"h-4 w-4"}),p("common.pagination.previous")]}),e("span",{className:"text-sm text-gray-600",children:p("admin.schedules.list.page",{current:x,total:k})}),a(d,{variant:"outline",size:"sm",disabled:!I,onClick:()=>u(e=>e+1),children:[p("common.pagination.next"),e(i,{className:"h-4 w-4"})]})]})]}):null,m&&e("div",{className:"flex shrink-0 justify-end",children:e(d,{variant:"outline",onClick:m,children:p("common.cancel")})})]})}const b=["blue","green","purple","orange","pink","indigo"],y={blue:"bg-blue-100 text-blue-600",green:"bg-green-100 text-green-600",purple:"bg-purple-100 text-purple-600",orange:"bg-orange-100 text-orange-600",pink:"bg-pink-100 text-pink-600",indigo:"bg-indigo-100 text-indigo-600"};function w({template:t,index:l,onSelect:n,isLoading:i,disabled:o}){const d=b[l%b.length];return a(p,{className:"relative cursor-pointer border-2 border-gray-200 bg-white transition-all duration-200 hover:border-gray-300 hover:bg-gray-50 hover:shadow-md "+(o?"pointer-events-none opacity-60":""),onClick:()=>n(t),children:[e(x,{className:"px-4 py-3",children:a("div",{className:"flex items-center gap-2",children:[e("div",{className:`rounded-md p-1.5 ${y[d]}`,children:e(s,{className:"h-4 w-4"})}),a("div",{className:"min-w-0 flex-1",children:[e(u,{className:"truncate text-sm font-semibold",children:t.title}),t.subTitle&&e(h,{className:"truncate text-[11px]",children:t.subTitle})]})]})}),e(g,{className:"space-y-2 px-4 pb-3 pt-0",children:a("div",{className:"grid grid-cols-2 gap-x-3 gap-y-1 text-xs",children:[a("div",{className:"flex items-center gap-1.5",children:[e(s,{className:"h-3 w-3 text-gray-500"}),a("span",{className:"text-gray-600",children:[t.totalQuestions," câu hỏi"]})]}),a("div",{className:"flex items-center gap-1.5",children:[e("div",{className:"h-3 w-3 rounded-full bg-gray-300"}),a("span",{className:"text-gray-600",children:[t.totalParts," phần"]})]}),t.examLevel&&a("div",{className:"flex items-center gap-1.5",children:[e(m,{className:"h-3 w-3 text-gray-500"}),a("span",{className:"text-gray-600",children:["Cấp độ ",t.examLevel]})]}),t.createdAt&&a("div",{className:"flex items-center gap-1.5",children:[e(r,{className:"h-3 w-3 text-gray-500"}),e("span",{className:"text-gray-600",children:new Date(t.createdAt).toLocaleDateString("vi-VN")})]})]})}),i?e("div",{className:"absolute inset-0 flex items-center justify-center rounded-xl bg-white/60",children:e(c,{className:"h-5 w-5 animate-spin text-blue-600"})}):null]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from 'react';
4
+ import { BookOpen, ChevronLeft, ChevronRight, Clock, Loader2, Search, Users } from 'lucide-react';
5
+ import { Button } from '../../components/ui/button';
6
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../components/ui/card';
7
+ import { Input } from '../../components/ui/input';
8
+ import { useT } from '../../shared/lib/i18n';
9
+ import { mapTemplateDetailToExamTemplate } from '../../api/exam-create';
10
+ export function TemplateSelector({ api, onTemplateSelect, onCancel }) {
11
+ const t = useT();
12
+ const [search, setSearch] = useState('');
13
+ const [page, setPage] = useState(1);
14
+ const [templates, setTemplates] = useState([]);
15
+ const [totalPages, setTotalPages] = useState(1);
16
+ const [totalItems, setTotalItems] = useState(0);
17
+ const [isLoading, setIsLoading] = useState(true);
18
+ const [error, setError] = useState();
19
+ const [loadingId, setLoadingId] = useState();
20
+ // Reset to the first page whenever the search term changes so results stay in sync.
21
+ useEffect(() => { setPage(1); }, [search]);
22
+ useEffect(() => {
23
+ let ignore = false;
24
+ setIsLoading(true);
25
+ api.getExamTemplates({ page, search: search || undefined })
26
+ .then((result) => {
27
+ if (ignore)
28
+ return;
29
+ setTemplates(result.items);
30
+ setTotalPages(result.totalPages);
31
+ setTotalItems(result.totalItems);
32
+ })
33
+ .catch((err) => { if (!ignore)
34
+ setError(err instanceof Error ? err.message : t('admin.exams.questions.loadError')); })
35
+ .finally(() => { if (!ignore)
36
+ setIsLoading(false); });
37
+ return () => { ignore = true; };
38
+ }, [api, page, search, t]);
39
+ // The list endpoint returns lightweight summaries (no `parts`). Fetch the full template detail by
40
+ // id and map it to an `ExamTemplate` before emitting, so downstream (TemplateSection /
41
+ // buildExamTemplatePayload) gets a complete entity. Ported from oe-exam-fe TemplateSelector.
42
+ const handleSelect = async (summary) => {
43
+ setError(undefined);
44
+ setLoadingId(summary.id);
45
+ try {
46
+ const detail = unwrapDetail(await api.getExamTemplateDetail(summary.id));
47
+ onTemplateSelect(mapTemplateDetailToExamTemplate(detail));
48
+ }
49
+ catch (err) {
50
+ setError(err instanceof Error ? err.message : t('admin.exams.questions.loadError'));
51
+ }
52
+ finally {
53
+ setLoadingId(undefined);
54
+ }
55
+ };
56
+ const canPrev = page > 1 && !isLoading;
57
+ const canNext = page < totalPages && !isLoading;
58
+ return (_jsxs("div", { className: "flex min-h-0 flex-1 flex-col gap-4", children: [_jsxs("div", { className: "relative", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400" }), _jsx(Input, { value: search, onChange: (event) => setSearch(event.target.value), placeholder: t('admin.templates.searchPlaceholder'), className: "pl-9" })] }), isLoading ? _jsx("div", { className: "flex flex-1 items-center justify-center", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-blue-600" }) }) : null, !isLoading && error ? _jsx("p", { className: "text-sm text-red-600", children: error }) : null, !isLoading && !error ? _jsx("div", { className: "grid flex-1 content-start gap-3 overflow-y-auto md:grid-cols-2 lg:grid-cols-3", children: templates.map((template, index) => _jsx(TemplateCard, { template: template, index: index, onSelect: handleSelect, isLoading: loadingId === template.id, disabled: !!loadingId }, template.id)) }) : null, !isLoading && !error && templates.length === 0 ? _jsx("p", { className: "flex-1 text-center text-sm text-gray-500", children: t('admin.templates.table.empty') }) : null, !isLoading && !error && totalItems > 0 ? (_jsxs("div", { className: "flex shrink-0 items-center justify-between border-t border-gray-100 pt-3", children: [_jsx("span", { className: "text-sm text-gray-500", children: t('admin.templates.table.showing', { count: templates.length, total: totalItems }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", disabled: !canPrev, onClick: () => setPage((value) => Math.max(1, value - 1)), children: [_jsx(ChevronLeft, { className: "h-4 w-4" }), t('common.pagination.previous')] }), _jsx("span", { className: "text-sm text-gray-600", children: t('admin.schedules.list.page', { current: page, total: totalPages }) }), _jsxs(Button, { variant: "outline", size: "sm", disabled: !canNext, onClick: () => setPage((value) => value + 1), children: [t('common.pagination.next'), _jsx(ChevronRight, { className: "h-4 w-4" })] })] })] })) : null, onCancel && _jsx("div", { className: "flex shrink-0 justify-end", children: _jsx(Button, { variant: "outline", onClick: onCancel, children: t('common.cancel') }) })] }));
59
+ }
60
+ const TEMPLATE_COLORS = ['blue', 'green', 'purple', 'orange', 'pink', 'indigo'];
61
+ const ICON_COLOR_CLASSES = {
62
+ blue: 'bg-blue-100 text-blue-600',
63
+ green: 'bg-green-100 text-green-600',
64
+ purple: 'bg-purple-100 text-purple-600',
65
+ orange: 'bg-orange-100 text-orange-600',
66
+ pink: 'bg-pink-100 text-pink-600',
67
+ indigo: 'bg-indigo-100 text-indigo-600',
68
+ };
69
+ function TemplateCard({ template, index, onSelect, isLoading, disabled }) {
70
+ const color = TEMPLATE_COLORS[index % TEMPLATE_COLORS.length];
71
+ return (_jsxs(Card, { className: `relative cursor-pointer border-2 border-gray-200 bg-white transition-all duration-200 hover:border-gray-300 hover:bg-gray-50 hover:shadow-md ${disabled ? 'pointer-events-none opacity-60' : ''}`, onClick: () => onSelect(template), children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: `rounded-md p-1.5 ${ICON_COLOR_CLASSES[color]}`, children: _jsx(BookOpen, { className: "h-4 w-4" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx(CardTitle, { className: "truncate text-sm font-semibold", children: template.title }), template.subTitle && _jsx(CardDescription, { className: "truncate text-[11px]", children: template.subTitle })] })] }) }), _jsx(CardContent, { className: "space-y-2 px-4 pb-3 pt-0", children: _jsxs("div", { className: "grid grid-cols-2 gap-x-3 gap-y-1 text-xs", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(BookOpen, { className: "h-3 w-3 text-gray-500" }), _jsxs("span", { className: "text-gray-600", children: [template.totalQuestions, " c\u00E2u h\u1ECFi"] })] }), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "h-3 w-3 rounded-full bg-gray-300" }), _jsxs("span", { className: "text-gray-600", children: [template.totalParts, " ph\u1EA7n"] })] }), template.examLevel && _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Users, { className: "h-3 w-3 text-gray-500" }), _jsxs("span", { className: "text-gray-600", children: ["C\u1EA5p \u0111\u1ED9 ", template.examLevel] })] }), template.createdAt && _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Clock, { className: "h-3 w-3 text-gray-500" }), _jsx("span", { className: "text-gray-600", children: new Date(template.createdAt).toLocaleDateString('vi-VN') })] })] }) }), isLoading ? _jsx("div", { className: "absolute inset-0 flex items-center justify-center rounded-xl bg-white/60", children: _jsx(Loader2, { className: "h-5 w-5 animate-spin text-blue-600" }) }) : null] }));
72
+ }
73
+ /** Unwrap the optional `{ data }` envelope returned by the detail endpoint. */
74
+ function unwrapDetail(result) {
75
+ return 'data' in result ? result.data : result;
76
+ }
@@ -1 +1,7 @@
1
- "use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import{AlertDialog as n,AlertDialogAction as c,AlertDialogCancel as r,AlertDialogContent as l,AlertDialogDescription as s,AlertDialogFooter as t,AlertDialogHeader as i,AlertDialogTitle as h}from"../../components/ui/alert-dialog";import{AlertTriangle as m,Save as o,X as d}from"lucide-react";export function UnsavedChangesDialog({open:g,onOpenChange:x,onConfirm:p,onCancel:u,questionNumber:b}){return e(n,{open:g,onOpenChange:x,children:a(l,{className:"max-w-md",children:[a("div",{className:"flex flex-col items-center pt-2 pb-4",children:[e("div",{className:"flex h-16 w-16 items-center justify-center rounded-full bg-amber-100 mb-4",children:e(m,{className:"h-8 w-8 text-amber-600"})}),a(i,{className:"text-center space-y-2",children:[e(h,{className:"text-xl font-semibold text-gray-900",children:"Xác nhận chuyển câu hỏi"}),e(s,{className:"text-center text-gray-600",children:b?a("span",{children:["Câu hỏi ",e("strong",{className:"text-gray-800",children:b})," có những thay đổi chưa được lưu."]}):e("span",{children:"Câu hỏi hiện tại có những thay đổi chưa được lưu."})})]})]}),e("div",{className:"rounded-lg border border-amber-200 bg-amber-50 p-4 mb-4",children:e("p",{className:"text-sm text-amber-800",children:"Nếu bạn tiếp tục mà không lưu, các thay đổi sẽ bị mất và không thể khôi phục."})}),a(t,{className:"flex flex-col-reverse sm:flex-row gap-2 sm:gap-3",children:[a(r,{onClick:u,className:"flex-1 gap-2 border-gray-300 hover:bg-gray-50",children:[e(o,{className:"h-4 w-4"}),"Quay lại chỉnh sửa"]}),a(c,{onClick:p,className:"flex-1 gap-2 bg-red-500 hover:bg-red-600 text-white",children:[e(d,{className:"h-4 w-4"}),"Hủy thay đổi"]})]})]})})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '../../components/ui/alert-dialog';
4
+ import { AlertTriangle, Save, X } from 'lucide-react';
5
+ export function UnsavedChangesDialog({ open, onOpenChange, onConfirm, onCancel, questionNumber, }) {
6
+ return (_jsx(AlertDialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(AlertDialogContent, { className: "max-w-md", children: [_jsxs("div", { className: "flex flex-col items-center pt-2 pb-4", children: [_jsx("div", { className: "flex h-16 w-16 items-center justify-center rounded-full bg-amber-100 mb-4", children: _jsx(AlertTriangle, { className: "h-8 w-8 text-amber-600" }) }), _jsxs(AlertDialogHeader, { className: "text-center space-y-2", children: [_jsx(AlertDialogTitle, { className: "text-xl font-semibold text-gray-900", children: "X\u00E1c nh\u1EADn chuy\u1EC3n c\u00E2u h\u1ECFi" }), _jsx(AlertDialogDescription, { className: "text-center text-gray-600", children: questionNumber ? (_jsxs("span", { children: ["C\u00E2u h\u1ECFi ", _jsx("strong", { className: "text-gray-800", children: questionNumber }), " c\u00F3 nh\u1EEFng thay \u0111\u1ED5i ch\u01B0a \u0111\u01B0\u1EE3c l\u01B0u."] })) : (_jsx("span", { children: "C\u00E2u h\u1ECFi hi\u1EC7n t\u1EA1i c\u00F3 nh\u1EEFng thay \u0111\u1ED5i ch\u01B0a \u0111\u01B0\u1EE3c l\u01B0u." })) })] })] }), _jsx("div", { className: "rounded-lg border border-amber-200 bg-amber-50 p-4 mb-4", children: _jsx("p", { className: "text-sm text-amber-800", children: "N\u1EBFu b\u1EA1n ti\u1EBFp t\u1EE5c m\u00E0 kh\u00F4ng l\u01B0u, c\u00E1c thay \u0111\u1ED5i s\u1EBD b\u1ECB m\u1EA5t v\u00E0 kh\u00F4ng th\u1EC3 kh\u00F4i ph\u1EE5c." }) }), _jsxs(AlertDialogFooter, { className: "flex flex-col-reverse sm:flex-row gap-2 sm:gap-3", children: [_jsxs(AlertDialogCancel, { onClick: onCancel, className: "flex-1 gap-2 border-gray-300 hover:bg-gray-50", children: [_jsx(Save, { className: "h-4 w-4" }), "Quay l\u1EA1i ch\u1EC9nh s\u1EEDa"] }), _jsxs(AlertDialogAction, { onClick: onConfirm, className: "flex-1 gap-2 bg-red-500 hover:bg-red-600 text-white", children: [_jsx(X, { className: "h-4 w-4" }), "H\u1EE7y thay \u0111\u1ED5i"] })] })] }) }));
7
+ }
@@ -1 +1,3 @@
1
- export{buildExamTemplatePayload,createExamCreateApi,createMockCreateExamApi}from"../../api/exam-create";export{createExamQuestionsApi,createExamQuestionsFetchApi,createMockExamQuestionsApi}from"../../api/exam-questions";export{createExamTakingApi,createMockExamTakingApi}from"../../api/exam-taking";
1
+ export { buildExamTemplatePayload, createExamCreateApi, createMockCreateExamApi } from '../../api/exam-create';
2
+ export { createExamQuestionsApi, createExamQuestionsFetchApi, createMockExamQuestionsApi } from '../../api/exam-questions';
3
+ export { createExamTakingApi, createMockExamTakingApi } from '../../api/exam-taking';
@@ -1 +1 @@
1
- export{};
1
+ export {};
@@ -1 +1,18 @@
1
- import{z as t}from"zod";export const examFormSchema=t.object({code:t.string().min(1,"Mã đề kiểm tra không được để trống").max(100,"Mã đề kiểm tra không được vượt quá 100 ký tự").regex(/^[a-zA-Z0-9_-]+$/,"Mã đề kiểm tra chỉ được chứa chữ cái, số, dấu gạch ngang (-) và gạch dưới (_)").trim(),name:t.string().min(3,"Tên đề kiểm tra phải có ít nhất 3 ký tự").max(255,"Tên đề kiểm tra không được vượt quá 255 ký tự").trim(),level:t.string(),difficulty:t.string(),duration:t.string().refine(t=>{if(!t)return!0;const n=Number.parseInt(t,10);return!Number.isNaN(n)&&n>=5&&n<=300},{message:"Thời gian kiểm tra phải từ 5 đến 300 phút"}),totalPoints:t.string().min(1,"Vui lòng nhập tổng điểm").refine(t=>/^[1-9]\d*$/.test(t.trim()),{message:"Tổng điểm phải là số nguyên dương"}).refine(t=>{const n=Number.parseInt(t,10);return n>=1&&n<=1e3},{message:"Tổng điểm phải từ 1 đến 1000"}),description:t.string().max(2e3,"Mô tả không được vượt quá 2000 ký tự").trim()});
1
+ import { z } from 'zod';
2
+ export const examFormSchema = z.object({
3
+ code: z.string().min(1, 'Mã đề kiểm tra không được để trống').max(100, 'Mã đề kiểm tra không được vượt quá 100 ký tự').regex(/^[a-zA-Z0-9_-]+$/, 'Mã đề kiểm tra chỉ được chứa chữ cái, số, dấu gạch ngang (-) và gạch dưới (_)').trim(),
4
+ name: z.string().min(3, 'Tên đề kiểm tra phải có ít nhất 3 ký tự').max(255, 'Tên đề kiểm tra không được vượt quá 255 ký tự').trim(),
5
+ level: z.string(),
6
+ difficulty: z.string(),
7
+ duration: z.string().refine((value) => {
8
+ if (!value)
9
+ return true;
10
+ const minutes = Number.parseInt(value, 10);
11
+ return !Number.isNaN(minutes) && minutes >= 5 && minutes <= 300;
12
+ }, { message: 'Thời gian kiểm tra phải từ 5 đến 300 phút' }),
13
+ totalPoints: z.string().min(1, 'Vui lòng nhập tổng điểm').refine((value) => /^[1-9]\d*$/.test(value.trim()), { message: 'Tổng điểm phải là số nguyên dương' }).refine((value) => {
14
+ const points = Number.parseInt(value, 10);
15
+ return points >= 1 && points <= 1000;
16
+ }, { message: 'Tổng điểm phải từ 1 đến 1000' }),
17
+ description: z.string().max(2000, 'Mô tả không được vượt quá 2000 ký tự').trim(),
18
+ });
@@ -1 +1,11 @@
1
- "use client";import{jsx as n,jsxs as i,Fragment as e}from"react/jsx-runtime";import{AlertDialog as t,AlertDialogAction as r,AlertDialogCancel as l,AlertDialogContent as a,AlertDialogDescription as c,AlertDialogFooter as o,AlertDialogHeader as m,AlertDialogTitle as s}from"../../../components/ui/alert-dialog";import{Loader2 as d}from"lucide-react";export function EntryRestartDialog({open:h,onOpenChange:p,isStarting:u,onContinue:g,onRestart:b}){return n(t,{open:h,onOpenChange:p,children:i(a,{children:[i(m,{children:[n(s,{children:"Bạn đang có bài thi dở"}),n(c,{children:"Bạn đã bắt đầu làm bài thi này trước đó. Bạn muốn tiếp tục làm bài hay bắt đầu lại từ đầu?"})]}),i(o,{children:[n(l,{disabled:u,onClick:g,children:"Làm tiếp"}),n(r,{disabled:u,onClick:b,children:u?i(e,{children:[n(d,{className:"mr-2 h-4 w-4 animate-spin"}),"Đang xử lý..."]}):"Làm lại từ đầu"})]})]})})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '../../../components/ui/alert-dialog';
4
+ import { Loader2 } from 'lucide-react';
5
+ /**
6
+ * Shown when the student already has an IN_PROGRESS attempt for this room.
7
+ * Lets them resume the existing attempt or discard it and start fresh.
8
+ */
9
+ export function EntryRestartDialog({ open, onOpenChange, isStarting, onContinue, onRestart, }) {
10
+ return (_jsx(AlertDialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: "B\u1EA1n \u0111ang c\u00F3 b\u00E0i thi d\u1EDF" }), _jsx(AlertDialogDescription, { children: "B\u1EA1n \u0111\u00E3 b\u1EAFt \u0111\u1EA7u l\u00E0m b\u00E0i thi n\u00E0y tr\u01B0\u1EDBc \u0111\u00F3. B\u1EA1n mu\u1ED1n ti\u1EBFp t\u1EE5c l\u00E0m b\u00E0i hay b\u1EAFt \u0111\u1EA7u l\u1EA1i t\u1EEB \u0111\u1EA7u?" })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: isStarting, onClick: onContinue, children: "L\u00E0m ti\u1EBFp" }), _jsx(AlertDialogAction, { disabled: isStarting, onClick: onRestart, children: isStarting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), "\u0110ang x\u1EED l\u00FD..."] })) : ('Làm lại từ đầu') })] })] }) }));
11
+ }
@@ -1 +1,358 @@
1
- "use client";import{jsx as e,Fragment as t,jsxs as a}from"react/jsx-runtime";import{useState as r,useEffect as n,useCallback as i,useMemo as o}from"react";import{Dialog as s,DialogContent as l,DialogDescription as d,DialogTitle as c}from"../../../components/ui/dialog";import{t as m}from"../../../shared/lib/i18n";import{ROUTES as h}from"../../../shared/constants/routes";import{normalizeExamTheme as x,resolveThemeConfig as u}from"../../../shared/lib/themes";import{unwrap as p}from"../take/exam-taking.utils";import{toast as g}from"sonner";import{Loader2 as b,Clock as f,Trophy as v,ArrowLeft as w}from"lucide-react";import y from"next/image";import{BeforeStartCard as N,ExpiredCard as _,AlreadySubmittedCard as k}from"../../../components/features/exam-entry";import{CloseButton as T}from"../../../components/features/exam-entry/themed/components";import{ToeicWaitingRoom as E}from"../../../components/features/exam-entry/themed/ToeicWaitingRoom";import{CambridgeYleWaitingRoom as D}from"../../../components/features/exam-entry/themed/CambridgeYleWaitingRoom";import{SummerSkyWaitingRoom as S}from"../../../components/themes/summer-sky/SummerSkyWaitingRoom";import{FullscreenLoading as R}from"./fullscreen-loading";export function StudentExamEntryPageContainer({roomId:I,inviteToken:A=null,api:O,onNavigate:M,takePath:C=e=>`/student/exams/${e}/take`,closePath:B,renderRegistrationForm:W}){const L=i((e,t=!1)=>{M?M(e,{replace:t}):"undefined"!=typeof window&&(t?window.location.replace(e):window.location.href=e)},[M]),[j,F]=r(null),[z,U]=r(null),[$,P]=r("LOADING"),[X,Y]=r(0),[H,V]=r(5),[G,K]=r(!1),[Q,q]=r(!1),[J,Z]=r(null),[ee,te]=r(!1),[ae,re]=r(!1),[ne,ie]=r(!1),oe=o(()=>{const e=j?.exam?.theme,t=j?.exam?.template?.theme;return u({examTheme:e,templateTheme:t,level:j?.exam?.level})},[j?.exam,I,j]),se=o(()=>B?B(j):j?.contest?.id?h.STUDENT.CONTEST_ROOMS(j.contest.id):h.STUDENT.CONTESTS_DISCOVER,[B,j]),le=i((e,t)=>{const a=!0===e.allowMultipleSubmissions;if(t){const e=t.status;if("SUBMITTED"===e||"GRADED"===e||"PUBLISHED"===e)return a?"WITHIN_WINDOW":"ALREADY_SUBMITTED";if("IN_PROGRESS"===e)return"WITHIN_WINDOW"}const r=(new Date).getTime(),n=e.startDatetime?new Date(e.startDatetime).getTime():null,i=e.endDatetime?new Date(e.endDatetime).getTime():null;return!a&&i&&r>i?"EXPIRED":n&&r<n?"BEFORE_START":"WITHIN_WINDOW"},[]);n(()=>{(async()=>{try{if(A)try{await(O.joinViaInvite?.(I,A))}catch{}const e=await O.getRoom(I),t=p(e);if(F(t),!t.canAccess&&!A)return void P("NO_ACCESS");if(t.registrationFormConfig?.enabled&&t.registrationFormConfig.fields?.length>0&&!t.hasSubmittedFormData)return void re(!0);if(!t.exam)return void P("NO_EXAM");const a=await O.getActiveAttemptByRoom(I),r=p(a);r&&(Z(r),U(r));const n=!0===t.allowMultipleSubmissions;if(t.hasJoined&&!r&&!n)return void P("ALREADY_SUBMITTED");const i=le(t,r??null);if(P(i),"BEFORE_START"===i&&t.startDatetime){const e=new Date(t.startDatetime).getTime(),a=(new Date).getTime();Y(Math.max(0,Math.floor((e-a)/1e3)))}}catch(e){console.error("Failed to load room data:",e),g.error(m("student.exam.entry.loadError"))}})()},[I,A,le]),n(()=>{if("BEFORE_START"!==$||X<=0)return;const e=setInterval(()=>{Y(e=>e<=1?(P("WITHIN_WINDOW"),0):e-1)},1e3);return()=>clearInterval(e)},[$,X]);const de=i(async()=>{try{K(!0),await O.startAttempt(I),L(C(I),!0)}catch(e){const t=e,a=t.response?.data?.errorCode;"ROOM_NO_EXAM"===a?P("NO_EXAM"):"ROOM_NOT_STARTED"===a?(g.error(m("student.exam.entry.notStarted")),P("BEFORE_START"),q(!1),V(5)):"ROOM_ENDED"===a?(g.error(m("student.exam.entry.expired")),P("EXPIRED")):"ATTEMPT_ALREADY_SUBMITTED"===a?(g.error("Bạn đã hoàn thành bài thi này rồi."),P("ALREADY_SUBMITTED")):g.error(m("student.exam.entry.startError")),K(!1)}},[I]),ce=i(()=>{J?te(!0):de()},[J,de]),me=i(()=>{te(!1),g.success(m("student.exam.entry.continue")),L(C(I),!0)},[I]),he=i(async()=>{if(J)try{te(!1),K(!0),await O.deleteAttempt(J.id),Z(null),await de()}catch(e){console.error("Failed to restart attempt:",e),g.error(m("student.exam.entry.restartError")),K(!1)}},[J,de]);n(()=>{if(!Q||H<=0)return;const e=setInterval(()=>{V(e=>e<=1?(L(C(I),!0),0):e-1)},1e3);return()=>clearInterval(e)},[Q,H,I]);const xe=i(async e=>{if(j?.contest?.id){ie(!0);try{await(O.registerContest?.(j.contest.id,e)),re(!1);const t=await O.getRoom(I),a=p(t);if(F(a),!a.exam)return void P("NO_EXAM");const r=await O.getActiveAttemptByRoom(I),n=p(r);n&&(Z(n),U(n));const i=!0===a.allowMultipleSubmissions;if(a.hasJoined&&!n&&!i)return void P("ALREADY_SUBMITTED");const o=le(a,n??null);if(P(o),"BEFORE_START"===o&&a.startDatetime){const e=new Date(a.startDatetime).getTime(),t=(new Date).getTime();Y(Math.max(0,Math.floor((e-t)/1e3)))}}catch{g.error("Không thể gửi thông tin đăng ký. Vui lòng thử lại.")}finally{ie(!1)}}},[j?.contest?.id,I,le]);if(ae&&j?.registrationFormConfig&&W)return a(t,{children:[e(R,{}),W({room:j,onSubmit:xe,isSubmitting:ne})]});if("LOADING"===$)return e(R,{});if("NO_EXAM"===$)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:a("div",{className:"mx-auto max-w-md p-4 text-center",children:[e("div",{className:"mb-6 text-7xl",children:"📝✨"}),e("h2",{className:"mb-3 text-2xl font-bold text-sky-700",children:"Bài thi đang được chuẩn bị"}),j&&a("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:"font-semibold text-base",children:j.name}),j.code&&a("p",{className:"mt-1 text-sky-500",children:["Mã phòng: ",e("span",{className:"font-mono font-medium",children:j.code})]}),(j.startDatetime||j.endDatetime)&&a("p",{className:"mt-1 text-sky-500",children:["Thời gian thi:"," ",j.startDatetime?new Date(j.startDatetime).toLocaleString("vi-VN"):"—"," → ",j.endDatetime?new Date(j.endDatetime).toLocaleString("vi-VN"):"—"]})]}),e("p",{className:"mb-6 text-base text-sky-600",children:"Thầy cô đang chuẩn bị đề thi cho phòng này. Bạn quay lại sau nhé! 😊"}),e("button",{className:"mx-auto inline-flex items-center gap-2 rounded-2xl border-b-4 border-sky-600 bg-sky-500 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",onClick:()=>window.history.back(),children:"← Quay lại"})]})});if("NO_ACCESS"===$)return e("div",{className:"fixed inset-0 z-50 flex items-center justify-center overflow-hidden bg-gradient-to-b from-red-50 via-orange-50 to-amber-50",children:a("div",{className:"mx-auto max-w-md p-4 text-center",children:[e("div",{className:"mb-6 text-7xl",children:"🔒"}),e("h2",{className:"mb-3 text-2xl font-bold text-red-600",children:"Không có quyền truy cập"}),e("p",{className:"mb-6 text-base text-red-500",children:"Bạn chưa được đăng ký vào phòng thi này. Hãy liên hệ thầy cô để được thêm vào phòng thi nhé!"}),e("button",{className:"mx-auto inline-flex items-center gap-2 rounded-2xl border-b-4 border-red-600 bg-red-500 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",onClick:()=>window.history.back(),children:"← Quay lại"})]})});const ue="TOEIC"===j?.exam?.template?.subTitle,pe="SUMMER_SKY"===x(j?.exam?.theme||j?.exam?.template?.theme);if("WITHIN_WINDOW"===$&&j)return a(t,{children:[ue?e(E,{room:j,onStart:ce,isStarting:G,showCountdown:Q,autoStartCountdown:H,closeRedirectUrl:se,onClose:()=>{L(se)}}):pe?e(S,{room:j,onStart:ce,isStarting:G,onExit:()=>{L(se)}}):e(D,{room:j,onStart:ce,isStarting:G,showCountdown:Q,autoStartCountdown:H,closeRedirectUrl:se,onClose:()=>{L(se)}}),e(s,{open:ee,onOpenChange:te,children:e(l,{className:"overflow-visible border-0 bg-transparent p-0 shadow-none sm:max-w-md",children:a("div",{className:"relative overflow-visible rounded-[28px] bg-gradient-to-br from-cyan-300/25 via-white/5 to-white/0 p-px shadow-[0_30px_80px_-20px_rgba(10,75,140,0.85)] backdrop-blur-xl",children:[e("div",{className:"pointer-events-none absolute -left-16 -top-16 h-48 w-48 rounded-full bg-cyan-400/35 blur-3xl"}),e("div",{className:"pointer-events-none absolute -right-16 -bottom-16 h-48 w-48 rounded-full bg-sky-500/30 blur-3xl"}),e("div",{className:"pointer-events-none absolute left-1/2 top-0 h-24 w-1/2 -translate-x-1/2 rounded-full bg-cyan-300/15 blur-2xl"}),a("div",{className:"relative overflow-hidden rounded-[calc(28px-1px)] bg-gradient-to-b from-[#0e3d70]/97 via-[#0a4b8c]/97 to-[#072d54]/97 ring-1 ring-cyan-300/25",children:[e("div",{className:"pointer-events-none absolute inset-0 opacity-[0.04]",style:{backgroundImage:"radial-gradient(circle at 1px 1px, white 1px, transparent 0)",backgroundSize:"24px 24px"}}),e("div",{className:"pointer-events-none absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-cyan-300/40 to-transparent"}),a("div",{className:"relative flex flex-col items-center gap-4 px-8 pt-10 pb-6",children:[a("div",{className:"relative",children:[e("div",{className:"absolute inset-0 animate-ping rounded-full bg-cyan-300/30",style:{animationDuration:"2.8s"}}),e("div",{className:"absolute -inset-3 rounded-full bg-gradient-to-br from-cyan-300/15 to-sky-500/10 blur-md"}),e("div",{className:"absolute -inset-1.5 rounded-full bg-gradient-to-br from-cyan-200/30 to-sky-400/20"}),e("div",{className:"relative flex h-16 w-16 items-center justify-center rounded-full bg-gradient-to-br from-cyan-300 via-sky-500 to-sky-700 shadow-[0_10px_30px_-6px_rgba(34,211,238,0.7),inset_0_2px_4px_rgba(255,255,255,0.4)] ring-[3px] ring-cyan-200/40",children:a("svg",{className:"h-8 w-8 text-white drop-shadow-[0_2px_3px_rgba(0,0,0,0.3)]",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2.5",strokeLinecap:"round",strokeLinejoin:"round",children:[e("path",{d:"M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"}),e("line",{x1:"12",y1:"9",x2:"12",y2:"13"}),e("line",{x1:"12",y1:"17",x2:"12.01",y2:"17"})]})})]}),a("div",{className:"flex flex-col items-center gap-2",children:[e(c,{className:"text-center text-xl font-black tracking-tight text-white drop-shadow-[0_2px_8px_rgba(34,211,238,0.4)]",children:"Bạn đang có bài thi dở"}),e(d,{className:"max-w-sm text-center text-[13px] font-medium leading-relaxed text-cyan-100/75",children:"Bạn đã bắt đầu làm bài thi này trước đó. Bạn muốn tiếp tục làm bài hay bắt đầu lại từ đầu?"})]})]}),e("div",{className:"mx-8 h-px bg-gradient-to-r from-transparent via-cyan-300/30 to-transparent"}),a("div",{className:"relative flex flex-col-reverse gap-3 px-8 py-6 sm:flex-row sm:justify-center",children:[a("button",{onClick:me,className:"group relative inline-flex min-w-[140px] cursor-pointer items-center justify-center gap-2 overflow-hidden rounded-full bg-white/[0.08] px-7 py-3 text-sm font-bold text-white ring-1 ring-cyan-200/30 backdrop-blur-md transition-all duration-200 hover:-translate-y-0.5 hover:bg-white/[0.14] hover:ring-cyan-200/60 hover:shadow-[0_8px_20px_-4px_rgba(34,211,238,0.3)] active:translate-y-0",children:[e("span",{className:"pointer-events-none absolute inset-0 -translate-x-full bg-gradient-to-r from-transparent via-white/20 to-transparent transition-transform duration-700 group-hover:translate-x-full"}),e("span",{className:"relative",children:"Làm tiếp"})]}),a("button",{onClick:he,disabled:G,className:"group relative inline-flex min-w-[160px] cursor-pointer items-center justify-center gap-2 overflow-hidden rounded-full bg-gradient-to-b from-[#ffd54a] via-[#ffb733] to-[#ff8a1f] px-7 py-3 text-sm font-black uppercase tracking-wide text-white shadow-[0_6px_0_0_#c46410,0_10px_22px_rgba(255,138,31,0.5)] ring-2 ring-orange-300/70 transition-all duration-150 hover:-translate-y-0.5 hover:brightness-110 hover:shadow-[0_8px_0_0_#c46410,0_14px_26px_rgba(255,138,31,0.55)] active:translate-y-1.5 active:shadow-[0_1px_0_0_#c46410,0_3px_8px_rgba(255,138,31,0.3)] disabled:cursor-not-allowed disabled:opacity-70",style:{textShadow:"0 2px 0 rgba(180,80,10,0.55)"},children:[e("span",{className:"pointer-events-none absolute inset-x-0 top-0 h-1/2 bg-gradient-to-b from-white/30 to-transparent"}),G?a(t,{children:[e(b,{className:"relative h-4 w-4 animate-spin"}),e("span",{className:"relative",children:"Đang xử lý..."})]}):e("span",{className:"relative",children:"Làm lại từ đầu"})]})]})]})]})})})]});const ge=oe.visuals?.backgroundFilter??"grayscale(1) blur(10px) saturate(0.85) brightness(0.9)",be=oe.visuals?.backgroundOverlay??"linear-gradient(to bottom, rgba(0, 0, 0, 0.2), transparent, rgba(0, 0, 0, 0.3))",fe=oe.visuals?.cardBackground??"radial-gradient(60% 60% at 50% 40%, rgba(184, 229, 157, 0.85) 0%, rgba(115, 213, 133, 0.85) 100%)";return a("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(${oe.assets.background})`,filter:ge}}),e("div",{className:"absolute inset-0",style:{background:be}}),a("div",{className:"relative z-10 w-[88%] max-w-2xl sm:w-[70%] md:w-[50%] lg:w-[42%]",children:[e(T,{imageSrc:oe.assets.closeButton,onClick:()=>L(se),className:"absolute -right-3 -top-7 z-30",size:72}),a("div",{className:"absolute left-1/2 top-0 z-10 -translate-x-1/2 -translate-y-1/2",children:[e(y,{src:oe.assets.titleBar,alt:"",width:260,height:56,className:"drop-shadow-lg",priority:!0,unoptimized:!0}),e("span",{className:"absolute inset-0 mt-2.5 flex items-center justify-center pb-1 text-sm font-extrabold tracking-wider text-white drop-shadow",children:oe.displayName})]}),a("div",{className:"relative overflow-hidden rounded-3xl shadow-2xl ring-1 ring-white/20",children:[e("div",{className:"h-2 w-full",style:{backgroundImage:`url(${oe.assets.cardFrame})`,backgroundSize:"100% 100%"}}),a("div",{className:"relative p-6 pt-12 md:p-8 md:pt-14",style:{background:fe},children:[a("div",{className:"mb-4 rounded-2xl bg-black/20 px-4 py-3.5 text-center ring-1 ring-white/10 backdrop-blur-sm",children:[e("h2",{className:"text-base font-extrabold tracking-wide text-white drop-shadow-sm",children:j?.name}),j?.exam?.name&&e("p",{className:"mt-0.5 text-xs font-medium text-white/80",children:j.exam.name})]}),(j?.exam?.durationMinutes||j?.exam?.totalScore)&&a("div",{className:"mb-4 grid grid-cols-2 gap-3",children:[j?.exam?.durationMinutes&&a("div",{className:"flex flex-col items-center rounded-2xl bg-black/20 px-2 py-4 ring-1 ring-white/10 backdrop-blur-sm",children:[e("div",{className:"mb-2 flex h-8 w-8 items-center justify-center rounded-full bg-white/10",children:e(f,{className:"h-4 w-4 text-white"})}),e("span",{className:"text-2xl font-extrabold text-white",children:j.exam.durationMinutes}),e("span",{className:"text-xs font-semibold text-white/80",children:"Phút"})]}),j?.exam?.totalScore&&a("div",{className:"flex flex-col items-center rounded-2xl bg-black/20 px-2 py-4 ring-1 ring-white/10 backdrop-blur-sm",children:[e("div",{className:"mb-2 flex h-8 w-8 items-center justify-center rounded-full bg-white/10",children:e(v,{className:"h-4 w-4 text-white"})}),e("span",{className:"text-2xl font-extrabold text-white",children:j.exam.totalScore}),e("span",{className:"text-xs font-semibold text-white/80",children:"Thang điểm"})]})]}),"BEFORE_START"===$&&e(N,{countdown:X,formatCountdown:e=>{const t=Math.floor(e/86400),a=Math.floor(e%86400/3600),r=Math.floor(e%3600/60),n=e%60;return t>0?`${t} ngày ${a} giờ ${r} phút`:a>0?`${a} giờ ${r} phút ${n} giây`:r>0?`${r} phút ${n} giây`:`${n} giây`}}),"EXPIRED"===$&&e(_,{}),"ALREADY_SUBMITTED"===$&&e(k,{attempt:z}),e("div",{className:"mt-5 flex justify-center",children:a("button",{className:"group relative inline-flex cursor-pointer items-center gap-2 rounded-full bg-[#0473f9] px-8 py-3 shadow-[0_4px_0_0_#1d4ed8] transition-all duration-150 hover:brightness-110 active:translate-y-[2px] active:shadow-[0_2px_0_0_#1d4ed8]",onClick:()=>L(se),children:[e(w,{className:"h-5 w-5 text-white transition-transform group-hover:-translate-x-0.5"}),e("span",{className:"text-sm font-extrabold uppercase tracking-wider text-white",children:"Quay lại"})]})}),e("div",{className:"pointer-events-none absolute -bottom-1 -left-1 opacity-60",children:e(y,{src:oe.assets.bottomLeft,alt:"",width:80,height:80,unoptimized:!0})}),e("div",{className:"pointer-events-none absolute -bottom-1 -right-1 opacity-60",children:e(y,{src:oe.assets.bottomRight,alt:"",width:80,height:80,unoptimized:!0})})]})]})]})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState, useEffect, useCallback, useMemo } from 'react';
4
+ import { Dialog, DialogContent, DialogDescription, DialogTitle, } from '../../../components/ui/dialog';
5
+ import { t } from '../../../shared/lib/i18n';
6
+ import { ROUTES } from '../../../shared/constants/routes';
7
+ import { normalizeExamTheme, resolveThemeConfig } from '../../../shared/lib/themes';
8
+ import { unwrap } from '../take/exam-taking.utils';
9
+ import { toast } from 'sonner';
10
+ import { Loader2, Clock, Trophy, ArrowLeft } from 'lucide-react';
11
+ import Image from 'next/image';
12
+ import { BeforeStartCard, ExpiredCard, AlreadySubmittedCard, } from '../../../components/features/exam-entry';
13
+ import { CloseButton } from '../../../components/features/exam-entry/themed/components';
14
+ import { ToeicWaitingRoom } from '../../../components/features/exam-entry/themed/ToeicWaitingRoom';
15
+ import { CambridgeYleWaitingRoom } from '../../../components/features/exam-entry/themed/CambridgeYleWaitingRoom';
16
+ import { SummerSkyWaitingRoom } from '../../../components/themes/summer-sky/SummerSkyWaitingRoom';
17
+ import { FullscreenLoading } from './fullscreen-loading';
18
+ /**
19
+ * SDK container for the fullscreen student exam entry screen. Ports the host
20
+ * app's `/student/exams/:roomId/enter` flow: invite join, access/exam/
21
+ * registration gating, room-window state machine, active-attempt resume, the
22
+ * restart-or-continue dialog, and the themed waiting rooms (TOEIC / Cambridge
23
+ * YLE / Summer Sky). Infrastructure is injected via {@link ExamEntrySdkApi},
24
+ * `onNavigate`, and `renderRegistrationForm`.
25
+ */
26
+ export function StudentExamEntryPageContainer({ roomId, inviteToken = null, api, onNavigate, takePath = (id) => `/student/exams/${id}/take`, closePath, renderRegistrationForm, }) {
27
+ const navigate = useCallback((href, replace = false) => {
28
+ if (onNavigate)
29
+ onNavigate(href, { replace });
30
+ else if (typeof window !== 'undefined') {
31
+ if (replace)
32
+ window.location.replace(href);
33
+ else
34
+ window.location.href = href;
35
+ }
36
+ }, [onNavigate]);
37
+ const [room, setRoom] = useState(null);
38
+ const [attempt, setAttempt] = useState(null);
39
+ const [entryState, setEntryState] = useState('LOADING');
40
+ const [countdown, setCountdown] = useState(0);
41
+ const [autoStartCountdown, setAutoStartCountdown] = useState(5);
42
+ const [isStarting, setIsStarting] = useState(false);
43
+ const [showCountdown, setShowCountdown] = useState(false);
44
+ // States for active attempt handling
45
+ const [activeAttempt, setActiveAttempt] = useState(null);
46
+ const [showRestartDialog, setShowRestartDialog] = useState(false);
47
+ // Registration form states
48
+ const [showRegistrationForm, setShowRegistrationForm] = useState(false);
49
+ const [isSubmittingForm, setIsSubmittingForm] = useState(false);
50
+ const theme = useMemo(() => {
51
+ const examTheme = room?.exam?.theme;
52
+ const templateTheme = room?.exam?.template?.theme;
53
+ const resolved = resolveThemeConfig({
54
+ examTheme,
55
+ templateTheme,
56
+ level: room?.exam?.level,
57
+ });
58
+ return resolved;
59
+ }, [room?.exam, roomId, room]);
60
+ // Compute the close/back URL: go to the contest rooms page if contest exists, otherwise discover page
61
+ const closeUrl = useMemo(() => closePath
62
+ ? closePath(room)
63
+ : room?.contest?.id
64
+ ? ROUTES.STUDENT.CONTEST_ROOMS(room.contest.id)
65
+ : ROUTES.STUDENT.CONTESTS_DISCOVER, [closePath, room]);
66
+ // Determine entry state based on room and attempt data
67
+ const determineState = useCallback((roomData, attemptData) => {
68
+ const canMultipleSubmit = roomData.allowMultipleSubmissions === true;
69
+ if (attemptData) {
70
+ const status = attemptData.status;
71
+ if (status === 'SUBMITTED' || status === 'GRADED' || status === 'PUBLISHED') {
72
+ // If multiple submissions allowed, treat as WITHIN_WINDOW so student can start a new attempt
73
+ if (canMultipleSubmit) {
74
+ return 'WITHIN_WINDOW';
75
+ }
76
+ return 'ALREADY_SUBMITTED';
77
+ }
78
+ if (status === 'IN_PROGRESS')
79
+ return 'WITHIN_WINDOW';
80
+ }
81
+ const now = new Date().getTime();
82
+ const start = roomData.startDatetime ? new Date(roomData.startDatetime).getTime() : null;
83
+ const end = roomData.endDatetime ? new Date(roomData.endDatetime).getTime() : null;
84
+ // Skip end-time check if multiple submissions allowed
85
+ if (!canMultipleSubmit && end && now > end)
86
+ return 'EXPIRED';
87
+ if (start && now < start)
88
+ return 'BEFORE_START';
89
+ return 'WITHIN_WINDOW';
90
+ }, []);
91
+ // Load room and check active attempt on page load
92
+ useEffect(() => {
93
+ const loadData = async () => {
94
+ try {
95
+ // If invite token present, join via invite first (silently)
96
+ if (inviteToken) {
97
+ try {
98
+ await api.joinViaInvite?.(roomId, inviteToken);
99
+ }
100
+ catch {
101
+ // Ignore - student may already be a member
102
+ }
103
+ }
104
+ const roomResponse = await api.getRoom(roomId);
105
+ const roomData = unwrap(roomResponse);
106
+ setRoom(roomData);
107
+ // Check if student has access to this room
108
+ if (!roomData.canAccess && !inviteToken) {
109
+ setEntryState('NO_ACCESS');
110
+ return;
111
+ }
112
+ // Check if registration form is required but not yet submitted
113
+ if (roomData.registrationFormConfig?.enabled &&
114
+ roomData.registrationFormConfig.fields?.length > 0 &&
115
+ !roomData.hasSubmittedFormData) {
116
+ setShowRegistrationForm(true);
117
+ return;
118
+ }
119
+ // Check if room has an exam assigned
120
+ if (!roomData.exam) {
121
+ setEntryState('NO_EXAM');
122
+ return;
123
+ }
124
+ // Check active attempt specifically for THIS room
125
+ // (not the global getActive which may return an attempt from another room)
126
+ const attemptResponse = await api.getActiveAttemptByRoom(roomId);
127
+ const activeForRoom = unwrap(attemptResponse);
128
+ if (activeForRoom) {
129
+ // Student has an IN_PROGRESS attempt for this room - allow continuing
130
+ setActiveAttempt(activeForRoom);
131
+ setAttempt(activeForRoom);
132
+ }
133
+ // If student already joined (has attempt) but no active IN_PROGRESS attempt for this room,
134
+ // and multiple submissions not allowed → already submitted
135
+ const canMultipleSubmit = roomData.allowMultipleSubmissions === true;
136
+ if (roomData.hasJoined && !activeForRoom && !canMultipleSubmit) {
137
+ setEntryState('ALREADY_SUBMITTED');
138
+ return;
139
+ }
140
+ const state = determineState(roomData, activeForRoom ?? null);
141
+ setEntryState(state);
142
+ if (state === 'BEFORE_START' && roomData.startDatetime) {
143
+ const start = new Date(roomData.startDatetime).getTime();
144
+ const now = new Date().getTime();
145
+ setCountdown(Math.max(0, Math.floor((start - now) / 1000)));
146
+ }
147
+ }
148
+ catch (error) {
149
+ console.error('Failed to load room data:', error);
150
+ toast.error(t('student.exam.entry.loadError'));
151
+ }
152
+ };
153
+ loadData();
154
+ }, [roomId, inviteToken, determineState]);
155
+ // Countdown for BEFORE_START state
156
+ useEffect(() => {
157
+ if (entryState !== 'BEFORE_START' || countdown <= 0)
158
+ return;
159
+ const timer = setInterval(() => {
160
+ setCountdown((prev) => {
161
+ if (prev <= 1) {
162
+ setEntryState('WITHIN_WINDOW');
163
+ return 0;
164
+ }
165
+ return prev - 1;
166
+ });
167
+ }, 1000);
168
+ return () => clearInterval(timer);
169
+ }, [entryState, countdown]);
170
+ // Start a new attempt and navigate
171
+ const startNewAttempt = useCallback(async () => {
172
+ try {
173
+ setIsStarting(true);
174
+ await api.startAttempt(roomId);
175
+ // Skip countdown and navigate immediately, replacing history so back button bypasses enter page
176
+ navigate(takePath(roomId), true);
177
+ }
178
+ catch (error) {
179
+ const err = error;
180
+ const errorCode = err.response?.data?.errorCode;
181
+ if (errorCode === 'ROOM_NO_EXAM') {
182
+ setEntryState('NO_EXAM');
183
+ }
184
+ else if (errorCode === 'ROOM_NOT_STARTED') {
185
+ toast.error(t('student.exam.entry.notStarted'));
186
+ setEntryState('BEFORE_START');
187
+ setShowCountdown(false);
188
+ setAutoStartCountdown(5);
189
+ }
190
+ else if (errorCode === 'ROOM_ENDED') {
191
+ toast.error(t('student.exam.entry.expired'));
192
+ setEntryState('EXPIRED');
193
+ }
194
+ else if (errorCode === 'ATTEMPT_ALREADY_SUBMITTED') {
195
+ toast.error('Bạn đã hoàn thành bài thi này rồi.');
196
+ setEntryState('ALREADY_SUBMITTED');
197
+ }
198
+ else {
199
+ toast.error(t('student.exam.entry.startError'));
200
+ }
201
+ setIsStarting(false);
202
+ }
203
+ }, [roomId]);
204
+ // Handle start button click
205
+ const handleStartClick = useCallback(() => {
206
+ if (activeAttempt) {
207
+ // Show dialog to choose continue or restart
208
+ setShowRestartDialog(true);
209
+ }
210
+ else {
211
+ // No active attempt - start new
212
+ startNewAttempt();
213
+ }
214
+ }, [activeAttempt, startNewAttempt]);
215
+ // Continue with existing attempt
216
+ const handleContinue = useCallback(() => {
217
+ setShowRestartDialog(false);
218
+ toast.success(t('student.exam.entry.continue'));
219
+ navigate(takePath(roomId), true);
220
+ }, [roomId]);
221
+ // Restart - delete old attempt and start new
222
+ const handleRestart = useCallback(async () => {
223
+ if (!activeAttempt)
224
+ return;
225
+ try {
226
+ setShowRestartDialog(false);
227
+ setIsStarting(true);
228
+ await api.deleteAttempt(activeAttempt.id);
229
+ setActiveAttempt(null);
230
+ await startNewAttempt();
231
+ }
232
+ catch (error) {
233
+ console.error('Failed to restart attempt:', error);
234
+ toast.error(t('student.exam.entry.restartError'));
235
+ setIsStarting(false);
236
+ }
237
+ }, [activeAttempt, startNewAttempt]);
238
+ // 5-sec countdown then navigate
239
+ useEffect(() => {
240
+ if (!showCountdown || autoStartCountdown <= 0)
241
+ return;
242
+ const timer = setInterval(() => {
243
+ setAutoStartCountdown((prev) => {
244
+ if (prev <= 1) {
245
+ navigate(takePath(roomId), true);
246
+ return 0;
247
+ }
248
+ return prev - 1;
249
+ });
250
+ }, 1000);
251
+ return () => clearInterval(timer);
252
+ }, [showCountdown, autoStartCountdown, roomId]);
253
+ const formatCountdown = (seconds) => {
254
+ const days = Math.floor(seconds / 86400);
255
+ const hours = Math.floor((seconds % 86400) / 3600);
256
+ const mins = Math.floor((seconds % 3600) / 60);
257
+ const secs = seconds % 60;
258
+ if (days > 0)
259
+ return `${days} ngày ${hours} giờ ${mins} phút`;
260
+ if (hours > 0)
261
+ return `${hours} giờ ${mins} phút ${secs} giây`;
262
+ if (mins > 0)
263
+ return `${mins} phút ${secs} giây`;
264
+ return `${secs} giây`;
265
+ };
266
+ // Handle registration form submission
267
+ const handleRegistrationFormSubmit = useCallback(async (formData) => {
268
+ if (!room?.contest?.id)
269
+ return;
270
+ setIsSubmittingForm(true);
271
+ try {
272
+ await api.registerContest?.(room.contest.id, formData);
273
+ setShowRegistrationForm(false);
274
+ // Reload room data to continue the flow
275
+ const roomResponse = await api.getRoom(roomId);
276
+ const roomData = unwrap(roomResponse);
277
+ setRoom(roomData);
278
+ if (!roomData.exam) {
279
+ setEntryState('NO_EXAM');
280
+ return;
281
+ }
282
+ const attemptResponse = await api.getActiveAttemptByRoom(roomId);
283
+ const activeForRoom = unwrap(attemptResponse);
284
+ if (activeForRoom) {
285
+ setActiveAttempt(activeForRoom);
286
+ setAttempt(activeForRoom);
287
+ }
288
+ const canMultipleSubmit = roomData.allowMultipleSubmissions === true;
289
+ if (roomData.hasJoined && !activeForRoom && !canMultipleSubmit) {
290
+ setEntryState('ALREADY_SUBMITTED');
291
+ return;
292
+ }
293
+ const state = determineState(roomData, activeForRoom ?? null);
294
+ setEntryState(state);
295
+ if (state === 'BEFORE_START' && roomData.startDatetime) {
296
+ const start = new Date(roomData.startDatetime).getTime();
297
+ const now = new Date().getTime();
298
+ setCountdown(Math.max(0, Math.floor((start - now) / 1000)));
299
+ }
300
+ }
301
+ catch {
302
+ toast.error('Không thể gửi thông tin đăng ký. Vui lòng thử lại.');
303
+ }
304
+ finally {
305
+ setIsSubmittingForm(false);
306
+ }
307
+ }, [room?.contest?.id, roomId, determineState]);
308
+ // Show registration form via the host-provided slot
309
+ if (showRegistrationForm && room?.registrationFormConfig && renderRegistrationForm) {
310
+ return (_jsxs(_Fragment, { children: [_jsx(FullscreenLoading, {}), renderRegistrationForm({
311
+ room: room,
312
+ onSubmit: handleRegistrationFormSubmit,
313
+ isSubmitting: isSubmittingForm,
314
+ })] }));
315
+ }
316
+ // Loading state - fullscreen themed loader
317
+ if (entryState === 'LOADING') {
318
+ return _jsx(FullscreenLoading, {});
319
+ }
320
+ // NO_EXAM state - room has no exam assigned (fullscreen, child-friendly)
321
+ if (entryState === 'NO_EXAM') {
322
+ 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: "mx-auto max-w-md p-4 text-center", children: [_jsx("div", { className: "mb-6 text-7xl", children: "\uD83D\uDCDD\u2728" }), _jsx("h2", { className: "mb-3 text-2xl font-bold text-sky-700", children: "B\u00E0i thi \u0111ang \u0111\u01B0\u1EE3c chu\u1EA9n b\u1ECB" }), 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: "font-semibold text-base", 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 })] })), (room.startDatetime || room.endDatetime) && (_jsxs("p", { className: "mt-1 text-sky-500", children: ["Th\u1EDDi gian thi:", ' ', room.startDatetime
323
+ ? new Date(room.startDatetime).toLocaleString('vi-VN')
324
+ : '—', ' → ', room.endDatetime
325
+ ? new Date(room.endDatetime).toLocaleString('vi-VN')
326
+ : '—'] }))] })), _jsx("p", { className: "mb-6 text-base text-sky-600", children: "Th\u1EA7y c\u00F4 \u0111ang chu\u1EA9n b\u1ECB \u0111\u1EC1 thi cho ph\u00F2ng n\u00E0y. B\u1EA1n quay l\u1EA1i sau nh\u00E9! \uD83D\uDE0A" }), _jsx("button", { className: "mx-auto inline-flex items-center gap-2 rounded-2xl border-b-4 border-sky-600 bg-sky-500 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", onClick: () => window.history.back(), children: "\u2190 Quay l\u1EA1i" })] }) }));
327
+ }
328
+ // NO_ACCESS state - student doesn't have permission to access this room
329
+ if (entryState === 'NO_ACCESS') {
330
+ return (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center overflow-hidden bg-gradient-to-b from-red-50 via-orange-50 to-amber-50", children: _jsxs("div", { className: "mx-auto max-w-md p-4 text-center", children: [_jsx("div", { className: "mb-6 text-7xl", children: "\uD83D\uDD12" }), _jsx("h2", { className: "mb-3 text-2xl font-bold text-red-600", children: "Kh\u00F4ng c\u00F3 quy\u1EC1n truy c\u1EADp" }), _jsx("p", { className: "mb-6 text-base text-red-500", children: "B\u1EA1n ch\u01B0a \u0111\u01B0\u1EE3c \u0111\u0103ng k\u00FD v\u00E0o ph\u00F2ng thi n\u00E0y. H\u00E3y li\u00EAn h\u1EC7 th\u1EA7y c\u00F4 \u0111\u1EC3 \u0111\u01B0\u1EE3c th\u00EAm v\u00E0o ph\u00F2ng thi nh\u00E9!" }), _jsx("button", { className: "mx-auto inline-flex items-center gap-2 rounded-2xl border-b-4 border-red-600 bg-red-500 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", onClick: () => window.history.back(), children: "\u2190 Quay l\u1EA1i" })] }) }));
331
+ }
332
+ // Detect special exams and themes
333
+ const isToeicExam = room?.exam?.template?.subTitle === 'TOEIC';
334
+ const examThemeStr = normalizeExamTheme(room?.exam?.theme || room?.exam?.template?.theme);
335
+ const isSummerSkyTheme = examThemeStr === 'SUMMER_SKY';
336
+ // WITHIN_WINDOW state - Show themed waiting room (fullscreen)
337
+ if (entryState === 'WITHIN_WINDOW' && room) {
338
+ return (_jsxs(_Fragment, { children: [isToeicExam ? (_jsx(ToeicWaitingRoom, { room: room, onStart: handleStartClick, isStarting: isStarting, showCountdown: showCountdown, autoStartCountdown: autoStartCountdown, closeRedirectUrl: closeUrl, onClose: () => { navigate(closeUrl); } })) : isSummerSkyTheme ? (_jsx(SummerSkyWaitingRoom, { room: room, onStart: handleStartClick, isStarting: isStarting, onExit: () => { navigate(closeUrl); } })) : (_jsx(CambridgeYleWaitingRoom, { room: room, onStart: handleStartClick, isStarting: isStarting, showCountdown: showCountdown, autoStartCountdown: autoStartCountdown, closeRedirectUrl: closeUrl, onClose: () => { navigate(closeUrl); } })), _jsx(Dialog, { open: showRestartDialog, onOpenChange: setShowRestartDialog, children: _jsx(DialogContent, { className: "overflow-visible border-0 bg-transparent p-0 shadow-none sm:max-w-md", children: _jsxs("div", { className: "relative overflow-visible rounded-[28px] bg-gradient-to-br from-cyan-300/25 via-white/5 to-white/0 p-px shadow-[0_30px_80px_-20px_rgba(10,75,140,0.85)] backdrop-blur-xl", children: [_jsx("div", { className: "pointer-events-none absolute -left-16 -top-16 h-48 w-48 rounded-full bg-cyan-400/35 blur-3xl" }), _jsx("div", { className: "pointer-events-none absolute -right-16 -bottom-16 h-48 w-48 rounded-full bg-sky-500/30 blur-3xl" }), _jsx("div", { className: "pointer-events-none absolute left-1/2 top-0 h-24 w-1/2 -translate-x-1/2 rounded-full bg-cyan-300/15 blur-2xl" }), _jsxs("div", { className: "relative overflow-hidden rounded-[calc(28px-1px)] bg-gradient-to-b from-[#0e3d70]/97 via-[#0a4b8c]/97 to-[#072d54]/97 ring-1 ring-cyan-300/25", children: [_jsx("div", { className: "pointer-events-none absolute inset-0 opacity-[0.04]", style: {
339
+ backgroundImage: 'radial-gradient(circle at 1px 1px, white 1px, transparent 0)',
340
+ backgroundSize: '24px 24px',
341
+ } }), _jsx("div", { className: "pointer-events-none absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-cyan-300/40 to-transparent" }), _jsxs("div", { className: "relative flex flex-col items-center gap-4 px-8 pt-10 pb-6", children: [_jsxs("div", { className: "relative", children: [_jsx("div", { className: "absolute inset-0 animate-ping rounded-full bg-cyan-300/30", style: { animationDuration: '2.8s' } }), _jsx("div", { className: "absolute -inset-3 rounded-full bg-gradient-to-br from-cyan-300/15 to-sky-500/10 blur-md" }), _jsx("div", { className: "absolute -inset-1.5 rounded-full bg-gradient-to-br from-cyan-200/30 to-sky-400/20" }), _jsx("div", { className: "relative flex h-16 w-16 items-center justify-center rounded-full bg-gradient-to-br from-cyan-300 via-sky-500 to-sky-700 shadow-[0_10px_30px_-6px_rgba(34,211,238,0.7),inset_0_2px_4px_rgba(255,255,255,0.4)] ring-[3px] ring-cyan-200/40", children: _jsxs("svg", { className: "h-8 w-8 text-white drop-shadow-[0_2px_3px_rgba(0,0,0,0.3)]", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", strokeLinecap: "round", strokeLinejoin: "round", children: [_jsx("path", { d: "M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" }), _jsx("line", { x1: "12", y1: "9", x2: "12", y2: "13" }), _jsx("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })] }) })] }), _jsxs("div", { className: "flex flex-col items-center gap-2", children: [_jsx(DialogTitle, { className: "text-center text-xl font-black tracking-tight text-white drop-shadow-[0_2px_8px_rgba(34,211,238,0.4)]", children: "B\u1EA1n \u0111ang c\u00F3 b\u00E0i thi d\u1EDF" }), _jsx(DialogDescription, { className: "max-w-sm text-center text-[13px] font-medium leading-relaxed text-cyan-100/75", children: "B\u1EA1n \u0111\u00E3 b\u1EAFt \u0111\u1EA7u l\u00E0m b\u00E0i thi n\u00E0y tr\u01B0\u1EDBc \u0111\u00F3. B\u1EA1n mu\u1ED1n ti\u1EBFp t\u1EE5c l\u00E0m b\u00E0i hay b\u1EAFt \u0111\u1EA7u l\u1EA1i t\u1EEB \u0111\u1EA7u?" })] })] }), _jsx("div", { className: "mx-8 h-px bg-gradient-to-r from-transparent via-cyan-300/30 to-transparent" }), _jsxs("div", { className: "relative flex flex-col-reverse gap-3 px-8 py-6 sm:flex-row sm:justify-center", children: [_jsxs("button", { onClick: handleContinue, className: "group relative inline-flex min-w-[140px] cursor-pointer items-center justify-center gap-2 overflow-hidden rounded-full bg-white/[0.08] px-7 py-3 text-sm font-bold text-white ring-1 ring-cyan-200/30 backdrop-blur-md transition-all duration-200 hover:-translate-y-0.5 hover:bg-white/[0.14] hover:ring-cyan-200/60 hover:shadow-[0_8px_20px_-4px_rgba(34,211,238,0.3)] active:translate-y-0", children: [_jsx("span", { className: "pointer-events-none absolute inset-0 -translate-x-full bg-gradient-to-r from-transparent via-white/20 to-transparent transition-transform duration-700 group-hover:translate-x-full" }), _jsx("span", { className: "relative", children: "L\u00E0m ti\u1EBFp" })] }), _jsxs("button", { onClick: handleRestart, disabled: isStarting, className: "group relative inline-flex min-w-[160px] cursor-pointer items-center justify-center gap-2 overflow-hidden rounded-full bg-gradient-to-b from-[#ffd54a] via-[#ffb733] to-[#ff8a1f] px-7 py-3 text-sm font-black uppercase tracking-wide text-white shadow-[0_6px_0_0_#c46410,0_10px_22px_rgba(255,138,31,0.5)] ring-2 ring-orange-300/70 transition-all duration-150 hover:-translate-y-0.5 hover:brightness-110 hover:shadow-[0_8px_0_0_#c46410,0_14px_26px_rgba(255,138,31,0.55)] active:translate-y-1.5 active:shadow-[0_1px_0_0_#c46410,0_3px_8px_rgba(255,138,31,0.3)] disabled:cursor-not-allowed disabled:opacity-70", style: { textShadow: '0 2px 0 rgba(180,80,10,0.55)' }, children: [_jsx("span", { className: "pointer-events-none absolute inset-x-0 top-0 h-1/2 bg-gradient-to-b from-white/30 to-transparent" }), isStarting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "relative h-4 w-4 animate-spin" }), _jsx("span", { className: "relative", children: "\u0110ang x\u1EED l\u00FD..." })] })) : (_jsx("span", { className: "relative", children: "L\u00E0m l\u1EA1i t\u1EEB \u0111\u1EA7u" }))] })] })] })] }) }) })] }));
342
+ }
343
+ const backgroundFilter = theme.visuals?.backgroundFilter ?? 'grayscale(1) blur(10px) saturate(0.85) brightness(0.9)';
344
+ const backgroundOverlay = theme.visuals?.backgroundOverlay ??
345
+ 'linear-gradient(to bottom, rgba(0, 0, 0, 0.2), transparent, rgba(0, 0, 0, 0.3))';
346
+ const cardBackground = theme.visuals?.cardBackground ??
347
+ 'radial-gradient(60% 60% at 50% 40%, rgba(184, 229, 157, 0.85) 0%, rgba(115, 213, 133, 0.85) 100%)';
348
+ // Other states - Show fullscreen themed UI matching ThemedWaitingRoom style
349
+ 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: {
350
+ backgroundImage: `url(${theme.assets.background})`,
351
+ filter: backgroundFilter,
352
+ } }), _jsx("div", { className: "absolute inset-0", style: { background: backgroundOverlay } }), _jsxs("div", { className: "relative z-10 w-[88%] max-w-2xl sm:w-[70%] md:w-[50%] lg:w-[42%]", children: [_jsx(CloseButton, { imageSrc: theme.assets.closeButton, onClick: () => (navigate(closeUrl)), className: "absolute -right-3 -top-7 z-30", size: 72 }), _jsxs("div", { className: "absolute left-1/2 top-0 z-10 -translate-x-1/2 -translate-y-1/2", children: [_jsx(Image, { src: theme.assets.titleBar, alt: "", width: 260, height: 56, className: "drop-shadow-lg", priority: true, unoptimized: true }), _jsx("span", { className: "absolute inset-0 mt-2.5 flex items-center justify-center pb-1 text-sm font-extrabold tracking-wider text-white drop-shadow", children: theme.displayName })] }), _jsxs("div", { className: "relative overflow-hidden rounded-3xl shadow-2xl ring-1 ring-white/20", children: [_jsx("div", { className: "h-2 w-full", style: {
353
+ backgroundImage: `url(${theme.assets.cardFrame})`,
354
+ backgroundSize: '100% 100%',
355
+ } }), _jsxs("div", { className: "relative p-6 pt-12 md:p-8 md:pt-14", style: {
356
+ background: cardBackground,
357
+ }, children: [_jsxs("div", { className: "mb-4 rounded-2xl bg-black/20 px-4 py-3.5 text-center ring-1 ring-white/10 backdrop-blur-sm", children: [_jsx("h2", { className: "text-base font-extrabold tracking-wide text-white drop-shadow-sm", children: room?.name }), room?.exam?.name && (_jsx("p", { className: "mt-0.5 text-xs font-medium text-white/80", 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 && (_jsxs("div", { className: "flex flex-col items-center rounded-2xl bg-black/20 px-2 py-4 ring-1 ring-white/10 backdrop-blur-sm", children: [_jsx("div", { className: "mb-2 flex h-8 w-8 items-center justify-center rounded-full bg-white/10", children: _jsx(Clock, { className: "h-4 w-4 text-white" }) }), _jsx("span", { className: "text-2xl font-extrabold text-white", children: room.exam.durationMinutes }), _jsx("span", { className: "text-xs font-semibold text-white/80", children: "Ph\u00FAt" })] })), room?.exam?.totalScore && (_jsxs("div", { className: "flex flex-col items-center rounded-2xl bg-black/20 px-2 py-4 ring-1 ring-white/10 backdrop-blur-sm", children: [_jsx("div", { className: "mb-2 flex h-8 w-8 items-center justify-center rounded-full bg-white/10", children: _jsx(Trophy, { className: "h-4 w-4 text-white" }) }), _jsx("span", { className: "text-2xl font-extrabold text-white", children: room.exam.totalScore }), _jsx("span", { className: "text-xs font-semibold text-white/80", children: "Thang \u0111i\u1EC3m" })] }))] })), entryState === 'BEFORE_START' && (_jsx(BeforeStartCard, { countdown: countdown, formatCountdown: formatCountdown })), entryState === 'EXPIRED' && _jsx(ExpiredCard, {}), entryState === 'ALREADY_SUBMITTED' && _jsx(AlreadySubmittedCard, { attempt: attempt }), _jsx("div", { className: "mt-5 flex justify-center", children: _jsxs("button", { className: "group relative inline-flex cursor-pointer items-center gap-2 rounded-full bg-[#0473f9] px-8 py-3 shadow-[0_4px_0_0_#1d4ed8] transition-all duration-150 hover:brightness-110 active:translate-y-[2px] active:shadow-[0_2px_0_0_#1d4ed8]", onClick: () => (navigate(closeUrl)), children: [_jsx(ArrowLeft, { className: "h-5 w-5 text-white transition-transform group-hover:-translate-x-0.5" }), _jsx("span", { className: "text-sm font-extrabold uppercase tracking-wider text-white", children: "Quay l\u1EA1i" })] }) }), _jsx("div", { className: "pointer-events-none absolute -bottom-1 -left-1 opacity-60", children: _jsx(Image, { src: theme.assets.bottomLeft, alt: "", width: 80, height: 80, unoptimized: true }) }), _jsx("div", { className: "pointer-events-none absolute -bottom-1 -right-1 opacity-60", children: _jsx(Image, { src: theme.assets.bottomRight, alt: "", width: 80, height: 80, unoptimized: true }) })] })] })] })] }));
358
+ }