@tinyweb_dev/oe-exam-sdk 0.1.28 → 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 (796) hide show
  1. package/README.md +4 -4
  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.js +3 -1
  310. package/dist/components/questions/types/choose-the-correct-answer-group/map-choose-the-correct-answer-group-data.js +94 -1
  311. package/dist/components/questions/types/choose-the-correct-answer-group/register.js +36 -1
  312. package/dist/components/questions/types/choose-the-correct-answer-group/transform.js +55 -1
  313. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +27 -1
  314. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +231 -1
  315. package/dist/components/questions/types/cross-out-word-group/index.js +5 -1
  316. package/dist/components/questions/types/cross-out-word-group/map-cross-out-word-group-data.d.ts +1 -1
  317. package/dist/components/questions/types/cross-out-word-group/map-cross-out-word-group-data.js +110 -1
  318. package/dist/components/questions/types/cross-out-word-group/register.js +36 -1
  319. package/dist/components/questions/types/cross-out-word-group/transform.js +67 -1
  320. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +275 -1
  321. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +464 -1
  322. package/dist/components/questions/types/fill-in-blank/index.js +2 -1
  323. package/dist/components/questions/types/fill-in-blank/register.js +148 -1
  324. package/dist/components/questions/types/fill-in-blank/transform.js +88 -1
  325. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +117 -1
  326. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +671 -1
  327. package/dist/components/questions/types/fill-missing-words-in-grid/index.js +2 -1
  328. package/dist/components/questions/types/fill-missing-words-in-grid/register.js +37 -1
  329. package/dist/components/questions/types/fill-missing-words-in-grid/transform.js +15 -1
  330. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +262 -1
  331. package/dist/components/questions/types/gn-speaking-interview/register.js +121 -1
  332. package/dist/components/questions/types/gn-speaking-interview/transform.js +48 -1
  333. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingClient.js +16 -1
  334. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingCreator.js +27 -1
  335. package/dist/components/questions/types/image-object-matching/index.js +2 -1
  336. package/dist/components/questions/types/image-object-matching/register.js +3 -1
  337. package/dist/components/questions/types/image-object-matching/transform.js +2 -1
  338. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +102 -1
  339. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +344 -1
  340. package/dist/components/questions/types/label-the-picture/index.js +2 -1
  341. package/dist/components/questions/types/label-the-picture/register.js +54 -1
  342. package/dist/components/questions/types/label-the-picture/transform.js +28 -1
  343. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +78 -1
  344. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +232 -1
  345. package/dist/components/questions/types/listen-and-choose-from-answer-group/index.js +2 -1
  346. package/dist/components/questions/types/listen-and-choose-from-answer-group/register.js +82 -1
  347. package/dist/components/questions/types/listen-and-choose-from-answer-group/transform.js +27 -1
  348. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +70 -1
  349. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +386 -1
  350. package/dist/components/questions/types/listen-and-choose-objects-in-scene/index.js +2 -1
  351. package/dist/components/questions/types/listen-and-choose-objects-in-scene/register.js +100 -1
  352. package/dist/components/questions/types/listen-and-choose-objects-in-scene/transform.js +33 -1
  353. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +106 -1
  354. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +457 -1
  355. package/dist/components/questions/types/listen-and-drag-objects-into-scene/index.js +2 -1
  356. package/dist/components/questions/types/listen-and-drag-objects-into-scene/register.js +110 -1
  357. package/dist/components/questions/types/listen-and-drag-objects-into-scene/transform.js +35 -1
  358. package/dist/components/questions/types/listen-and-speak-answer/ArrowIndicator.js +8 -1
  359. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +22 -1
  360. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +291 -1
  361. package/dist/components/questions/types/listen-and-speak-answer/index.js +2 -1
  362. package/dist/components/questions/types/listen-and-speak-answer/register.js +84 -1
  363. package/dist/components/questions/types/listen-and-speak-answer/transform.js +27 -1
  364. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +32 -1
  365. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +295 -1
  366. package/dist/components/questions/types/listen-and-speak-compare-images/register.js +79 -1
  367. package/dist/components/questions/types/listen-and-speak-compare-images/transform.js +27 -1
  368. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +34 -1
  369. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +285 -1
  370. package/dist/components/questions/types/listen-and-speak-image-group/register.js +81 -1
  371. package/dist/components/questions/types/listen-and-speak-image-group/transform.js +26 -1
  372. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +38 -1
  373. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +385 -1
  374. package/dist/components/questions/types/listen-and-speak-info-exchange/register.js +87 -1
  375. package/dist/components/questions/types/listen-and-speak-info-exchange/transform.js +32 -1
  376. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +26 -1
  377. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +247 -1
  378. package/dist/components/questions/types/listen-and-speak-odd-one-out/register.js +59 -1
  379. package/dist/components/questions/types/listen-and-speak-odd-one-out/transform.js +18 -1
  380. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +26 -1
  381. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +256 -1
  382. package/dist/components/questions/types/listen-and-speak-question-list/register.js +81 -1
  383. package/dist/components/questions/types/listen-and-speak-question-list/transform.js +26 -1
  384. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +26 -1
  385. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +230 -1
  386. package/dist/components/questions/types/listen-and-speak-with-story-images/register.js +56 -1
  387. package/dist/components/questions/types/listen-and-speak-with-story-images/transform.js +16 -1
  388. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +72 -1
  389. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +230 -1
  390. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +1 -1
  391. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +66 -1
  392. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +42 -1
  393. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +96 -1
  394. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +238 -1
  395. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +2 -1
  396. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +76 -1
  397. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +19 -1
  398. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +134 -1
  399. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +617 -1
  400. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +2 -1
  401. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +82 -1
  402. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +29 -1
  403. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +38 -1
  404. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +359 -1
  405. package/dist/components/questions/types/match-by-writing-answer/index.js +3 -1
  406. package/dist/components/questions/types/match-by-writing-answer/register.js +58 -1
  407. package/dist/components/questions/types/match-by-writing-answer/transform.js +61 -1
  408. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsClient.js +16 -1
  409. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsCreator.js +23 -1
  410. package/dist/components/questions/types/read-and-color-objects/index.js +2 -1
  411. package/dist/components/questions/types/read-and-color-objects/register.js +3 -1
  412. package/dist/components/questions/types/read-and-color-objects/transform.js +36 -1
  413. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +125 -1
  414. package/dist/components/questions/types/read-displayed-content/register.js +64 -1
  415. package/dist/components/questions/types/read-displayed-content/transform.js +12 -1
  416. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +65 -1
  417. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +511 -1
  418. package/dist/components/questions/types/read-passage-and-answer-questions/index.js +2 -1
  419. package/dist/components/questions/types/read-passage-and-answer-questions/register.js +58 -1
  420. package/dist/components/questions/types/read-passage-and-answer-questions/transform.js +28 -1
  421. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +373 -1
  422. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +41 -1
  423. package/dist/components/questions/types/speaking-conversation/register.js +110 -1
  424. package/dist/components/questions/types/speaking-conversation/transform.js +34 -1
  425. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +74 -1
  426. package/dist/components/questions/types/speaking-cue-card/register.js +52 -1
  427. package/dist/components/questions/types/speaking-cue-card/transform.js +12 -1
  428. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +10 -1
  429. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +161 -1
  430. package/dist/components/questions/types/speaking-describe-image/index.js +2 -1
  431. package/dist/components/questions/types/speaking-describe-image/register.js +69 -1
  432. package/dist/components/questions/types/speaking-describe-image/transform.js +16 -1
  433. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +234 -1
  434. package/dist/components/questions/types/spontaneous-qa/register.js +93 -1
  435. package/dist/components/questions/types/spontaneous-qa/transform.js +34 -1
  436. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.d.ts +2 -0
  437. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.js +9 -0
  438. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.d.ts +2 -0
  439. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.js +179 -0
  440. package/dist/components/questions/types/spontaneous-qa-group/creator-helpers.d.ts +5 -0
  441. package/dist/components/questions/types/spontaneous-qa-group/creator-helpers.js +43 -0
  442. package/dist/components/questions/types/spontaneous-qa-group/flatten.d.ts +14 -0
  443. package/dist/components/questions/types/spontaneous-qa-group/flatten.js +69 -0
  444. package/dist/components/questions/types/spontaneous-qa-group/index.d.ts +2 -0
  445. package/dist/components/questions/types/spontaneous-qa-group/index.js +2 -0
  446. package/dist/components/questions/types/spontaneous-qa-group/register.d.ts +2 -0
  447. package/dist/components/questions/types/spontaneous-qa-group/register.js +139 -0
  448. package/dist/components/questions/types/spontaneous-qa-group/transform.d.ts +2 -0
  449. package/dist/components/questions/types/spontaneous-qa-group/transform.js +52 -0
  450. package/dist/components/questions/types/true-false/TrueFalseClient.js +75 -1
  451. package/dist/components/questions/types/true-false/TrueFalseCreator.js +244 -1
  452. package/dist/components/questions/types/true-false/index.js +2 -1
  453. package/dist/components/questions/types/true-false/register.js +77 -1
  454. package/dist/components/questions/types/true-false/transform.js +32 -1
  455. package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +25 -1
  456. package/dist/components/questions/types/true-false-group/TrueFalseGroupCreator.js +129 -1
  457. package/dist/components/questions/types/true-false-group/index.js +5 -1
  458. package/dist/components/questions/types/true-false-group/map-true-false-group-data.js +69 -1
  459. package/dist/components/questions/types/true-false-group/register.js +36 -1
  460. package/dist/components/questions/types/true-false-group/transform.js +43 -1
  461. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +233 -1
  462. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +631 -1
  463. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphWrapper.js +28 -1
  464. package/dist/components/questions/types/word-fill-paragraph/index.js +2 -1
  465. package/dist/components/questions/types/word-fill-paragraph/register.js +136 -1
  466. package/dist/components/questions/types/word-fill-paragraph/transform.js +56 -1
  467. package/dist/components/questions/types/word-fill-structured-form/WfsfWordBankEditor.js +86 -1
  468. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +246 -1
  469. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.d.ts +2 -0
  470. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +484 -1
  471. package/dist/components/questions/types/word-fill-structured-form/index.js +3 -1
  472. package/dist/components/questions/types/word-fill-structured-form/map-wfsf-question-data.js +113 -1
  473. package/dist/components/questions/types/word-fill-structured-form/register.js +36 -1
  474. package/dist/components/questions/types/word-fill-structured-form/transform.js +77 -1
  475. package/dist/components/questions/types/word-fill-structured-form/wfsf-word-bank.utils.js +110 -1
  476. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.d.ts +3 -0
  477. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.js +190 -0
  478. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupCreator.d.ts +3 -0
  479. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupCreator.js +182 -0
  480. package/dist/components/questions/types/word-order-and-match-group/index.d.ts +4 -0
  481. package/dist/components/questions/types/word-order-and-match-group/index.js +4 -0
  482. package/dist/components/questions/types/word-order-and-match-group/register.d.ts +2 -0
  483. package/dist/components/questions/types/word-order-and-match-group/register.js +121 -0
  484. package/dist/components/questions/types/word-order-and-match-group/transform.d.ts +2 -0
  485. package/dist/components/questions/types/word-order-and-match-group/transform.js +70 -0
  486. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +84 -1
  487. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +192 -1
  488. package/dist/components/questions/types/word-ordering/index.js +2 -1
  489. package/dist/components/questions/types/word-ordering/register.js +46 -1
  490. package/dist/components/questions/types/word-ordering/transform.js +24 -1
  491. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +99 -1
  492. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +320 -1
  493. package/dist/components/questions/types/write-a-short-letter/index.js +2 -1
  494. package/dist/components/questions/types/write-a-short-letter/register.js +68 -1
  495. package/dist/components/questions/types/write-a-short-letter/transform.js +25 -1
  496. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +61 -1
  497. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +196 -1
  498. package/dist/components/questions/types/write-correct-verb-form/index.js +2 -1
  499. package/dist/components/questions/types/write-correct-verb-form/register.js +62 -1
  500. package/dist/components/questions/types/write-correct-verb-form/transform.js +17 -1
  501. package/dist/components/questions/types/write-sentences/WriteSentencesClient.js +22 -1
  502. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +174 -1
  503. package/dist/components/questions/types/write-sentences/index.js +2 -1
  504. package/dist/components/questions/types/write-sentences/register.js +58 -1
  505. package/dist/components/questions/types/write-sentences/transform.js +28 -1
  506. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +96 -1
  507. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +161 -1
  508. package/dist/components/questions/types/write-short-paragraph/index.js +2 -1
  509. package/dist/components/questions/types/write-short-paragraph/register.js +4 -1
  510. package/dist/components/questions/types/write-short-paragraph/transform.js +21 -1
  511. package/dist/components/questions/viewer/ChooseTheCorrectAnswerGroupViewer.js +11 -1
  512. package/dist/components/questions/viewer/CrossOutWordGroupViewer.js +8 -1
  513. package/dist/components/questions/viewer/QuestionGroupViewer.js +36 -1
  514. package/dist/components/questions/viewer/QuestionViewer.js +692 -1
  515. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.d.ts +2 -1
  516. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.js +82 -1
  517. package/dist/components/questions/viewer/TrueFalseGroupViewer.js +11 -1
  518. package/dist/components/questions/viewer/WordFillStructuredFormViewer.js +14 -1
  519. package/dist/components/questions/viewer/WordOrderAndMatchGroupViewer.d.ts +3 -1
  520. package/dist/components/questions/viewer/WordOrderAndMatchGroupViewer.js +95 -1
  521. package/dist/components/questions/viewer/index.js +8 -1
  522. package/dist/components/results/ResultReviewPageContainer.js +220 -1
  523. package/dist/components/results/ReviewQuestionRenderer.js +55 -1
  524. package/dist/components/results/hooks/useResultReviewPolling.js +76 -1
  525. package/dist/components/results/index.js +22 -1
  526. package/dist/components/results/renderers/QuestionExplanations.js +19 -1
  527. package/dist/components/results/renderers/ReviewChooseAdjectiveRenderer.js +94 -1
  528. package/dist/components/results/renderers/ReviewChooseBestAnswerRenderer.js +75 -1
  529. package/dist/components/results/renderers/ReviewColoringRenderer.js +110 -1
  530. package/dist/components/results/renderers/ReviewListenAndWriteRenderer.js +48 -1
  531. package/dist/components/results/renderers/ReviewListenChooseAnswerGroupRenderer.js +87 -1
  532. package/dist/components/results/renderers/ReviewListenChooseObjectsRenderer.js +104 -1
  533. package/dist/components/results/renderers/ReviewListenDragObjectsRenderer.js +123 -1
  534. package/dist/components/results/renderers/ReviewListeningHeader.js +104 -1
  535. package/dist/components/results/renderers/ReviewSpeakingAIPanel.js +116 -1
  536. package/dist/components/results/renderers/ReviewSpeakingMedia.js +48 -1
  537. package/dist/components/results/renderers/ReviewSpeakingQuestionItem.js +42 -1
  538. package/dist/components/results/renderers/ReviewSpeakingRenderer.js +12 -1
  539. package/dist/components/results/renderers/ReviewSpeakingRenderer.types.js +85 -1
  540. package/dist/components/results/renderers/review-question-body-dedicated.js +104 -1
  541. package/dist/components/results/renderers/review-question-body-movers.js +148 -1
  542. package/dist/components/results/renderers/review-speaking-map.js +226 -1
  543. package/dist/components/results/renderers/review-spontaneous-qa-group.d.ts +4 -0
  544. package/dist/components/results/renderers/review-spontaneous-qa-group.js +74 -0
  545. package/dist/components/results/report/BadgeTitle.js +5 -1
  546. package/dist/components/results/report/ExamSkillRow.js +20 -1
  547. package/dist/components/results/report/OceanEduExamReport.js +58 -1
  548. package/dist/components/results/report/OceanEduSpeakingReport.js +62 -1
  549. package/dist/components/results/report/OverallAssessmentSection.js +17 -1
  550. package/dist/components/results/report/ResultErrorState.js +18 -1
  551. package/dist/components/results/report/ResultLoadingState.js +10 -1
  552. package/dist/components/results/report/ResultNotPublicState.js +27 -1
  553. package/dist/components/results/report/SpeakingReportParts.js +14 -1
  554. package/dist/components/results/report/SpeakingReportShared.js +86 -1
  555. package/dist/components/results/report/SpeakingRubricSection.js +171 -1
  556. package/dist/components/results/report/speaking-report.utils.js +198 -1
  557. package/dist/components/results/result-review.utils.js +6 -1
  558. package/dist/components/results/review-data.js +222 -1
  559. package/dist/components/results/review-renderer.utils.js +97 -1
  560. package/dist/components/results/review-status.utils.js +36 -1
  561. package/dist/components/results/review-types.js +1 -1
  562. package/dist/components/results/speaking/SpeakingResultAudioPlayer.js +77 -1
  563. package/dist/components/results/speaking/SpeakingReviewResult.js +48 -1
  564. package/dist/components/results/utils/review-score.utils.js +72 -1
  565. package/dist/components/results/utils/speaking-summary.js +224 -1
  566. package/dist/components/results/utils/transform-speaking-review.js +183 -1
  567. package/dist/components/shared/RichTextEditor.d.ts +9 -0
  568. package/dist/components/shared/RichTextEditor.js +47 -0
  569. package/dist/components/shared/audio/SpeakingAudioPlayer.js +100 -1
  570. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +140 -1
  571. package/dist/components/themes/cambridge-yle/CambridgeYleBottomNav.js +10 -1
  572. package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.d.ts +1 -1
  573. package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.js +18 -1
  574. package/dist/components/themes/cambridge-yle/CambridgeYleExampleText.js +11 -1
  575. package/dist/components/themes/cambridge-yle/CambridgeYleHeader.js +11 -1
  576. package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.d.ts +2 -1
  577. package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.js +19 -1
  578. package/dist/components/themes/cambridge-yle/CambridgeYleNavButton.js +13 -1
  579. package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.d.ts +1 -1
  580. package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.js +3 -1
  581. package/dist/components/themes/cambridge-yle/CambridgeYleProgressBar.js +6 -1
  582. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCard.js +5 -1
  583. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCardFullWidth.js +9 -1
  584. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.d.ts +2 -1
  585. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.js +61 -1
  586. package/dist/components/themes/cambridge-yle/CambridgeYleTimer.d.ts +1 -1
  587. package/dist/components/themes/cambridge-yle/CambridgeYleTimer.js +25 -1
  588. package/dist/components/themes/cambridge-yle/index.js +13 -1
  589. package/dist/components/themes/english-certification/EcAnswerTheQuestion.js +25 -1
  590. package/dist/components/themes/english-certification/EcMatchByWritingAnswer.js +89 -1
  591. package/dist/components/themes/english-certification/EcWordFillParagraph.js +56 -1
  592. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +192 -1
  593. package/dist/components/themes/english-certification/EcWritingTask.js +60 -1
  594. package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +108 -1
  595. package/dist/components/themes/english-certification/EnglishCertificationBrand.js +11 -1
  596. package/dist/components/themes/english-certification/EnglishCertificationExamLayout.js +34 -1
  597. package/dist/components/themes/english-certification/EnglishCertificationFlagButton.js +15 -1
  598. package/dist/components/themes/english-certification/EnglishCertificationFooterBar.js +19 -1
  599. package/dist/components/themes/english-certification/EnglishCertificationGroupedQuestion.js +166 -1
  600. package/dist/components/themes/english-certification/EnglishCertificationHeader.js +31 -1
  601. package/dist/components/themes/english-certification/EnglishCertificationInstructionBanner.js +79 -1
  602. package/dist/components/themes/english-certification/EnglishCertificationNavigationPanel.js +71 -1
  603. package/dist/components/themes/english-certification/EnglishCertificationOptionSelector.js +79 -1
  604. package/dist/components/themes/english-certification/EnglishCertificationPartHeader.js +47 -1
  605. package/dist/components/themes/english-certification/EnglishCertificationPassagePanel.js +77 -1
  606. package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.js +25 -1
  607. package/dist/components/themes/english-certification/EnglishCertificationQuestionItem.js +36 -1
  608. package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +81 -1
  609. package/dist/components/themes/english-certification/EnglishCertificationReadingSection.js +95 -1
  610. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestion.js +18 -1
  611. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js +32 -1
  612. package/dist/components/themes/english-certification/english-cert-scroll.js +163 -1
  613. package/dist/components/themes/english-certification/english-certification-theme.js +58 -1
  614. package/dist/components/themes/english-certification/grouped-question-utils.js +243 -1
  615. package/dist/components/themes/english-certification/index.js +24 -1
  616. package/dist/components/themes/index.js +25 -1
  617. package/dist/components/themes/summer-sky/SummerSkyAudioPlayer.js +170 -1
  618. package/dist/components/themes/summer-sky/SummerSkyExitModal.js +21 -1
  619. package/dist/components/themes/summer-sky/SummerSkyLayout.js +221 -1
  620. package/dist/components/themes/summer-sky/SummerSkySubmitModal.js +18 -1
  621. package/dist/components/themes/summer-sky/SummerSkyWaitingRoom.js +193 -1
  622. package/dist/components/themes/types.js +1 -1
  623. package/dist/components/ui/alert-dialog.js +26 -1
  624. package/dist/components/ui/alert.js +22 -1
  625. package/dist/components/ui/avatar.js +12 -1
  626. package/dist/components/ui/badge.js +38 -1
  627. package/dist/components/ui/button.js +35 -1
  628. package/dist/components/ui/calendar.js +76 -1
  629. package/dist/components/ui/card.js +16 -1
  630. package/dist/components/ui/carousel.js +91 -1
  631. package/dist/components/ui/checkbox.js +9 -1
  632. package/dist/components/ui/collapsible.js +13 -1
  633. package/dist/components/ui/command.js +34 -1
  634. package/dist/components/ui/confirm-dialog.js +49 -1
  635. package/dist/components/ui/dialog.js +23 -1
  636. package/dist/components/ui/drawer.js +35 -1
  637. package/dist/components/ui/dropdown-menu.js +33 -1
  638. package/dist/components/ui/file-upload.js +155 -1
  639. package/dist/components/ui/form.js +62 -1
  640. package/dist/components/ui/hover-card.js +10 -1
  641. package/dist/components/ui/image-preview.js +29 -1
  642. package/dist/components/ui/input.js +8 -1
  643. package/dist/components/ui/label.js +9 -1
  644. package/dist/components/ui/language-switcher.js +15 -1
  645. package/dist/components/ui/multi-select.js +90 -1
  646. package/dist/components/ui/points-input.js +33 -1
  647. package/dist/components/ui/popover.js +26 -1
  648. package/dist/components/ui/progress.js +16 -1
  649. package/dist/components/ui/radio-group.js +12 -1
  650. package/dist/components/ui/scroll-area.js +11 -1
  651. package/dist/components/ui/select.js +38 -1
  652. package/dist/components/ui/separator.js +8 -1
  653. package/dist/components/ui/sheet.js +40 -1
  654. package/dist/components/ui/single-select.js +76 -1
  655. package/dist/components/ui/skeleton.js +6 -1
  656. package/dist/components/ui/switch.js +8 -1
  657. package/dist/components/ui/table.js +20 -1
  658. package/dist/components/ui/tabs.js +17 -1
  659. package/dist/components/ui/textarea.js +8 -1
  660. package/dist/components/ui/tooltip.js +11 -1
  661. package/dist/index.d.ts +3 -0
  662. package/dist/index.js +24 -1
  663. package/dist/shared/constants/ApiConstant.d.ts +1 -0
  664. package/dist/shared/constants/ApiConstant.js +278 -1
  665. package/dist/shared/constants/QueryKeyConstant.js +112 -1
  666. package/dist/shared/constants/exam-level.enum.js +41 -1
  667. package/dist/shared/constants/landings/placement-test.constants.js +431 -1
  668. package/dist/shared/constants/question-skills.js +277 -1
  669. package/dist/shared/constants/routes.js +81 -1
  670. package/dist/shared/index.js +10 -1
  671. package/dist/shared/lib/hooks/index.js +10 -1
  672. package/dist/shared/lib/hooks/useAlertDialog.js +57 -1
  673. package/dist/shared/lib/hooks/useAudioPlayCounter.js +30 -1
  674. package/dist/shared/lib/hooks/useAudioPreview.js +81 -1
  675. package/dist/shared/lib/hooks/useAudioUpload.js +24 -1
  676. package/dist/shared/lib/hooks/useConfirmDialog.js +89 -1
  677. package/dist/shared/lib/hooks/useDebouncedCallback.js +32 -1
  678. package/dist/shared/lib/hooks/useDelayedAutoPlay.js +12 -1
  679. package/dist/shared/lib/hooks/useFileUpload.js +24 -1
  680. package/dist/shared/lib/hooks/useImageUrl.js +5 -1
  681. package/dist/shared/lib/hooks/usePresignedFileUrl.js +108 -1
  682. package/dist/shared/lib/hooks/useReactHookForm.js +31 -1
  683. package/dist/shared/lib/hooks/useToast.js +62 -1
  684. package/dist/shared/lib/html-content.js +22 -1
  685. package/dist/shared/lib/i18n/index.js +54 -1
  686. package/dist/shared/lib/i18n/messages/en/admin.d.ts +11 -4
  687. package/dist/shared/lib/i18n/messages/en/admin.js +2111 -1
  688. package/dist/shared/lib/i18n/messages/en/auth.js +86 -1
  689. package/dist/shared/lib/i18n/messages/en/common.js +140 -1
  690. package/dist/shared/lib/i18n/messages/en/file-upload.js +18 -1
  691. package/dist/shared/lib/i18n/messages/en/student.js +133 -1
  692. package/dist/shared/lib/i18n/messages/en/terms.js +37 -1
  693. package/dist/shared/lib/i18n/messages/en.d.ts +11 -4
  694. package/dist/shared/lib/i18n/messages/en.js +15 -1
  695. package/dist/shared/lib/i18n/messages/vi/admin.d.ts +11 -4
  696. package/dist/shared/lib/i18n/messages/vi/admin.js +2111 -1
  697. package/dist/shared/lib/i18n/messages/vi/auth.js +86 -1
  698. package/dist/shared/lib/i18n/messages/vi/common.js +140 -1
  699. package/dist/shared/lib/i18n/messages/vi/file-upload.js +18 -1
  700. package/dist/shared/lib/i18n/messages/vi/student.js +133 -1
  701. package/dist/shared/lib/i18n/messages/vi/terms.js +37 -1
  702. package/dist/shared/lib/i18n/messages/vi.d.ts +11 -4
  703. package/dist/shared/lib/i18n/messages/vi.js +15 -1
  704. package/dist/shared/lib/i18n/types.js +1 -1
  705. package/dist/shared/lib/stores/examQuestionStore.js +212 -1
  706. package/dist/shared/lib/stores/examTakingStore.js +240 -1
  707. package/dist/shared/lib/stores/gradingTransientStore.js +39 -1
  708. package/dist/shared/lib/stores/localeStore.js +10 -1
  709. package/dist/shared/lib/themes/config/starters.config.js +71 -1
  710. package/dist/shared/lib/themes/index.js +49 -1
  711. package/dist/shared/lib/themes/types.js +2 -1
  712. package/dist/shared/lib/toast.js +43 -1
  713. package/dist/shared/lib/utils/public-page.js +44 -1
  714. package/dist/shared/lib/utils/question-reverse-transform.js +438 -1
  715. package/dist/shared/lib/utils/question-transform-types.js +1 -1
  716. package/dist/shared/lib/utils/question-transform-validation.js +194 -1
  717. package/dist/shared/lib/utils/question-transform.js +199 -1
  718. package/dist/shared/lib/utils.js +168 -1
  719. package/dist/shared/lib/validation/choose-correct-answer.validation.js +111 -1
  720. package/dist/shared/lib/validation/label-the-picture.validation.js +19 -1
  721. package/dist/shared/lib/validation/listen-and-choose-from-answer-group.validation.js +46 -1
  722. package/dist/shared/lib/validation/listen-and-choose-objects-in-scene.validation.js +21 -1
  723. package/dist/shared/lib/validation/listen-and-drag-objects-into-scene.validation.js +34 -1
  724. package/dist/shared/lib/validation/listen-and-speak-answer.validation.js +40 -1
  725. package/dist/shared/lib/validation/listen-and-speak-compare-images.validation.js +45 -1
  726. package/dist/shared/lib/validation/listen-and-speak-image-group.validation.js +19 -1
  727. package/dist/shared/lib/validation/listen-and-speak-info-exchange.validation.js +39 -1
  728. package/dist/shared/lib/validation/listen-and-speak-odd-one-out.validation.js +32 -1
  729. package/dist/shared/lib/validation/listen-and-speak-question-list.validation.js +19 -1
  730. package/dist/shared/lib/validation/listen-and-speak-with-story-images.validation.js +28 -1
  731. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +12 -1
  732. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +47 -1
  733. package/dist/shared/lib/validation/read-and-color-objects.validation.js +29 -1
  734. package/dist/shared/lib/validation/read-displayed-content.validation.js +28 -1
  735. package/dist/shared/lib/validation/speaking-conversation.validation.js +75 -1
  736. package/dist/shared/lib/validation/speaking-describe-image.validation.js +31 -1
  737. package/dist/shared/lib/validation/spontaneous-qa.validation.js +48 -1
  738. package/dist/shared/lib/validation/word-fill-paragraph-default.validation.js +38 -1
  739. package/dist/shared/lib/validation/word-fill-paragraph-with-options.validation.js +39 -1
  740. package/dist/shared/lib/validation/word-fill-paragraph.validation.js +43 -1
  741. package/dist/shared/lib/validation/word-ordering.validation.js +33 -1
  742. package/dist/shared/types/branch.types.js +6 -1
  743. package/dist/shared/types/common.types.js +49 -1
  744. package/dist/shared/types/entities/exam-schedule.types.js +5 -1
  745. package/dist/shared/types/entities/exam.types.js +1 -1
  746. package/dist/shared/types/entities/question.types.js +4 -1
  747. package/dist/shared/types/entities/result.types.js +4 -1
  748. package/dist/shared/types/entities/user.types.js +4 -1
  749. package/dist/shared/types/exam-taking.types.js +2 -1
  750. package/dist/shared/types/history.types.js +34 -1
  751. package/dist/shared/types/index.js +1 -1
  752. package/dist/shared/types/questions/choose-correct-adjective.js +4 -1
  753. package/dist/shared/types/questions/choose-the-correct-answer-group.d.ts +1 -0
  754. package/dist/shared/types/questions/choose-the-correct-answer-group.js +1 -1
  755. package/dist/shared/types/questions/choose-the-correct-answer.js +9 -1
  756. package/dist/shared/types/questions/cross-out-word-group.d.ts +2 -0
  757. package/dist/shared/types/questions/cross-out-word-group.js +1 -1
  758. package/dist/shared/types/questions/fill-missing-words-in-grid.js +11 -1
  759. package/dist/shared/types/questions/index.d.ts +1 -0
  760. package/dist/shared/types/questions/index.js +46 -1
  761. package/dist/shared/types/questions/listen-and-speak-answer.js +1 -1
  762. package/dist/shared/types/questions/listen-and-speak-compare-images.js +1 -1
  763. package/dist/shared/types/questions/listen-and-speak-image-group.js +1 -1
  764. package/dist/shared/types/questions/listen-and-speak-info-exchange.js +3 -1
  765. package/dist/shared/types/questions/listen-and-speak-odd-one-out.js +3 -1
  766. package/dist/shared/types/questions/listen-and-speak-question-list.js +1 -1
  767. package/dist/shared/types/questions/listen-and-speak-with-story-images.js +3 -1
  768. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +5 -1
  769. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +9 -1
  770. package/dist/shared/types/questions/look-picture-fill-word-hint.js +7 -1
  771. package/dist/shared/types/questions/read-displayed-content.js +1 -1
  772. package/dist/shared/types/questions/speaking-conversation.js +7 -1
  773. package/dist/shared/types/questions/speaking-describe-image.js +1 -1
  774. package/dist/shared/types/questions/spontaneous-qa.js +1 -1
  775. package/dist/shared/types/questions/word-order-and-match-group.d.ts +33 -0
  776. package/dist/shared/types/questions/word-order-and-match-group.js +1 -0
  777. package/dist/shared/types/questions/write-correct-verb-form.js +12 -1
  778. package/dist/shared/types/role.types.js +1 -1
  779. package/dist/shared/types/structure-management.js +28 -1
  780. package/dist/shared/types/student.types.js +57 -1
  781. package/dist/shared/types/teacher-dashboard-rooms.js +2 -1
  782. package/package.json +184 -166
  783. package/dist/mcp/config.d.ts +0 -12
  784. package/dist/mcp/config.js +0 -1
  785. package/dist/mcp/http-client.d.ts +0 -6
  786. package/dist/mcp/http-client.js +0 -1
  787. package/dist/mcp/index.d.ts +0 -6
  788. package/dist/mcp/index.js +0 -2
  789. package/dist/mcp/tools/_helpers.d.ts +0 -4
  790. package/dist/mcp/tools/_helpers.js +0 -1
  791. package/dist/mcp/tools/exams.d.ts +0 -2
  792. package/dist/mcp/tools/exams.js +0 -1
  793. package/dist/mcp/tools/index.d.ts +0 -2
  794. package/dist/mcp/tools/index.js +0 -1
  795. package/dist/mcp/tools/results.d.ts +0 -2
  796. package/dist/mcp/tools/results.js +0 -1
@@ -1 +1,166 @@
1
- "use client";import{jsx as e,jsxs as s}from"react/jsx-runtime";import{FileText as t,Info as a}from"lucide-react";import{EnglishCertificationQuestionCard as i}from"./EnglishCertificationQuestionCard";import{EnglishCertificationPassagePanel as r}from"./EnglishCertificationPassagePanel";import{EnglishCertificationReadingSection as n}from"./EnglishCertificationReadingSection";import{EnglishCertificationAudioPlayer as o}from"./EnglishCertificationAudioPlayer";import{EnglishCertificationInstructionBanner as l}from"./EnglishCertificationInstructionBanner";import{EnglishCertificationPartHeader as d}from"./EnglishCertificationPartHeader";import{ENGLISH_CERT_CONTENT_WIDTH_CLASS as c}from"./english-certification-theme";import{ENGLISH_CERT_STICKY_AUDIO_ATTR as p,englishCertQuestionDomId as g}from"./english-cert-scroll";import{isHtmlContent as u,stripInstruction as m}from"./grouped-question-utils";import{ResolvedImage as f}from"../../../components/common/ResolvedImage";function h(e){const s=e.articles;return Array.isArray(s)?s:[]}function x(e){return e.some(e=>Boolean(e?.content?.trim())||Boolean(e?.imageUrl))}export function EnglishCertificationGroupedQuestion({part:b,partConfig:w,answers:N,flaggedQuestions:v,onAnswerChange:I,onToggleFlag:q,isReviewMode:y=!1,isListeningPart:C=!1,partAudioUrl:T,isAudioLocked:U=!1,onAudioFirstPlay:_}){const P=w.startIndex??0,k=function(e,s){const t=e.questions[0];return(t?.questionGroup?.payload?.instructions??(s?.trim()||void 0)??t?.content?.question??"").trim()}(b,b.instructions??w.instructions),A=b.sections?.length?[(()=>{const e=b.questions[0]?.questionGroup?.payload??{},s=e.content??e.passage??"",t=m(s,k),a=e.imageUrl??e.passageImageUrl??"",i=h(e);return{groupId:b.partId,questions:b.questions,passage:t,passageTitle:e.passageTitle??e.title??"",passageSubtitle:e.subtitle??"",passageImageUrl:a,articles:i,hasPassageContent:Boolean(t.trim())||Boolean(a)||x(i)}})()]:function(e,s){const t=[],a=new Map;return e.questions.forEach(e=>{const s=e.group_id??e.id;a.has(s)||(a.set(s,[]),t.push(s)),a.get(s).push(e)}),t.map(e=>{const t=a.get(e),i=t[0]?.questionGroup?.payload??{},r=i.content??i.passage??"",n=m(r,s),o=i.imageUrl??i.passageImageUrl??"",l=h(i);return{groupId:e,questions:t,passage:n,passageTitle:i.passageTitle??i.title??"",passageSubtitle:i.subtitle??"",passageImageUrl:o,articles:l,hasPassageContent:Boolean(n.trim())||Boolean(o)||x(l)}})}(b,k);if(!A.length)return e("div",{className:"flex h-full items-center justify-center rounded-xl border border-red-200 bg-red-50 p-8 text-red-600",children:"No questions found for this part"});const B=A.some(e=>e.hasPassageContent),E=C&&Boolean(T),F=s=>b.sections?.length?e("div",{className:"flex-1 space-y-6 px-4 py-4",children:b.sections.map((t,a)=>{const i=s.questions.filter(e=>{const s=b.questions.indexOf(e);return s>=t.startIndex-P&&s<=t.endIndex-P});return i.length?e(n,{part:b,section:t,questions:i,startIndex:P,answers:N,flaggedQuestions:v,onAnswerChange:I,onToggleFlag:q,isReviewMode:y},`${s.groupId}-section-${a}`):null})}):e("div",{className:"flex-1 space-y-3 px-4 py-4",children:s.questions.map(s=>{const t=b.questions.indexOf(s)+1;return e(i,{question:s,globalQuestionNumber:P+t,answer:N[s.id],isFlagged:v.has(s.id),sharedInstruction:k,onAnswerChange:I,onToggleFlag:q,isReviewMode:y,domId:g(s.id)},s.id)})});return s("div",{className:"flex h-full flex-col gap-6 overflow-y-auto pb-4",children:[e(d,{partName:b.name,partNo:b.partNo,skill:w.category,isFullWidth:B}),e(l,{instruction:k,isListeningPart:C,isFullWidth:B}),E&&e("div",{[p]:"",className:`sticky top-0 z-20 ${c} w-full self-center`,children:e(o,{audioUrl:T,partNumber:b.partNo,isLocked:U,onFirstPlay:_})}),A.map(i=>{const n=b.questions.indexOf(i.questions[0])+P+1,o=b.questions.indexOf(i.questions[i.questions.length-1])+P+1,l=u(i.passage);return i.hasPassageContent?s("div",{className:"flex flex-col gap-3",children:[e("div",{className:"flex flex-wrap items-center gap-3 rounded-xl border border-[#d9e4f2] bg-white/85 px-4 py-3 shadow-[0_5px_16px_rgba(13,71,161,0.08)] backdrop-blur-md",children:s("div",{className:"flex items-center gap-2.5 rounded-md border border-[#bfd8f8] bg-white/70 px-3 py-1.5 text-base font-bold text-[#0b57d0]",children:[e("span",{className:"grid h-[18px] w-[18px] place-items-center rounded-full bg-[#1167e8] text-white",children:e(a,{className:"h-3 w-3"})}),"Questions ",n,"–",o," refer to the following passage."]})}),s("div",{className:"grid gap-4",style:{gridTemplateColumns:"minmax(0, 1fr) minmax(0, 1fr)"},children:[e("div",{className:"h-full max-h-[calc(100vh-220px)] min-h-0 overflow-y-auto",children:e(r,{passage:i.passage,passageTitle:i.passageTitle,passageSubtitle:i.passageSubtitle,passageImageUrl:i.passageImageUrl,articles:i.articles,passageIsHtml:l})}),e("section",{className:"flex max-h-[calc(100vh-220px)] min-h-0 flex-col overflow-y-auto rounded-xl border border-[#d9e4f2] bg-white/90 shadow-[0_14px_35px_rgba(13,71,161,0.12)] backdrop-blur-md",children:F(i)})]})]},i.groupId):s("div",{className:`${c} flex w-full flex-col gap-3 self-center`,children:[s("div",{className:"flex items-center gap-4 rounded-xl border border-[#d9e4f2] bg-white/90 px-5 py-4 shadow-[0_5px_16px_rgba(13,71,161,0.08)] backdrop-blur-md",children:[e("span",{className:"grid h-12 w-12 shrink-0 place-items-center rounded-xl bg-[#eaf2ff] text-[#1167e8]",children:e(t,{className:"h-6 w-6"})}),s("div",{className:"min-w-0 flex-1",children:[s("span",{className:"inline-flex items-center gap-1.5 rounded-md border border-[#bfd8f8] bg-[#eaf2ff] px-2.5 py-1 text-xs font-bold text-[#0b57d0]",children:["Questions ",n,"–",o]}),i.passageTitle&&e("p",{className:"mt-2 text-lg font-bold leading-tight text-[#0f1730]",children:i.passageTitle}),i.passageSubtitle&&e("p",{className:"mt-1 text-[15px] font-medium text-[#475569]",children:i.passageSubtitle})]}),i.passageImageUrl&&e(f,{src:i.passageImageUrl,alt:i.passageTitle||"Illustration",className:"h-20 w-32 shrink-0 rounded-lg object-contain"})]}),e("section",{className:"flex flex-col overflow-hidden rounded-xl border border-[#d9e4f2] bg-white/90 shadow-[0_14px_35px_rgba(13,71,161,0.12)] backdrop-blur-md",children:F(i)})]},i.groupId)})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { FileText, Info } from 'lucide-react';
4
+ import { EnglishCertificationQuestionCard } from './EnglishCertificationQuestionCard';
5
+ import { EnglishCertificationPassagePanel } from './EnglishCertificationPassagePanel';
6
+ import { EnglishCertificationReadingSection } from './EnglishCertificationReadingSection';
7
+ import { EnglishCertificationAudioPlayer } from './EnglishCertificationAudioPlayer';
8
+ import { EnglishCertificationInstructionBanner } from './EnglishCertificationInstructionBanner';
9
+ import { EnglishCertificationPartHeader } from './EnglishCertificationPartHeader';
10
+ import { ENGLISH_CERT_CONTENT_WIDTH_CLASS } from './english-certification-theme';
11
+ import { ENGLISH_CERT_STICKY_AUDIO_ATTR, englishCertQuestionDomId, } from './english-cert-scroll';
12
+ import { isHtmlContent, stripInstruction } from './grouped-question-utils';
13
+ import { ResolvedImage } from '../../../components/common/ResolvedImage';
14
+ /** Fixed split layout: equal-width passage / questions columns. */
15
+ const SPLIT_VIEW_GRID = 'minmax(0, 1fr) minmax(0, 1fr)';
16
+ /** Extracts the article list from a group payload (empty when absent). */
17
+ function extractArticles(payload) {
18
+ const raw = payload.articles;
19
+ return Array.isArray(raw) ? raw : [];
20
+ }
21
+ /** True when any article carries body text or an image worth displaying. */
22
+ function articlesHaveContent(articles) {
23
+ return articles.some((article) => Boolean(article?.content?.trim()) || Boolean(article?.imageUrl));
24
+ }
25
+ /**
26
+ * Splits a grouped reading part into its individual document groups while
27
+ * preserving the original question order. Each document keeps its own passage
28
+ * payload so the layout can stack them vertically on a single screen.
29
+ */
30
+ function buildDocumentGroups(part, sharedInstruction) {
31
+ const order = [];
32
+ const byGroup = new Map();
33
+ part.questions.forEach((question) => {
34
+ const groupId = question.group_id ?? question.id;
35
+ if (!byGroup.has(groupId)) {
36
+ byGroup.set(groupId, []);
37
+ order.push(groupId);
38
+ }
39
+ byGroup.get(groupId).push(question);
40
+ });
41
+ return order.map((groupId) => {
42
+ const questions = byGroup.get(groupId);
43
+ const payload = questions[0]?.questionGroup?.payload ?? {};
44
+ const rawPassage = payload.content ?? payload.passage ?? '';
45
+ const passage = stripInstruction(rawPassage, sharedInstruction);
46
+ const passageImageUrl = payload.imageUrl ?? payload.passageImageUrl ?? '';
47
+ const articles = extractArticles(payload);
48
+ return {
49
+ groupId,
50
+ questions,
51
+ passage,
52
+ // API exposes the group heading as `title`; older Reading payloads use
53
+ // `passageTitle`. Fall back to `title` so grouped MCQ headings show.
54
+ passageTitle: payload.passageTitle ?? payload.title ?? '',
55
+ passageSubtitle: payload.subtitle ?? '',
56
+ passageImageUrl,
57
+ articles,
58
+ hasPassageContent: Boolean(passage.trim()) || Boolean(passageImageUrl) || articlesHaveContent(articles),
59
+ };
60
+ });
61
+ }
62
+ /**
63
+ * Resolves the shared part instruction.
64
+ *
65
+ * Priority:
66
+ * 1. `questionGroup.payload.instructions` — used by some grouped question types.
67
+ * 2. `partInstructions` — the template-level instruction from the QuestionPart
68
+ * (e.g. Part 6 TOEIC where the instruction lives on the template part).
69
+ * 3. `content.question` of the first question — legacy fallback.
70
+ */
71
+ function resolveSharedInstruction(part, partInstructions) {
72
+ const first = part.questions[0];
73
+ return (first?.questionGroup?.payload?.instructions ??
74
+ (partInstructions?.trim() || undefined) ??
75
+ first?.content?.question ??
76
+ '').trim();
77
+ }
78
+ /**
79
+ * Ocean Edu branded split-view renderer for grouped reading parts.
80
+ *
81
+ * Renders every document of the part stacked vertically. Each document shows a
82
+ * reading passage (title, optional image, body, source bar) on the left and its
83
+ * MCQ questions on the right in a balanced 50/50 split.
84
+ */
85
+ export function EnglishCertificationGroupedQuestion({ part, partConfig, answers, flaggedQuestions, onAnswerChange, onToggleFlag, isReviewMode = false, isListeningPart = false, partAudioUrl, isAudioLocked = false, onAudioFirstPlay, }) {
86
+ const startIndex = partConfig.startIndex ?? 0;
87
+ // Part-level instruction (e.g. "Read the texts that follow…") is shared by
88
+ // every document. Render it once at the top and strip it from each passage
89
+ // (the API prepends it to every document's content) to avoid duplication.
90
+ // Prefer part.instructions (set by groupQuestionsByPart from the template)
91
+ // which covers TOEIC Part 6 where the instruction lives on the template part
92
+ // rather than inside the question group payload.
93
+ const sharedInstruction = resolveSharedInstruction(part, part.instructions ?? partConfig.instructions);
94
+ // When the part carries template sections, render every question in a single
95
+ // document so the section loop in renderQuestions runs once and stacks each
96
+ // sub-section. Splitting by group_id here would duplicate the section headings
97
+ // and break per-section dispatch.
98
+ //
99
+ // The document still carries the shared passage when present: IELTS Reading
100
+ // parts ship a DOCUMENT group whose passage lives on each question's
101
+ // `questionGroup.payload` (content/title/subtitle). Surfacing it flips
102
+ // `hasPassageContent` to true so the split branch shows passage-left /
103
+ // questions-right (TOEIC Part 7 style). Mixed Listening parts (SECTION 2:
104
+ // CHOOSE + MATCH + WORD_FILL) carry no passage, so this stays false and the
105
+ // full-width no-passage branch is preserved.
106
+ const buildSectionedDocument = () => {
107
+ const payload = (part.questions[0]?.questionGroup?.payload ?? {});
108
+ const rawPassage = (payload.content ?? payload.passage ?? '');
109
+ const passage = stripInstruction(rawPassage, sharedInstruction);
110
+ const passageImageUrl = (payload.imageUrl ?? payload.passageImageUrl ?? '');
111
+ const articles = extractArticles(payload);
112
+ return {
113
+ groupId: part.partId,
114
+ questions: part.questions,
115
+ passage,
116
+ passageTitle: (payload.passageTitle ?? payload.title ?? ''),
117
+ passageSubtitle: (payload.subtitle ?? ''),
118
+ passageImageUrl,
119
+ articles,
120
+ hasPassageContent: Boolean(passage.trim()) || Boolean(passageImageUrl) || articlesHaveContent(articles),
121
+ };
122
+ };
123
+ const documents = part.sections?.length
124
+ ? [buildSectionedDocument()]
125
+ : buildDocumentGroups(part, sharedInstruction);
126
+ if (!documents.length) {
127
+ return (_jsx("div", { className: "flex h-full items-center justify-center rounded-xl border border-red-200 bg-red-50 p-8 text-red-600", children: "No questions found for this part" }));
128
+ }
129
+ const isFullWidth = documents.some((doc) => doc.hasPassageContent);
130
+ const showAudio = isListeningPart && Boolean(partAudioUrl);
131
+ /** Renders a document's question list (used by both split and full-width layouts). */
132
+ const renderQuestions = (document) => {
133
+ if (part.sections?.length) {
134
+ return (_jsx("div", { className: "flex-1 space-y-6 px-4 py-4", children: part.sections.map((section, sectionIdx) => {
135
+ const sectionQuestions = document.questions.filter((question) => {
136
+ const pos = part.questions.indexOf(question);
137
+ return (pos >= section.startIndex - startIndex &&
138
+ pos <= section.endIndex - startIndex);
139
+ });
140
+ if (!sectionQuestions.length)
141
+ return null;
142
+ return (_jsx(EnglishCertificationReadingSection, { part: part, section: section, questions: sectionQuestions, startIndex: startIndex, answers: answers, flaggedQuestions: flaggedQuestions, onAnswerChange: onAnswerChange, onToggleFlag: onToggleFlag, isReviewMode: isReviewMode }, `${document.groupId}-section-${sectionIdx}`));
143
+ }) }));
144
+ }
145
+ return (_jsx("div", { className: "flex-1 space-y-3 px-4 py-4", children: document.questions.map((question) => {
146
+ const questionNumber = part.questions.indexOf(question) + 1;
147
+ const globalQuestionNumber = startIndex + questionNumber;
148
+ return (_jsx(EnglishCertificationQuestionCard, { question: question, globalQuestionNumber: globalQuestionNumber, answer: answers[question.id], isFlagged: flaggedQuestions.has(question.id), sharedInstruction: sharedInstruction, onAnswerChange: onAnswerChange, onToggleFlag: onToggleFlag, isReviewMode: isReviewMode, domId: englishCertQuestionDomId(question.id) }, question.id));
149
+ }) }));
150
+ };
151
+ return (_jsxs("div", { className: "flex h-full flex-col gap-6 overflow-y-auto pb-4", children: [_jsx(EnglishCertificationPartHeader, { partName: part.name, partNo: part.partNo, skill: partConfig.category, isFullWidth: isFullWidth }), _jsx(EnglishCertificationInstructionBanner, { instruction: sharedInstruction, isListeningPart: isListeningPart, isFullWidth: isFullWidth }), showAudio && (_jsx("div", { [ENGLISH_CERT_STICKY_AUDIO_ATTR]: '', className: `sticky top-0 z-20 ${ENGLISH_CERT_CONTENT_WIDTH_CLASS} w-full self-center`, children: _jsx(EnglishCertificationAudioPlayer, { audioUrl: partAudioUrl, partNumber: part.partNo, isLocked: isAudioLocked, onFirstPlay: onAudioFirstPlay }) })), documents.map((document) => {
152
+ const firstNumber = part.questions.indexOf(document.questions[0]) + startIndex + 1;
153
+ const lastNumber = part.questions.indexOf(document.questions[document.questions.length - 1]) +
154
+ startIndex +
155
+ 1;
156
+ const passageIsHtml = isHtmlContent(document.passage);
157
+ // No-passage group (e.g. Listening grouped MCQ that only carries a
158
+ // heading like "Choose the correct letter, A, B or C"): hide the empty
159
+ // passage panel and render the group title as a banner above the
160
+ // full-width questions.
161
+ if (!document.hasPassageContent) {
162
+ return (_jsxs("div", { className: `${ENGLISH_CERT_CONTENT_WIDTH_CLASS} flex w-full flex-col gap-3 self-center`, children: [_jsxs("div", { className: "flex items-center gap-4 rounded-xl border border-[#d9e4f2] bg-white/90 px-5 py-4 shadow-[0_5px_16px_rgba(13,71,161,0.08)] backdrop-blur-md", children: [_jsx("span", { className: "grid h-12 w-12 shrink-0 place-items-center rounded-xl bg-[#eaf2ff] text-[#1167e8]", children: _jsx(FileText, { className: "h-6 w-6" }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-md border border-[#bfd8f8] bg-[#eaf2ff] px-2.5 py-1 text-xs font-bold text-[#0b57d0]", children: ["Questions ", firstNumber, "\u2013", lastNumber] }), document.passageTitle && (_jsx("p", { className: "mt-2 text-lg font-bold leading-tight text-[#0f1730]", children: document.passageTitle })), document.passageSubtitle && (_jsx("p", { className: "mt-1 text-[15px] font-medium text-[#475569]", children: document.passageSubtitle }))] }), document.passageImageUrl && (_jsx(ResolvedImage, { src: document.passageImageUrl, alt: document.passageTitle || 'Illustration', className: "h-20 w-32 shrink-0 rounded-lg object-contain" }))] }), _jsx("section", { className: "flex flex-col overflow-hidden rounded-xl border border-[#d9e4f2] bg-white/90 shadow-[0_14px_35px_rgba(13,71,161,0.12)] backdrop-blur-md", children: renderQuestions(document) })] }, document.groupId));
163
+ }
164
+ return (_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("div", { className: "flex flex-wrap items-center gap-3 rounded-xl border border-[#d9e4f2] bg-white/85 px-4 py-3 shadow-[0_5px_16px_rgba(13,71,161,0.08)] backdrop-blur-md", children: _jsxs("div", { className: "flex items-center gap-2.5 rounded-md border border-[#bfd8f8] bg-white/70 px-3 py-1.5 text-base font-bold text-[#0b57d0]", children: [_jsx("span", { className: "grid h-[18px] w-[18px] place-items-center rounded-full bg-[#1167e8] text-white", children: _jsx(Info, { className: "h-3 w-3" }) }), "Questions ", firstNumber, "\u2013", lastNumber, " refer to the following passage."] }) }), _jsxs("div", { className: "grid gap-4", style: { gridTemplateColumns: SPLIT_VIEW_GRID }, children: [_jsx("div", { className: "h-full max-h-[calc(100vh-220px)] min-h-0 overflow-y-auto", children: _jsx(EnglishCertificationPassagePanel, { passage: document.passage, passageTitle: document.passageTitle, passageSubtitle: document.passageSubtitle, passageImageUrl: document.passageImageUrl, articles: document.articles, passageIsHtml: passageIsHtml }) }), _jsx("section", { className: "flex max-h-[calc(100vh-220px)] min-h-0 flex-col overflow-y-auto rounded-xl border border-[#d9e4f2] bg-white/90 shadow-[0_14px_35px_rgba(13,71,161,0.12)] backdrop-blur-md", children: renderQuestions(document) })] })] }, document.groupId));
165
+ })] }));
166
+ }
@@ -1 +1,31 @@
1
- "use client";import{jsx as e,jsxs as t,Fragment as a}from"react/jsx-runtime";import{Loader2 as i}from"lucide-react";import{cn as n}from"../../../shared/lib/utils";import{EnglishCertificationBrand as s}from"./EnglishCertificationBrand";function l(e){const t=Math.floor(e/3600),a=Math.floor(e%3600/60),i=e%60;return`${String(t).padStart(2,"0")}:${String(a).padStart(2,"0")}:${String(i).padStart(2,"0")}`}export function EnglishCertificationHeader({examName:r,remainingSeconds:d,totalQuestions:c,answeredCount:o,isSubmitting:m,onSubmit:x,lastSavedAt:h,isSaving:b,partLabel:p,partSubLabel:g,questionRange:f}){const u=d<=900&&d>300,N=d<=300;return t("header",{className:n("sticky top-0 z-50 flex h-[74px] items-center gap-4 border-b border-[#d9e4f2] px-4 lg:px-6","bg-white/95 shadow-[0_8px_26px_rgba(10,61,154,0.08)] backdrop-blur-md"),children:[e("div",{className:"flex shrink-0 items-center gap-3",children:e("div",{className:"hidden border-r border-[#d9e4f2] pr-4 sm:block",children:e(s,{})})}),t("div",{className:"hidden min-w-0 lg:block",children:[e("strong",{className:"block truncate text-[17px] font-semibold leading-tight text-[#0f1730]",children:r}),e("span",{className:"mt-1 block text-sm font-bold text-[#64748b]",children:"English Certification"})]}),t("div",{className:"flex flex-1 items-center justify-center gap-5 text-center",children:[p&&t("div",{className:"leading-tight text-[#0b57d0]",children:[e("div",{className:"text-sm font-black tracking-wide",children:p}),g&&e("div",{className:"mt-1 text-xs font-semibold text-[#27324a]",children:g})]}),f&&t("div",{className:"whitespace-nowrap text-[15px] font-bold text-[#0f1730]",children:["Questions ",e("span",{className:"text-[#0b57d0]",children:f})]})]}),t("div",{className:"flex shrink-0 flex-col items-center border-l border-[#d9e4f2] pl-4",children:[e("span",{className:n("font-mono text-xl font-black leading-none tracking-wide tabular-nums lg:text-2xl",!u&&!N&&"text-[#0b57d0]",u&&"text-orange-500",N&&"animate-pulse text-red-500"),"aria-live":"polite","aria-label":`Time remaining: ${l(d)}`,children:l(d)}),e("span",{className:"mt-1 text-[11px] font-semibold text-[#64748b]",children:"Time Remaining"}),(b||h)&&e("span",{className:"mt-0.5 flex items-center gap-1 text-[10px] text-slate-400",children:b?t(a,{children:[e(i,{className:"h-2.5 w-2.5 animate-spin"}),"Saving..."]}):h?t(a,{children:["Saved ",(v=h,v.toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",hour12:!1}))]}):null})]}),t("div",{className:"flex shrink-0 items-center gap-3",children:[t("div",{className:"hidden text-right sm:block",children:[t("div",{className:"text-sm font-semibold text-[#0f1730]",children:[e("span",{className:"text-[#0b57d0]",children:o}),t("span",{className:"text-slate-400",children:["/",c]})]}),e("div",{className:"text-[11px] text-slate-400",children:"Answered"})]}),e("button",{type:"button",onClick:x,disabled:m,className:n("inline-flex h-12 items-center gap-1.5 rounded-lg px-5 text-sm font-black tracking-wide text-white","bg-gradient-to-b from-[#ffad31] to-[#ff8a00] shadow-[0_12px_24px_rgba(255,138,0,0.3)]","transition-all hover:-translate-y-0.5 hover:brightness-[0.98]","disabled:cursor-not-allowed disabled:opacity-60"),children:m?t(a,{children:[e(i,{className:"h-4 w-4 animate-spin"}),"Submitting..."]}):"SUBMIT TEST"})]})]});var v}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { Loader2 } from 'lucide-react';
4
+ import { cn } from '../../../shared/lib/utils';
5
+ import { EnglishCertificationBrand } from './EnglishCertificationBrand';
6
+ /**
7
+ * Formats seconds into HH:MM:SS string.
8
+ * Always shows hours for the 2-hour standardized exams.
9
+ */
10
+ function formatTime(seconds) {
11
+ const h = Math.floor(seconds / 3600);
12
+ const m = Math.floor((seconds % 3600) / 60);
13
+ const s = seconds % 60;
14
+ return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
15
+ }
16
+ /**
17
+ * Formats a Date into HH:MM local time string.
18
+ */
19
+ function formatSavedAt(date) {
20
+ return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false });
21
+ }
22
+ /**
23
+ * Branded Ocean Edu top header bar for English Certification exam taking.
24
+ *
25
+ * Layout (desktop): brand block · test name · part/range meta · timer · submit.
26
+ */
27
+ export function EnglishCertificationHeader({ examName, remainingSeconds, totalQuestions, answeredCount, isSubmitting, onSubmit, lastSavedAt, isSaving, partLabel, partSubLabel, questionRange, }) {
28
+ const isWarning = remainingSeconds <= 900 && remainingSeconds > 300; // 5-15 min
29
+ const isCritical = remainingSeconds <= 300; // ≤5 min
30
+ return (_jsxs("header", { className: cn('sticky top-0 z-50 flex h-[74px] items-center gap-4 border-b border-[#d9e4f2] px-4 lg:px-6', 'bg-white/95 shadow-[0_8px_26px_rgba(10,61,154,0.08)] backdrop-blur-md'), children: [_jsx("div", { className: "flex shrink-0 items-center gap-3", children: _jsx("div", { className: "hidden border-r border-[#d9e4f2] pr-4 sm:block", children: _jsx(EnglishCertificationBrand, {}) }) }), _jsxs("div", { className: "hidden min-w-0 lg:block", children: [_jsx("strong", { className: "block truncate text-[17px] font-semibold leading-tight text-[#0f1730]", children: examName }), _jsx("span", { className: "mt-1 block text-sm font-bold text-[#64748b]", children: "English Certification" })] }), _jsxs("div", { className: "flex flex-1 items-center justify-center gap-5 text-center", children: [partLabel && (_jsxs("div", { className: "leading-tight text-[#0b57d0]", children: [_jsx("div", { className: "text-sm font-black tracking-wide", children: partLabel }), partSubLabel && (_jsx("div", { className: "mt-1 text-xs font-semibold text-[#27324a]", children: partSubLabel }))] })), questionRange && (_jsxs("div", { className: "whitespace-nowrap text-[15px] font-bold text-[#0f1730]", children: ["Questions ", _jsx("span", { className: "text-[#0b57d0]", children: questionRange })] }))] }), _jsxs("div", { className: "flex shrink-0 flex-col items-center border-l border-[#d9e4f2] pl-4", children: [_jsx("span", { className: cn('font-mono text-xl font-black leading-none tracking-wide tabular-nums lg:text-2xl', !isWarning && !isCritical && 'text-[#0b57d0]', isWarning && 'text-orange-500', isCritical && 'animate-pulse text-red-500'), "aria-live": "polite", "aria-label": `Time remaining: ${formatTime(remainingSeconds)}`, children: formatTime(remainingSeconds) }), _jsx("span", { className: "mt-1 text-[11px] font-semibold text-[#64748b]", children: "Time Remaining" }), (isSaving || lastSavedAt) && (_jsx("span", { className: "mt-0.5 flex items-center gap-1 text-[10px] text-slate-400", children: isSaving ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-2.5 w-2.5 animate-spin" }), "Saving..."] })) : lastSavedAt ? (_jsxs(_Fragment, { children: ["Saved ", formatSavedAt(lastSavedAt)] })) : null }))] }), _jsxs("div", { className: "flex shrink-0 items-center gap-3", children: [_jsxs("div", { className: "hidden text-right sm:block", children: [_jsxs("div", { className: "text-sm font-semibold text-[#0f1730]", children: [_jsx("span", { className: "text-[#0b57d0]", children: answeredCount }), _jsxs("span", { className: "text-slate-400", children: ["/", totalQuestions] })] }), _jsx("div", { className: "text-[11px] text-slate-400", children: "Answered" })] }), _jsx("button", { type: "button", onClick: onSubmit, disabled: isSubmitting, className: cn('inline-flex h-12 items-center gap-1.5 rounded-lg px-5 text-sm font-black tracking-wide text-white', 'bg-gradient-to-b from-[#ffad31] to-[#ff8a00] shadow-[0_12px_24px_rgba(255,138,0,0.3)]', 'transition-all hover:-translate-y-0.5 hover:brightness-[0.98]', 'disabled:cursor-not-allowed disabled:opacity-60'), children: isSubmitting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), "Submitting..."] })) : ('SUBMIT TEST') })] })] }));
31
+ }
@@ -1 +1,79 @@
1
- "use client";import{jsx as e,jsxs as n}from"react/jsx-runtime";import{Headphones as t,Info as i}from"lucide-react";import{ENGLISH_CERT_COLORS as r,ENGLISH_CERT_CONTENT_WIDTH_CLASS as l,ENGLISH_CERT_PRIMARY_GRADIENT as s}from"./english-certification-theme";import{isHtmlContent as a}from"./grouped-question-utils";export function EnglishCertificationInstructionBanner({instruction:o,isListeningPart:c=!1,isFullWidth:d=!1}){if(!o)return null;const p=c?t:i,m=a(o),h=m?o:(f=o).replace(/^\s*directions\s*[:.\\-–]?\s*/i,"").trim()||f;var f;return n("div",{className:d?"w-full":`${l} w-full self-center`,children:[n("div",{className:"overflow-hidden rounded-2xl",style:{boxShadow:"0 4px 6px -1px rgba(11,87,208,0.08), 0 12px_28px rgba(11,87,208,0.10)",border:`1px solid ${r.blue600}26`},children:[n("div",{className:"flex items-center gap-3 px-5 py-3",style:{background:s},children:[e("span",{className:"grid h-8 w-8 shrink-0 place-items-center rounded-lg",style:{background:"rgba(255,255,255,0.18)"},children:e(p,{className:"h-4 w-4 text-white",strokeWidth:2.5})}),e("span",{className:"text-[11px] font-black uppercase tracking-[0.22em] text-white/90",children:"Directions"}),e("div",{className:"ml-auto flex items-center gap-1","aria-hidden":!0,children:[.5,.35,.2].map((n,t)=>e("span",{className:"h-1.5 w-1.5 rounded-full bg-white",style:{opacity:n}},t))})]}),e("div",{className:"px-6 py-5",style:{background:"linear-gradient(160deg, #f8fbff 0%, #eef5ff 100%)"},children:m?e("div",{className:"ec-instruction-html text-[15px] leading-[1.8] text-[#0f1730]",dangerouslySetInnerHTML:{__html:h}}):e("p",{className:"text-[15px] font-semibold leading-[1.8] text-[#0f1730]",children:h})})]}),e("style",{children:`\n /* Base paragraphs */\n .ec-instruction-html p {\n margin-bottom: 0.5rem;\n font-size: 15px;\n font-weight: 600;\n color: #0f1730;\n }\n .ec-instruction-html p:last-child {\n margin-bottom: 0;\n }\n\n /* Bold labels ("Directions:", "Example:") */\n .ec-instruction-html strong {\n font-weight: 700;\n color: ${r.blue700};\n }\n\n /* "Example:" paragraph — inset callout block */\n .ec-instruction-html p:has(> strong:first-child):not(:first-child) {\n margin-top: 1rem;\n padding: 0.7rem 1rem;\n background: rgba(11, 87, 208, 0.06);\n border-left: 3px solid ${r.blue600};\n border-radius: 0 8px 8px 0;\n }\n\n /* Lines that follow inside an inset callout */\n .ec-instruction-html p:has(> strong:first-child) ~ p {\n padding-left: 1rem;\n font-size: 14.5px;\n font-weight: 500;\n color: #1e2d4d;\n }\n `})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Headphones, Info } from 'lucide-react';
4
+ import { ENGLISH_CERT_COLORS, ENGLISH_CERT_CONTENT_WIDTH_CLASS, ENGLISH_CERT_PRIMARY_GRADIENT, } from './english-certification-theme';
5
+ import { isHtmlContent } from './grouped-question-utils';
6
+ /**
7
+ * Drops a leading "Directions:" prefix (legacy plain-text format) so it isn't
8
+ * repeated below the styled "Directions" header. Only applied to plain text —
9
+ * HTML instructions (which may include a worked "Example:" section after the
10
+ * directions paragraph) are rendered as-is, since safely stripping just the
11
+ * leading label from arbitrary rich markup isn't reliable.
12
+ */
13
+ function stripDirectionsPrefix(text) {
14
+ return text.replace(/^\s*directions\s*[:.\\-–]?\s*/i, '').trim() || text;
15
+ }
16
+ /**
17
+ * Premium branded "Directions" callout block.
18
+ *
19
+ * Design:
20
+ * - Gradient header strip with icon + "DIRECTIONS" label — clear visual anchor.
21
+ * - Cream-white body with generous padding for readability.
22
+ * - HTML instructions rendered as rich prose with "Example:" sections styled
23
+ * as an inset callout for TOEIC-style worked examples.
24
+ * - Plain-text instructions get the same header but a simple prose paragraph.
25
+ */
26
+ export function EnglishCertificationInstructionBanner({ instruction, isListeningPart = false, isFullWidth = false, }) {
27
+ if (!instruction)
28
+ return null;
29
+ const Icon = isListeningPart ? Headphones : Info;
30
+ const htmlInstruction = isHtmlContent(instruction);
31
+ const body = htmlInstruction ? instruction : stripDirectionsPrefix(instruction);
32
+ return (_jsxs("div", { className: isFullWidth ? 'w-full' : `${ENGLISH_CERT_CONTENT_WIDTH_CLASS} w-full self-center`, children: [_jsxs("div", { className: "overflow-hidden rounded-2xl", style: {
33
+ boxShadow: '0 4px 6px -1px rgba(11,87,208,0.08), 0 12px_28px rgba(11,87,208,0.10)',
34
+ border: `1px solid ${ENGLISH_CERT_COLORS.blue600}26`,
35
+ }, children: [_jsxs("div", { className: "flex items-center gap-3 px-5 py-3", style: { background: ENGLISH_CERT_PRIMARY_GRADIENT }, children: [_jsx("span", { className: "grid h-8 w-8 shrink-0 place-items-center rounded-lg", style: { background: 'rgba(255,255,255,0.18)' }, children: _jsx(Icon, { className: "h-4 w-4 text-white", strokeWidth: 2.5 }) }), _jsx("span", { className: "text-[11px] font-black uppercase tracking-[0.22em] text-white/90", children: "Directions" }), _jsx("div", { className: "ml-auto flex items-center gap-1", "aria-hidden": true, children: [0.5, 0.35, 0.2].map((op, i) => (_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-white", style: { opacity: op } }, i))) })] }), _jsx("div", { className: "px-6 py-5", style: {
36
+ background: 'linear-gradient(160deg, #f8fbff 0%, #eef5ff 100%)',
37
+ }, children: htmlInstruction ? (
38
+ /* ── Rich HTML (TOEIC-style with Example block) ────── */
39
+ _jsx("div", { className: "ec-instruction-html text-[15px] leading-[1.8] text-[#0f1730]",
40
+ // The instruction HTML comes from the exam CMS and is
41
+ // not user-generated, so dangerouslySetInnerHTML is safe here.
42
+ dangerouslySetInnerHTML: { __html: body } })) : (
43
+ /* ── Plain text ─────────────────────────────────────── */
44
+ _jsx("p", { className: "text-[15px] font-semibold leading-[1.8] text-[#0f1730]", children: body })) })] }), _jsx("style", { children: `
45
+ /* Base paragraphs */
46
+ .ec-instruction-html p {
47
+ margin-bottom: 0.5rem;
48
+ font-size: 15px;
49
+ font-weight: 600;
50
+ color: #0f1730;
51
+ }
52
+ .ec-instruction-html p:last-child {
53
+ margin-bottom: 0;
54
+ }
55
+
56
+ /* Bold labels ("Directions:", "Example:") */
57
+ .ec-instruction-html strong {
58
+ font-weight: 700;
59
+ color: ${ENGLISH_CERT_COLORS.blue700};
60
+ }
61
+
62
+ /* "Example:" paragraph — inset callout block */
63
+ .ec-instruction-html p:has(> strong:first-child):not(:first-child) {
64
+ margin-top: 1rem;
65
+ padding: 0.7rem 1rem;
66
+ background: rgba(11, 87, 208, 0.06);
67
+ border-left: 3px solid ${ENGLISH_CERT_COLORS.blue600};
68
+ border-radius: 0 8px 8px 0;
69
+ }
70
+
71
+ /* Lines that follow inside an inset callout */
72
+ .ec-instruction-html p:has(> strong:first-child) ~ p {
73
+ padding-left: 1rem;
74
+ font-size: 14.5px;
75
+ font-weight: 500;
76
+ color: #1e2d4d;
77
+ }
78
+ ` })] }));
79
+ }
@@ -1 +1,71 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useRef as r,useEffect as n,useMemo as a}from"react";import{cn as d}from"../../../shared/lib/utils";function i(e){return null!=e&&""!==e&&("object"!=typeof e||!("optionId"in e)||Boolean(e.optionId))}export function EnglishCertificationNavigationPanel({parts:o,questions:c,answers:u,flaggedQuestions:b,currentIndex:m,onQuestionClick:f,onPartClick:x}){const h=r(new Map);n(()=>{const e=h.current.get(m);e?.scrollIntoView({block:"center",behavior:"smooth"})},[m]);const{answeredCount:p,markedCount:g,unansweredCount:w}=a(()=>{const e=c.filter(e=>i(u[e.id])).length;return{answeredCount:e,markedCount:c.filter(e=>b.has(e.id)).length,unansweredCount:c.length-e}},[c,u,b]);return t("div",{className:"flex h-full flex-col",children:[t("div",{className:"grid grid-cols-3 gap-3 p-4 pb-2",children:[e(s,{label:"Answered",value:p,variant:"answered"}),e(s,{label:"Unanswered",value:w,variant:"unanswered"}),e(s,{label:"Marked",value:g,variant:"marked"})]}),t("div",{className:"flex-1 overflow-y-auto px-4 pb-4",children:[o.map((r,n)=>{const a=r.startIndex??0,s=r.endIndex??a,l=s-a+1;return t("section",{className:"mb-5",children:[e("button",{type:"button",onClick:()=>x(n),className:d("mb-3 w-full text-left text-[15px] font-black text-[#17213a] transition-colors hover:text-[#0b57d0]",m>=a&&m<=s&&"text-[#0b57d0]"),children:r.name||`Part ${n+1}`}),e("div",{className:"grid grid-cols-5 gap-2.5",children:Array.from({length:l},(r,n)=>{const s=a+n,l=c[s];if(!l)return null;const o=i(u[l.id]),x=b.has(l.id),p=s===m,g=s+1;return t("button",{ref:e=>{e?h.current.set(s,e):h.current.delete(s)},type:"button",onClick:()=>f(s),className:d("relative flex h-[50px] items-center justify-center rounded-md border text-[15px] font-bold transition-all","focus-visible:ring-2 focus-visible:ring-[#1167e8] focus-visible:ring-offset-1",o?"border-[#b9edc8] bg-[#ddf8e7] text-[#111827]":"border-[#d9e4f2] bg-white text-[#111827]",p&&"border-[#1167e8] text-[#0b57d0] shadow-[inset_0_0_0_1px_#1167e8]"),"aria-label":`Question ${g}${o?", answered":", unanswered"}${x?", marked":""}`,"aria-current":p?"true":void 0,children:[g,x&&e("span",{className:"absolute -top-px right-2 h-[18px] w-[9px] bg-[#ff8a00]",style:{clipPath:"polygon(0 0, 100% 0, 100% 100%, 50% 70%, 0 100%)"},"aria-hidden":"true"})]},l.id)})})]},n)}),t("div",{className:"mt-2 rounded-md border border-[#d9e4f2] bg-white/70 p-4",children:[e(l,{variant:"current",children:"Current Question"}),e(l,{variant:"answered",children:"Answered"}),e(l,{variant:"none",children:"Not Answered"}),e(l,{variant:"marked",children:"Marked for Review"})]})]})]})}function s({label:r,value:n,variant:a}){return e("div",{className:"grid min-h-[78px] place-items-center rounded-md border border-[#d9e4f2] bg-white text-center",children:t("div",{children:[e("span",{className:d("block text-[11px] font-black uppercase tracking-wide","answered"===a&&"text-[#0a3d9a]","unanswered"===a&&"text-[#17213a]","marked"===a&&"text-[#f97316]"),children:r}),e("strong",{className:d("mt-2 block text-[28px] leading-none","answered"===a&&"text-[#0b57d0]","unanswered"===a&&"text-[#17213a]","marked"===a&&"text-[#f97316]"),children:n})]})})}function l({variant:r,children:n}){return t("div",{className:"my-3 flex items-center gap-3.5 text-sm font-semibold text-[#334155]",children:[e("span","marked"===r?{className:"h-[18px] w-2.5 bg-[#ff8a00]",style:{clipPath:"polygon(0 0, 100% 0, 100% 100%, 50% 70%, 0 100%)"}}:{className:d("h-[22px] w-[22px] rounded border border-[#d9e4f2] bg-white","current"===r&&"border-[#1167e8] shadow-[inset_0_0_0_1px_#1167e8]","answered"===r&&"border-[#b9edc8] bg-[#ddf8e7]")}),n]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useRef, useEffect, useMemo } from 'react';
4
+ import { cn } from '../../../shared/lib/utils';
5
+ /** Returns true when an answer entry counts as "answered". */
6
+ function isAnswered(answer) {
7
+ if (answer === undefined || answer === null || answer === '')
8
+ return false;
9
+ if (typeof answer === 'object' && 'optionId' in answer) {
10
+ return Boolean(answer.optionId);
11
+ }
12
+ return true;
13
+ }
14
+ /**
15
+ * Branded Ocean Edu "Answer Sheet" navigation panel.
16
+ *
17
+ * Shows an Answered / Unanswered / Marked summary, a per-part question grid
18
+ * (answered, current, marked states) and a legend, bound to real store data.
19
+ */
20
+ export function EnglishCertificationNavigationPanel({ parts, questions, answers, flaggedQuestions, currentIndex, onQuestionClick, onPartClick, }) {
21
+ const buttonRefs = useRef(new Map());
22
+ // Scroll current button into view when currentIndex changes.
23
+ useEffect(() => {
24
+ const btn = buttonRefs.current.get(currentIndex);
25
+ btn?.scrollIntoView({ block: 'center', behavior: 'smooth' });
26
+ }, [currentIndex]);
27
+ const { answeredCount, markedCount, unansweredCount } = useMemo(() => {
28
+ const answered = questions.filter((q) => isAnswered(answers[q.id])).length;
29
+ const marked = questions.filter((q) => flaggedQuestions.has(q.id)).length;
30
+ return {
31
+ answeredCount: answered,
32
+ markedCount: marked,
33
+ unansweredCount: questions.length - answered,
34
+ };
35
+ }, [questions, answers, flaggedQuestions]);
36
+ return (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { className: "grid grid-cols-3 gap-3 p-4 pb-2", children: [_jsx(SummaryBox, { label: "Answered", value: answeredCount, variant: "answered" }), _jsx(SummaryBox, { label: "Unanswered", value: unansweredCount, variant: "unanswered" }), _jsx(SummaryBox, { label: "Marked", value: markedCount, variant: "marked" })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 pb-4", children: [parts.map((part, partIdx) => {
37
+ const startGlobal = part.startIndex ?? 0;
38
+ const endGlobal = part.endIndex ?? startGlobal;
39
+ const totalInPart = endGlobal - startGlobal + 1;
40
+ return (_jsxs("section", { className: "mb-5", children: [_jsx("button", { type: "button", onClick: () => onPartClick(partIdx), className: cn('mb-3 w-full text-left text-[15px] font-black text-[#17213a] transition-colors hover:text-[#0b57d0]', currentIndex >= startGlobal &&
41
+ currentIndex <= endGlobal &&
42
+ 'text-[#0b57d0]'), children: part.name || `Part ${partIdx + 1}` }), _jsx("div", { className: "grid grid-cols-5 gap-2.5", children: Array.from({ length: totalInPart }, (_, i) => {
43
+ const globalIdx = startGlobal + i;
44
+ const question = questions[globalIdx];
45
+ if (!question)
46
+ return null;
47
+ const answered = isAnswered(answers[question.id]);
48
+ const flagged = flaggedQuestions.has(question.id);
49
+ const current = globalIdx === currentIndex;
50
+ const displayNum = globalIdx + 1;
51
+ return (_jsxs("button", { ref: (el) => {
52
+ if (el)
53
+ buttonRefs.current.set(globalIdx, el);
54
+ else
55
+ buttonRefs.current.delete(globalIdx);
56
+ }, type: "button", onClick: () => onQuestionClick(globalIdx), className: cn('relative flex h-[50px] items-center justify-center rounded-md border text-[15px] font-bold transition-all', 'focus-visible:ring-2 focus-visible:ring-[#1167e8] focus-visible:ring-offset-1', answered
57
+ ? 'border-[#b9edc8] bg-[#ddf8e7] text-[#111827]'
58
+ : 'border-[#d9e4f2] bg-white text-[#111827]', current &&
59
+ 'border-[#1167e8] text-[#0b57d0] shadow-[inset_0_0_0_1px_#1167e8]'), "aria-label": `Question ${displayNum}${answered ? ', answered' : ', unanswered'}${flagged ? ', marked' : ''}`, "aria-current": current ? 'true' : undefined, children: [displayNum, flagged && (_jsx("span", { className: "absolute -top-px right-2 h-[18px] w-[9px] bg-[#ff8a00]", style: {
60
+ clipPath: 'polygon(0 0, 100% 0, 100% 100%, 50% 70%, 0 100%)',
61
+ }, "aria-hidden": "true" }))] }, question.id));
62
+ }) })] }, partIdx));
63
+ }), _jsxs("div", { className: "mt-2 rounded-md border border-[#d9e4f2] bg-white/70 p-4", children: [_jsx(LegendRow, { variant: "current", children: "Current Question" }), _jsx(LegendRow, { variant: "answered", children: "Answered" }), _jsx(LegendRow, { variant: "none", children: "Not Answered" }), _jsx(LegendRow, { variant: "marked", children: "Marked for Review" })] })] })] }));
64
+ }
65
+ function SummaryBox({ label, value, variant, }) {
66
+ return (_jsx("div", { className: "grid min-h-[78px] place-items-center rounded-md border border-[#d9e4f2] bg-white text-center", children: _jsxs("div", { children: [_jsx("span", { className: cn('block text-[11px] font-black uppercase tracking-wide', variant === 'answered' && 'text-[#0a3d9a]', variant === 'unanswered' && 'text-[#17213a]', variant === 'marked' && 'text-[#f97316]'), children: label }), _jsx("strong", { className: cn('mt-2 block text-[28px] leading-none', variant === 'answered' && 'text-[#0b57d0]', variant === 'unanswered' && 'text-[#17213a]', variant === 'marked' && 'text-[#f97316]'), children: value })] }) }));
67
+ }
68
+ function LegendRow({ variant, children, }) {
69
+ return (_jsxs("div", { className: "my-3 flex items-center gap-3.5 text-sm font-semibold text-[#334155]", children: [variant === 'marked' ? (_jsx("span", { className: "h-[18px] w-2.5 bg-[#ff8a00]", style: { clipPath: 'polygon(0 0, 100% 0, 100% 100%, 50% 70%, 0 100%)' } })) : (_jsx("span", { className: cn('h-[22px] w-[22px] rounded border border-[#d9e4f2] bg-white', variant === 'current' &&
70
+ 'border-[#1167e8] shadow-[inset_0_0_0_1px_#1167e8]', variant === 'answered' && 'border-[#b9edc8] bg-[#ddf8e7]') })), children] }));
71
+ }
@@ -1 +1,79 @@
1
- "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{useRef as t}from"react";import{cn as d}from"../../../shared/lib/utils";import{Check as a,CheckCircle2 as o,X as i,XCircle as l}from"lucide-react";export function EnglishCertificationOptionSelector({options:n,selectedOptionId:s,onSelect:c,disabled:b=!1,isReviewMode:h=!1,correctOptionId:x,variant:p="cards"}){const f=t([]),g=n.findIndex(e=>e.id===s),m=g>=0?g:0,w=(e,r)=>{if(b)return;let t;switch(e.key){case"ArrowRight":case"ArrowDown":t=(r+1)%n.length;break;case"ArrowLeft":case"ArrowUp":t=(r-1+n.length)%n.length;break;default:return}e.preventDefault();const d=n[t];c(d.id),f.current[t]?.focus()};return e("div","compact"===p?{className:"flex flex-col gap-1",role:"radiogroup",children:n.map((t,a)=>{const i=t.id===s,n=h&&t.id===x,p=h&&i&&t.id!==x;return r("button",{ref:e=>{f.current[a]=e},type:"button",role:"radio","aria-checked":i,disabled:b,tabIndex:a===m?0:-1,onClick:()=>!b&&c(t.id),onKeyDown:e=>w(e,a),className:d("flex items-center gap-2.5 rounded-md border border-transparent px-2.5 py-1.5 text-left text-base transition-all","hover:border-[#c8def7] hover:bg-[#f4f9ff]",i&&!h&&"border-[#99c5f8] bg-[#eff7ff] shadow-[inset_0_0_0_1px_rgba(17,103,232,0.08)]",n&&"border-[#b9edc8] bg-[#ddf8e7]",p&&"border-red-300 bg-red-50",b&&"cursor-not-allowed opacity-60"),children:[e("span",{className:d("grid h-4 w-4 shrink-0 place-items-center rounded-full border-2 border-[#94a3b8]",i&&!h&&"border-[#0b57d0]",n&&"border-[#22c55e]",p&&"border-red-500"),children:i&&e("span",{className:d("h-2 w-2 rounded-full bg-[#0b57d0]",n&&"bg-[#22c55e]",p&&"bg-red-500")})}),e("span",{className:"font-bold text-[#23304c]",children:t.label}),e("span",{className:"flex-1 leading-snug text-[#1e293b]",children:t.text}),h&&n&&e(o,{className:"h-4 w-4 shrink-0 text-[#22c55e]","aria-label":"Correct"}),h&&p&&e(l,{className:"h-4 w-4 shrink-0 text-red-500","aria-label":"Incorrect"})]},t.id)})}:"list"===p?{className:"flex flex-col gap-3",role:"radiogroup",children:n.map((t,o)=>{const l=t.id===s,n=h&&t.id===x,p=h&&l&&t.id!==x,g=l&&!h;return r("button",{ref:e=>{f.current[o]=e},type:"button",role:"radio","aria-checked":l,disabled:b,tabIndex:o===m?0:-1,onClick:()=>!b&&c(t.id),onKeyDown:e=>w(e,o),className:d("flex items-center gap-4 rounded-2xl border-2 p-4 text-left transition-all","border-[#e5eefb] bg-white shadow-[0_5px_16px_rgba(13,71,161,0.08)]","hover:border-[#bfd8f8] hover:bg-[#fbfdff] hover:shadow-[0_10px_22px_rgba(13,71,161,0.14)]",g&&"border-transparent text-white shadow-[0_16px_30px_rgba(11,87,208,0.25)]",n&&"border-[#22c55e] bg-[#ddf8e7]",p&&"border-red-400 bg-red-50",b&&"cursor-not-allowed opacity-60"),style:g?{background:"linear-gradient(180deg, #1f7cff, #0b57d0)"}:void 0,children:[e("span",{className:d("grid h-11 w-11 shrink-0 place-items-center rounded-full border-2 text-lg font-black transition-colors","border-[#bfd8f8] bg-[#eef5ff] text-[#0b57d0]",g&&"border-white/70 bg-white/15 text-white",n&&"border-[#22c55e] bg-white text-[#22c55e]",p&&"border-red-500 bg-white text-red-500"),children:t.label}),e("span",{className:d("flex-1 text-[15px] font-semibold leading-relaxed text-[#0f1730]",g&&"text-white",n&&"text-[#15803d]",p&&"text-red-700"),children:t.text}),(g||n)&&e("span",{className:"grid h-7 w-7 shrink-0 place-items-center rounded-full bg-white text-[#0b57d0] shadow-[0_2px_6px_rgba(11,87,208,0.25)]",children:e(a,{className:"h-4 w-4",strokeWidth:3})}),p&&e("span",{className:"grid h-7 w-7 shrink-0 place-items-center rounded-full bg-white text-red-500 shadow-[0_2px_6px_rgba(239,68,68,0.25)]",children:e(i,{className:"h-4 w-4",strokeWidth:3})})]},t.id)})}:{className:"grid gap-4 sm:gap-5",style:{gridTemplateColumns:`repeat(${Math.min(n.length,4)}, minmax(0, 1fr))`},role:"radiogroup",children:n.map((t,o)=>{const l=t.id===s,n=h&&t.id===x,p=h&&l&&t.id!==x,g=l&&!h;return r("button",{ref:e=>{f.current[o]=e},type:"button",role:"radio","aria-checked":l,disabled:b,tabIndex:o===m?0:-1,onClick:()=>!b&&c(t.id),onKeyDown:e=>w(e,o),className:d("relative flex min-h-[104px] flex-col items-center justify-center rounded-2xl border-2 p-3 transition-all","border-[#e5eefb] bg-white shadow-[0_5px_16px_rgba(13,71,161,0.08)]","hover:-translate-y-0.5 hover:border-[#bfd8f8] hover:shadow-[0_10px_22px_rgba(13,71,161,0.14)]",g&&"border-transparent text-white shadow-[0_16px_30px_rgba(11,87,208,0.25)] hover:-translate-y-0.5",n&&"border-[#22c55e] bg-[#ddf8e7]",p&&"border-red-400 bg-red-50",b&&"cursor-not-allowed opacity-60 hover:translate-y-0"),style:g?{background:"linear-gradient(180deg, #1f7cff, #0b57d0)"}:void 0,children:[e("span",{className:d("grid h-[52px] w-[52px] place-items-center rounded-full border-2 text-2xl font-black transition-colors","border-[#bfd8f8] bg-[#eef5ff] text-[#0b57d0]",g&&"border-white/70 bg-white/15 text-white",n&&"border-[#22c55e] bg-white text-[#22c55e]",p&&"border-red-500 bg-white text-red-500"),children:t.label}),t.text&&e("span",{className:d("mt-2 text-center text-sm font-semibold leading-snug text-[#0f1730]",g&&"text-white",n&&"text-[#15803d]",p&&"text-red-700"),children:t.text}),(g||n)&&e("span",{className:"absolute right-2.5 top-2.5 grid h-6 w-6 place-items-center rounded-full bg-white text-[#0b57d0] shadow-[0_2px_6px_rgba(11,87,208,0.25)]",children:e(a,{className:"h-3.5 w-3.5",strokeWidth:3})}),p&&e("span",{className:"absolute right-2.5 top-2.5 grid h-6 w-6 place-items-center rounded-full bg-white text-red-500 shadow-[0_2px_6px_rgba(239,68,68,0.25)]",children:e(i,{className:"h-3.5 w-3.5",strokeWidth:3})})]},t.id)})})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useRef } from 'react';
4
+ import { cn } from '../../../shared/lib/utils';
5
+ import { Check, CheckCircle2, X, XCircle } from 'lucide-react';
6
+ /**
7
+ * Shared A–D answer selector for all English Certification question types.
8
+ *
9
+ * Renders either large branded cards or compact rows, and shows
10
+ * correct/incorrect indicators in review mode.
11
+ */
12
+ export function EnglishCertificationOptionSelector({ options, selectedOptionId, onSelect, disabled = false, isReviewMode = false, correctOptionId, variant = 'cards', }) {
13
+ const buttonRefs = useRef([]);
14
+ // Roving tabindex: only the selected option (or the first, if none selected)
15
+ // is in the tab order; arrow keys move focus + selection between options,
16
+ // matching native <input type="radio"> group behavior.
17
+ const selectedIndex = options.findIndex((option) => option.id === selectedOptionId);
18
+ const tabbableIndex = selectedIndex >= 0 ? selectedIndex : 0;
19
+ const handleKeyDown = (event, index) => {
20
+ if (disabled)
21
+ return;
22
+ let nextIndex;
23
+ switch (event.key) {
24
+ case 'ArrowRight':
25
+ case 'ArrowDown':
26
+ nextIndex = (index + 1) % options.length;
27
+ break;
28
+ case 'ArrowLeft':
29
+ case 'ArrowUp':
30
+ nextIndex = (index - 1 + options.length) % options.length;
31
+ break;
32
+ default:
33
+ return;
34
+ }
35
+ event.preventDefault();
36
+ const nextOption = options[nextIndex];
37
+ onSelect(nextOption.id);
38
+ buttonRefs.current[nextIndex]?.focus();
39
+ };
40
+ if (variant === 'compact') {
41
+ return (_jsx("div", { className: "flex flex-col gap-1", role: "radiogroup", children: options.map((option, index) => {
42
+ const isSelected = option.id === selectedOptionId;
43
+ const isCorrect = isReviewMode && option.id === correctOptionId;
44
+ const isWrong = isReviewMode && isSelected && option.id !== correctOptionId;
45
+ return (_jsxs("button", { ref: (el) => {
46
+ buttonRefs.current[index] = el;
47
+ }, type: "button", role: "radio", "aria-checked": isSelected, disabled: disabled, tabIndex: index === tabbableIndex ? 0 : -1, onClick: () => !disabled && onSelect(option.id), onKeyDown: (event) => handleKeyDown(event, index), className: cn('flex items-center gap-2.5 rounded-md border border-transparent px-2.5 py-1.5 text-left text-base transition-all', 'hover:border-[#c8def7] hover:bg-[#f4f9ff]', isSelected &&
48
+ !isReviewMode &&
49
+ 'border-[#99c5f8] bg-[#eff7ff] shadow-[inset_0_0_0_1px_rgba(17,103,232,0.08)]', isCorrect && 'border-[#b9edc8] bg-[#ddf8e7]', isWrong && 'border-red-300 bg-red-50', disabled && 'cursor-not-allowed opacity-60'), children: [_jsx("span", { className: cn('grid h-4 w-4 shrink-0 place-items-center rounded-full border-2 border-[#94a3b8]', isSelected && !isReviewMode && 'border-[#0b57d0]', isCorrect && 'border-[#22c55e]', isWrong && 'border-red-500'), children: isSelected && (_jsx("span", { className: cn('h-2 w-2 rounded-full bg-[#0b57d0]', isCorrect && 'bg-[#22c55e]', isWrong && 'bg-red-500') })) }), _jsx("span", { className: "font-bold text-[#23304c]", children: option.label }), _jsx("span", { className: "flex-1 leading-snug text-[#1e293b]", children: option.text }), isReviewMode && isCorrect && (_jsx(CheckCircle2, { className: "h-4 w-4 shrink-0 text-[#22c55e]", "aria-label": "Correct" })), isReviewMode && isWrong && (_jsx(XCircle, { className: "h-4 w-4 shrink-0 text-red-500", "aria-label": "Incorrect" }))] }, option.id));
50
+ }) }));
51
+ }
52
+ if (variant === 'list') {
53
+ return (_jsx("div", { className: "flex flex-col gap-3", role: "radiogroup", children: options.map((option, index) => {
54
+ const isSelected = option.id === selectedOptionId;
55
+ const isCorrect = isReviewMode && option.id === correctOptionId;
56
+ const isWrong = isReviewMode && isSelected && option.id !== correctOptionId;
57
+ const isActive = isSelected && !isReviewMode;
58
+ return (_jsxs("button", { ref: (el) => {
59
+ buttonRefs.current[index] = el;
60
+ }, type: "button", role: "radio", "aria-checked": isSelected, disabled: disabled, tabIndex: index === tabbableIndex ? 0 : -1, onClick: () => !disabled && onSelect(option.id), onKeyDown: (event) => handleKeyDown(event, index), className: cn('flex items-center gap-4 rounded-2xl border-2 p-4 text-left transition-all', 'border-[#e5eefb] bg-white shadow-[0_5px_16px_rgba(13,71,161,0.08)]', 'hover:border-[#bfd8f8] hover:bg-[#fbfdff] hover:shadow-[0_10px_22px_rgba(13,71,161,0.14)]', isActive &&
61
+ 'border-transparent text-white shadow-[0_16px_30px_rgba(11,87,208,0.25)]', isCorrect && 'border-[#22c55e] bg-[#ddf8e7]', isWrong && 'border-red-400 bg-red-50', disabled && 'cursor-not-allowed opacity-60'), style: isActive
62
+ ? { background: 'linear-gradient(180deg, #1f7cff, #0b57d0)' }
63
+ : undefined, children: [_jsx("span", { className: cn('grid h-11 w-11 shrink-0 place-items-center rounded-full border-2 text-lg font-black transition-colors', 'border-[#bfd8f8] bg-[#eef5ff] text-[#0b57d0]', isActive && 'border-white/70 bg-white/15 text-white', isCorrect && 'border-[#22c55e] bg-white text-[#22c55e]', isWrong && 'border-red-500 bg-white text-red-500'), children: option.label }), _jsx("span", { className: cn('flex-1 text-[15px] font-semibold leading-relaxed text-[#0f1730]', isActive && 'text-white', isCorrect && 'text-[#15803d]', isWrong && 'text-red-700'), children: option.text }), (isActive || isCorrect) && (_jsx("span", { className: "grid h-7 w-7 shrink-0 place-items-center rounded-full bg-white text-[#0b57d0] shadow-[0_2px_6px_rgba(11,87,208,0.25)]", children: _jsx(Check, { className: "h-4 w-4", strokeWidth: 3 }) })), isWrong && (_jsx("span", { className: "grid h-7 w-7 shrink-0 place-items-center rounded-full bg-white text-red-500 shadow-[0_2px_6px_rgba(239,68,68,0.25)]", children: _jsx(X, { className: "h-4 w-4", strokeWidth: 3 }) }))] }, option.id));
64
+ }) }));
65
+ }
66
+ // 'cards' variant — large A–D answer cards.
67
+ return (_jsx("div", { className: "grid gap-4 sm:gap-5", style: { gridTemplateColumns: `repeat(${Math.min(options.length, 4)}, minmax(0, 1fr))` }, role: "radiogroup", children: options.map((option, index) => {
68
+ const isSelected = option.id === selectedOptionId;
69
+ const isCorrect = isReviewMode && option.id === correctOptionId;
70
+ const isWrong = isReviewMode && isSelected && option.id !== correctOptionId;
71
+ const isActive = isSelected && !isReviewMode;
72
+ return (_jsxs("button", { ref: (el) => {
73
+ buttonRefs.current[index] = el;
74
+ }, type: "button", role: "radio", "aria-checked": isSelected, disabled: disabled, tabIndex: index === tabbableIndex ? 0 : -1, onClick: () => !disabled && onSelect(option.id), onKeyDown: (event) => handleKeyDown(event, index), className: cn('relative flex min-h-[104px] flex-col items-center justify-center rounded-2xl border-2 p-3 transition-all', 'border-[#e5eefb] bg-white shadow-[0_5px_16px_rgba(13,71,161,0.08)]', 'hover:-translate-y-0.5 hover:border-[#bfd8f8] hover:shadow-[0_10px_22px_rgba(13,71,161,0.14)]', isActive &&
75
+ 'border-transparent text-white shadow-[0_16px_30px_rgba(11,87,208,0.25)] hover:-translate-y-0.5', isCorrect && 'border-[#22c55e] bg-[#ddf8e7]', isWrong && 'border-red-400 bg-red-50', disabled && 'cursor-not-allowed opacity-60 hover:translate-y-0'), style: isActive
76
+ ? { background: 'linear-gradient(180deg, #1f7cff, #0b57d0)' }
77
+ : undefined, children: [_jsx("span", { className: cn('grid h-[52px] w-[52px] place-items-center rounded-full border-2 text-2xl font-black transition-colors', 'border-[#bfd8f8] bg-[#eef5ff] text-[#0b57d0]', isActive && 'border-white/70 bg-white/15 text-white', isCorrect && 'border-[#22c55e] bg-white text-[#22c55e]', isWrong && 'border-red-500 bg-white text-red-500'), children: option.label }), option.text && (_jsx("span", { className: cn('mt-2 text-center text-sm font-semibold leading-snug text-[#0f1730]', isActive && 'text-white', isCorrect && 'text-[#15803d]', isWrong && 'text-red-700'), children: option.text })), (isActive || isCorrect) && (_jsx("span", { className: "absolute right-2.5 top-2.5 grid h-6 w-6 place-items-center rounded-full bg-white text-[#0b57d0] shadow-[0_2px_6px_rgba(11,87,208,0.25)]", children: _jsx(Check, { className: "h-3.5 w-3.5", strokeWidth: 3 }) })), isWrong && (_jsx("span", { className: "absolute right-2.5 top-2.5 grid h-6 w-6 place-items-center rounded-full bg-white text-red-500 shadow-[0_2px_6px_rgba(239,68,68,0.25)]", children: _jsx(X, { className: "h-3.5 w-3.5", strokeWidth: 3 }) }))] }, option.id));
78
+ }) }));
79
+ }
@@ -1 +1,47 @@
1
- "use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import{BookOpen as l,Headphones as t,Pencil as r,Mic as n}from"lucide-react";import{ENGLISH_CERT_COLORS as i,ENGLISH_CERT_CONTENT_WIDTH_CLASS as c,ENGLISH_CERT_PRIMARY_GRADIENT as s}from"./english-certification-theme";export function EnglishCertificationPartHeader({partName:o,partNo:d,skill:p,subtitle:h,isFullWidth:u=!1}){if(!o&&void 0===d)return null;const{Icon:b,label:x}=function(e){switch((e??"").toUpperCase()){case"LISTENING":return{Icon:t,label:"Listening",color:"#0b57d0"};case"READING":return{Icon:l,label:"Reading",color:"#0f6cbf"};case"WRITING":return{Icon:r,label:"Writing",color:"#1255a0"};case"SPEAKING":return{Icon:n,label:"Speaking",color:"#0d4fa3"};default:return{Icon:l,label:null,color:i.blue700}}}(p),g=o?o.replace(/^part\s*/i,"").toUpperCase():String(d??""),m=d??o?.match(/\d+/)?.[0]??"";return e("div",{className:u?"w-full":`${c} w-full self-center`,children:a("div",{className:"relative overflow-hidden rounded-2xl",style:{background:s,boxShadow:"0 8px 32px rgba(11,87,208,0.28), 0 2px 8px rgba(11,87,208,0.16)"},children:[e("span",{"aria-hidden":!0,className:"pointer-events-none absolute -right-4 -top-3 select-none font-black leading-none text-white/10",style:{fontSize:"clamp(80px, 12vw, 110px)"},children:m}),e("div",{"aria-hidden":!0,className:"pointer-events-none absolute -left-12 -top-12 h-48 w-48 rounded-full",style:{background:"radial-gradient(circle, rgba(255,255,255,0.12) 0%, transparent 70%)"}}),a("div",{className:"relative flex items-center gap-5 px-7 py-5",children:[e("div",{className:"grid h-14 w-14 shrink-0 place-items-center rounded-2xl shadow-[0_4px_16px_rgba(0,0,0,0.20)]",style:{background:"rgba(255,255,255,0.18)",backdropFilter:"blur(8px)"},children:e(b,{className:"h-6 w-6 text-white",strokeWidth:2})}),a("div",{className:"min-w-0 flex-1",children:[x&&e("span",{className:"mb-1.5 inline-flex items-center gap-1.5 rounded-full px-3 py-0.5 text-[10px] font-black uppercase tracking-[0.2em]",style:{background:"rgba(255,255,255,0.20)",color:"rgba(255,255,255,0.90)"},children:x}),a("p",{className:"text-2xl font-black leading-none tracking-tight text-white drop-shadow-sm",children:["Part"," ",e("span",{className:"rounded-lg px-2.5 py-0.5 text-[22px] font-black",style:{background:"rgba(255,255,255,0.22)"},children:g})]}),h&&e("p",{className:"mt-1 text-[13px] font-medium text-white/75",children:h})]})]}),e("svg",{"aria-hidden":!0,className:"block w-full",height:"18",viewBox:"0 0 940 18",preserveAspectRatio:"none",fill:"none",children:e("path",{d:"M0 8C160 0 300 16 470 8C640 0 780 16 940 8V18H0V8Z",fill:"rgba(255,255,255,0.10)"})})]})})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { BookOpen, Headphones, Pencil, Mic } from 'lucide-react';
4
+ import { ENGLISH_CERT_COLORS, ENGLISH_CERT_CONTENT_WIDTH_CLASS, ENGLISH_CERT_PRIMARY_GRADIENT, } from './english-certification-theme';
5
+ function skillMeta(skill) {
6
+ switch ((skill ?? '').toUpperCase()) {
7
+ case 'LISTENING':
8
+ return { Icon: Headphones, label: 'Listening', color: '#0b57d0' };
9
+ case 'READING':
10
+ return { Icon: BookOpen, label: 'Reading', color: '#0f6cbf' };
11
+ case 'WRITING':
12
+ return { Icon: Pencil, label: 'Writing', color: '#1255a0' };
13
+ case 'SPEAKING':
14
+ return { Icon: Mic, label: 'Speaking', color: '#0d4fa3' };
15
+ default:
16
+ return { Icon: BookOpen, label: null, color: ENGLISH_CERT_COLORS.blue700 };
17
+ }
18
+ }
19
+ /**
20
+ * Premium standalone Part title header for English Certification exams.
21
+ *
22
+ * Design decisions:
23
+ * - Full-bleed gradient card matching the brand palette (never plain text).
24
+ * - Large ghost number in the background gives spatial identity.
25
+ * - Skill badge + icon communicate exam section at a glance.
26
+ * - Decorative wave SVG and a thin accent bar at the bottom tie it
27
+ * to the footer / audio-player language.
28
+ */
29
+ export function EnglishCertificationPartHeader({ partName, partNo, skill, subtitle, isFullWidth = false, }) {
30
+ if (!partName && partNo === undefined)
31
+ return null;
32
+ const { Icon, label } = skillMeta(skill);
33
+ // Normalise display: strip leading "PART " so we can control formatting.
34
+ const displayName = partName
35
+ ? partName.replace(/^part\s*/i, '').toUpperCase()
36
+ : String(partNo ?? '');
37
+ const bigNumber = partNo ?? (partName?.match(/\d+/)?.[0] ?? '');
38
+ return (_jsx("div", { className: isFullWidth ? 'w-full' : `${ENGLISH_CERT_CONTENT_WIDTH_CLASS} w-full self-center`, children: _jsxs("div", { className: "relative overflow-hidden rounded-2xl", style: {
39
+ background: ENGLISH_CERT_PRIMARY_GRADIENT,
40
+ boxShadow: '0 8px 32px rgba(11,87,208,0.28), 0 2px 8px rgba(11,87,208,0.16)',
41
+ }, children: [_jsx("span", { "aria-hidden": true, className: "pointer-events-none absolute -right-4 -top-3 select-none font-black leading-none text-white/10", style: { fontSize: 'clamp(80px, 12vw, 110px)' }, children: bigNumber }), _jsx("div", { "aria-hidden": true, className: "pointer-events-none absolute -left-12 -top-12 h-48 w-48 rounded-full", style: {
42
+ background: 'radial-gradient(circle, rgba(255,255,255,0.12) 0%, transparent 70%)',
43
+ } }), _jsxs("div", { className: "relative flex items-center gap-5 px-7 py-5", children: [_jsx("div", { className: "grid h-14 w-14 shrink-0 place-items-center rounded-2xl shadow-[0_4px_16px_rgba(0,0,0,0.20)]", style: { background: 'rgba(255,255,255,0.18)', backdropFilter: 'blur(8px)' }, children: _jsx(Icon, { className: "h-6 w-6 text-white", strokeWidth: 2 }) }), _jsxs("div", { className: "min-w-0 flex-1", children: [label && (_jsx("span", { className: "mb-1.5 inline-flex items-center gap-1.5 rounded-full px-3 py-0.5 text-[10px] font-black uppercase tracking-[0.2em]", style: {
44
+ background: 'rgba(255,255,255,0.20)',
45
+ color: 'rgba(255,255,255,0.90)',
46
+ }, children: label })), _jsxs("p", { className: "text-2xl font-black leading-none tracking-tight text-white drop-shadow-sm", children: ["Part", ' ', _jsx("span", { className: "rounded-lg px-2.5 py-0.5 text-[22px] font-black", style: { background: 'rgba(255,255,255,0.22)' }, children: displayName })] }), subtitle && (_jsx("p", { className: "mt-1 text-[13px] font-medium text-white/75", children: subtitle }))] })] }), _jsx("svg", { "aria-hidden": true, className: "block w-full", height: "18", viewBox: "0 0 940 18", preserveAspectRatio: "none", fill: "none", children: _jsx("path", { d: "M0 8C160 0 300 16 470 8C640 0 780 16 940 8V18H0V8Z", fill: "rgba(255,255,255,0.10)" }) })] }) }));
47
+ }