@tinyweb_dev/oe-exam-sdk 0.1.28 → 0.1.30

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,192 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useRef as r,useEffect as n,useMemo as s,useCallback as a,memo as o}from"react";import{ResolvedImage as l}from"../../../components/common/ResolvedImage";import{cn as i}from"../../../shared/lib/utils";import{transformWordFillStructuredForm as d}from"../../../components/exams/take/exam-taking.utils";import{englishCertQuestionDomId as c}from"./english-cert-scroll";function b(e){const t=Number(e);return Number.isFinite(t)?String(t+1):e}function u(e,t){const r=e.trim();return t?r:r.toLowerCase()}function m(e,t){const r=Object.keys(e).map(e=>Number(e)).filter(e=>Number.isFinite(e)).sort((e,t)=>e-t);if(0===r.length)return`Question ${t}`;const n=r[0]+1,s=r[r.length-1]+1;return n===s?`Question ${n}`:`Questions ${n}–${s}`}const p={},f=o(function({question:a,initialAnswers:o,onBlankChange:d,isReviewMode:c}){const m=r(null),p=r(d);p.current=d;const f=r(!1),x=r(o),g=s(()=>a.content.replace(/\{([\w\d]+)\}/g,(e,t)=>`\n <span class="ec-blank-wrapper" data-blank-wrapper="${t}">\n <span class="ec-blank-number" data-blank-number="${t}">${b(t)}</span>\n <input type="text" data-blank-key="${t}" class="ec-blank-input" />\n </span>\n `),[a.content]);return n(()=>{const e=m.current;if(!e)return;f.current||(e.innerHTML=g,f.current=!0);const t=e.querySelectorAll("input.ec-blank-input"),r=[];return t.forEach(t=>{const n=t.getAttribute("data-blank-key")||"",s=e.querySelector(`[data-blank-wrapper="${n}"]`),o=x.current[n]||"",l=a.answers[n]||"",d=o.trim().length>0,m=d&&u(o,a.caseSensitive)===u(l,a.caseSensitive),f=c&&!m&&l.trim().length>0;t.value=f?`${d?o:"(empty)"} → ${l}`:o,t.disabled=c,t.className=i("ec-blank-input inline-block min-w-[100px] max-w-[200px] rounded-t border-0 border-b-2 border-dashed border-[#1167e8] bg-blue-50/30 px-2 py-0.5 text-base font-semibold leading-tight text-[#0f1730] align-baseline outline-none transition-all hover:bg-blue-50/60 focus:border-solid focus:border-[#0b57d0] focus:bg-blue-50/80",c?m?"border-solid border-[#22c55e] bg-emerald-50 text-[#15803d]":d?"border-solid border-[#ef4444] bg-rose-50 text-red-700":"border-solid border-[#f97316] bg-amber-50 text-[#c2410c]":"");const g=e.querySelector(`[data-blank-number="${n}"]`);g&&(g.textContent=b(n),g.className=i("ec-blank-number mr-1 inline-flex h-5 min-w-5 items-center justify-center rounded-full px-1.5 text-[11px] font-bold leading-none text-white align-baseline shadow-xs",c?m?"bg-[#22c55e]":d?"bg-[#ef4444]":"bg-[#f97316]":"bg-[#0b57d0]")),s&&(s.className=i("ec-blank-wrapper inline-flex items-baseline whitespace-nowrap align-baseline",c?"mx-1":"mx-0.5"));const h=e=>{const t=e.target;p.current(n,t.value)};t.addEventListener("input",h),r.push({el:t,handler:h})}),()=>{r.forEach(({el:e,handler:t})=>{e.removeEventListener("input",t)})}},[g,c]),t("div",{className:"space-y-3",children:[a.title&&e("h3",{className:"text-base font-semibold text-[#0f1730]",children:a.title}),a.imageUrl&&e("div",{className:"flex justify-center",children:e("div",{className:"overflow-hidden rounded-lg border border-[#d9e4f2]",children:e(l,{src:a.imageUrl,alt:a.title||"Question image",className:"max-h-56 w-auto object-contain"})})}),e("div",{ref:m,className:"ec-structured-content w-full overflow-x-auto rounded-xl border border-[#d9e4f2] bg-[#f4f9ff] p-5 text-base leading-relaxed text-[#0f1730] [&_table]:w-full [&_table]:border-collapse [&_td]:border [&_td]:border-[#d9e4f2] [&_td]:px-4 [&_td]:py-3 [&_td]:align-top [&_th]:border [&_th]:border-[#d9e4f2] [&_th]:bg-[#e9f3ff] [&_th]:px-4 [&_th]:py-3 [&_th]:text-left [&_th]:font-semibold"}),c&&Object.keys(a.answers).length>0&&t("div",{className:"rounded-xl border border-[#d9e4f2] bg-white/70 p-3",children:[e("span",{className:"text-[10px] font-semibold uppercase tracking-wider text-[#64748b]",children:"Correct answers"}),e("div",{className:"mt-1 flex flex-wrap gap-1.5",children:Object.entries(a.answers).map(([e,r])=>t("span",{className:"rounded border border-[#b9edc8] bg-[#ddf8e7] px-2 py-0.5 text-xs font-semibold text-[#15803d]",children:[e,": ",r]},e))})]})]})});export function EcWordFillStructuredForm({part:n,answers:o,onAnswerChange:i,isReviewMode:b=!1}){const{questions:u,instruction:x,groupTitle:g,groupContent:h,groupImageUrl:w}=s(()=>d(n.questions),[n.questions]),N=r(o);N.current=o;const v=r(i);v.current=i;const k=a((e,t,r)=>{const n=N.current[e]||{};v.current(e,{...n,[t]:r})},[]),y=s(()=>{const e={};return u.forEach(t=>{e[t.id]=(e,r)=>k(t.id,e,r)}),e},[u,k]);return t("div",{className:"flex flex-col gap-4",children:[x&&e("div",{className:"rounded-xl border border-[#d9e4f2] bg-[#e9f3ff] px-4 py-2.5 text-sm font-semibold uppercase tracking-wide text-[#0b57d0]",children:x}),g&&e("h2",{className:"text-center text-lg font-bold uppercase text-[#0b57d0]",children:g}),w&&e("div",{className:"flex items-center justify-center",children:e(l,{src:w,alt:g||"Group image",className:"max-h-[300px] rounded-lg object-contain shadow-md"})}),e("div",{className:"flex flex-col gap-4",children:u.map(r=>t("div",{id:c(r.id),className:"scroll-mt-24 rounded-2xl border border-[#d9e4f2] bg-white/90 p-4 shadow-[0_14px_35px_rgba(13,71,161,0.10)] backdrop-blur-md",children:[e("div",{className:"mb-2 flex items-center gap-2",children:e("span",{className:"inline-flex items-center rounded-full bg-gradient-to-b from-[#1f7cff] to-[#0b57d0] px-3 py-1 text-xs font-bold text-white shadow-sm",children:m(r.answers,r.questionNumber)})}),e(f,{question:r,initialAnswers:o[r.id]||p,onBlankChange:y[r.id],isReviewMode:b})]},r.id))})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useRef, useEffect, useMemo, useCallback, memo } from 'react';
4
+ import { ResolvedImage } from '../../../components/common/ResolvedImage';
5
+ import { cn } from '../../../shared/lib/utils';
6
+ import { transformWordFillStructuredForm } from '../../../components/exams/take/exam-taking.utils';
7
+ import { englishCertQuestionDomId } from './english-cert-scroll';
8
+ /**
9
+ * Replace `{key}` placeholders in raw HTML with inline `<input>` elements so the
10
+ * full HTML structure (tables, forms, …) is preserved and inputs render inside
11
+ * the correct cells — never breaking onto new lines or squeezing the layout.
12
+ */
13
+ /** Convert a 0-based blank key into the 1-based number shown to the student. */
14
+ function toDisplayNumber(key) {
15
+ const numeric = Number(key);
16
+ return Number.isFinite(numeric) ? String(numeric + 1) : key;
17
+ }
18
+ function buildHtmlWithInputPlaceholders(html) {
19
+ return html.replace(/\{([\w\d]+)\}/g, (_match, key) => {
20
+ return `
21
+ <span class="ec-blank-wrapper" data-blank-wrapper="${key}">
22
+ <span class="ec-blank-number" data-blank-number="${key}">${toDisplayNumber(key)}</span>
23
+ <input type="text" data-blank-key="${key}" class="ec-blank-input" />
24
+ </span>
25
+ `;
26
+ });
27
+ }
28
+ function normalizeStructuredAnswer(value, caseSensitive) {
29
+ const normalized = value
30
+ .trim()
31
+ .replace(/[\p{P}]/gu, '')
32
+ .replace(/\s+/g, ' ')
33
+ .trim();
34
+ return caseSensitive ? normalized : normalized.toLowerCase();
35
+ }
36
+ /**
37
+ * Build the card label from the blank keys (which are the IELTS question numbers).
38
+ * Returns "Questions x–y" for a numeric range, a single "Question n", or falls
39
+ * back to `Question {fallback}` when keys are non-numeric/empty.
40
+ */
41
+ function formatBlankRange(answers, fallback) {
42
+ const numericKeys = Object.keys(answers)
43
+ .map((key) => Number(key))
44
+ .filter((n) => Number.isFinite(n))
45
+ .sort((a, b) => a - b);
46
+ if (numericKeys.length === 0) {
47
+ return `Question ${fallback}`;
48
+ }
49
+ // Keys are 0-based; display numbers are 1-based.
50
+ const min = numericKeys[0] + 1;
51
+ const max = numericKeys[numericKeys.length - 1] + 1;
52
+ return min === max ? `Question ${min}` : `Questions ${min}–${max}`;
53
+ }
54
+ /** Stable empty object so React.memo children are not broken by new `{}`. */
55
+ const EMPTY_OBJECT = {};
56
+ /**
57
+ * Renders one structured-form question's HTML with inline branded inputs.
58
+ *
59
+ * CRITICAL: We set `innerHTML` imperatively via a ref exactly ONCE on mount and
60
+ * attach native input listeners, bypassing React reconciliation. Using
61
+ * `dangerouslySetInnerHTML` would re-apply HTML on every render, destroying the
62
+ * DOM inputs and losing focus while the student types.
63
+ */
64
+ const StructuredFormBody = memo(function StructuredFormBody({ question, initialAnswers, onBlankChange, isReviewMode, }) {
65
+ const containerRef = useRef(null);
66
+ const onBlankChangeRef = useRef(onBlankChange);
67
+ onBlankChangeRef.current = onBlankChange;
68
+ const mountedRef = useRef(false);
69
+ const initialAnswersRef = useRef(initialAnswers);
70
+ const processedHtml = useMemo(() => buildHtmlWithInputPlaceholders(question.content), [question.content]);
71
+ useEffect(() => {
72
+ const container = containerRef.current;
73
+ if (!container)
74
+ return;
75
+ if (!mountedRef.current) {
76
+ container.innerHTML = processedHtml;
77
+ mountedRef.current = true;
78
+ }
79
+ const inputs = container.querySelectorAll('input.ec-blank-input');
80
+ const handlers = [];
81
+ inputs.forEach((input) => {
82
+ const key = input.getAttribute('data-blank-key') || '';
83
+ const wrapper = container.querySelector(`[data-blank-wrapper="${key}"]`);
84
+ const userAnswer = initialAnswersRef.current[key] || '';
85
+ const correctAnswer = question.answers[key] || '';
86
+ const hasUserAnswer = userAnswer.trim().length > 0;
87
+ const isCorrect = hasUserAnswer &&
88
+ normalizeStructuredAnswer(userAnswer, question.caseSensitive) ===
89
+ normalizeStructuredAnswer(correctAnswer, question.caseSensitive);
90
+ const shouldShowReviewText = isReviewMode && !isCorrect && correctAnswer.trim().length > 0;
91
+ input.value = shouldShowReviewText
92
+ ? `${hasUserAnswer ? userAnswer : '(empty)'} → ${correctAnswer}`
93
+ : userAnswer;
94
+ input.disabled = isReviewMode;
95
+ const blankStyle = question.blankStyle || 'word';
96
+ const isLetter = blankStyle === 'letter';
97
+ const isPhrase = blankStyle === 'phrase';
98
+ if (isLetter) {
99
+ input.maxLength = Math.max(1, (correctAnswer || ' ').trim().length || 1);
100
+ }
101
+ input.className = cn('ec-blank-input inline-block bg-blue-50/30 text-base font-semibold leading-tight text-[#0f1730] align-baseline outline-none transition-all hover:bg-blue-50/60 focus:bg-blue-50/80', isLetter
102
+ ? 'h-8 w-8 rounded border-2 border-[#1167e8] px-0 text-center uppercase'
103
+ : isPhrase
104
+ ? 'min-w-[9rem] max-w-[18rem] w-[14ch] rounded-t border-0 border-b-2 border-dashed border-[#1167e8] px-2 py-0.5 text-center focus:border-solid focus:border-[#0b57d0]'
105
+ : // blankStyle=word — compact for one token
106
+ 'min-w-[4.5rem] max-w-[7.5rem] w-[8ch] rounded-t border-0 border-b-2 border-dashed border-[#1167e8] px-1 py-0.5 text-center focus:border-solid focus:border-[#0b57d0]', isReviewMode
107
+ ? isCorrect
108
+ ? 'border-solid border-[#22c55e] bg-emerald-50 text-[#15803d]'
109
+ : hasUserAnswer
110
+ ? 'border-solid border-[#ef4444] bg-rose-50 text-red-700'
111
+ : 'border-solid border-[#f97316] bg-amber-50 text-[#c2410c]'
112
+ : '');
113
+ if (!isLetter) {
114
+ input.size = isPhrase ? 18 : 10;
115
+ }
116
+ // letter inline box — host FE may not scan Tailwind
117
+ if (isLetter) {
118
+ input.style.display = 'inline-block';
119
+ input.style.width = '1.75rem';
120
+ input.style.height = '1.75rem';
121
+ input.style.minWidth = '1.75rem';
122
+ input.style.maxWidth = '1.75rem';
123
+ input.style.padding = '0';
124
+ input.style.margin = '0 2px';
125
+ input.style.borderWidth = '2px';
126
+ input.style.borderStyle = 'solid';
127
+ input.style.borderRadius = '4px';
128
+ input.style.textAlign = 'center';
129
+ input.style.fontWeight = '700';
130
+ input.style.textTransform = 'uppercase';
131
+ input.style.boxSizing = 'border-box';
132
+ }
133
+ const numberBadge = container.querySelector(`[data-blank-number="${key}"]`);
134
+ if (numberBadge) {
135
+ // Always set 1-based display number here so it is correct even if
136
+ // the cached innerHTML was built with a different formatter.
137
+ numberBadge.textContent = toDisplayNumber(key);
138
+ numberBadge.className = cn('ec-blank-number mr-1 inline-flex h-5 min-w-5 items-center justify-center rounded-full px-1.5 text-[11px] font-bold leading-none text-white align-baseline shadow-xs', isReviewMode
139
+ ? isCorrect
140
+ ? 'bg-[#22c55e]'
141
+ : hasUserAnswer
142
+ ? 'bg-[#ef4444]'
143
+ : 'bg-[#f97316]'
144
+ : 'bg-[#0b57d0]');
145
+ }
146
+ if (wrapper) {
147
+ wrapper.className = cn('ec-blank-wrapper inline-flex items-baseline whitespace-nowrap align-baseline', isReviewMode ? 'mx-1' : 'mx-0.5');
148
+ }
149
+ const handler = (e) => {
150
+ const target = e.target;
151
+ onBlankChangeRef.current(key, target.value);
152
+ };
153
+ input.addEventListener('input', handler);
154
+ handlers.push({ el: input, handler });
155
+ });
156
+ return () => {
157
+ handlers.forEach(({ el, handler }) => {
158
+ el.removeEventListener('input', handler);
159
+ });
160
+ };
161
+ // eslint-disable-next-line react-hooks/exhaustive-deps
162
+ }, [processedHtml, isReviewMode]);
163
+ return (_jsxs("div", { className: "space-y-3", children: [question.title && (_jsx("h3", { className: "text-base font-semibold text-[#0f1730]", children: question.title })), question.imageUrl && (_jsx("div", { className: "flex justify-center", children: _jsx("div", { className: "overflow-hidden rounded-lg border border-[#d9e4f2]", children: _jsx(ResolvedImage, { src: question.imageUrl, alt: question.title || 'Question image', className: "max-h-56 w-auto object-contain" }) }) })), _jsx("div", { ref: containerRef, className: "ec-structured-content w-full overflow-x-auto rounded-xl border border-[#d9e4f2] bg-[#f4f9ff] p-5 text-base leading-relaxed text-[#0f1730] [&_table]:w-full [&_table]:border-collapse [&_td]:border [&_td]:border-[#d9e4f2] [&_td]:px-4 [&_td]:py-3 [&_td]:align-top [&_th]:border [&_th]:border-[#d9e4f2] [&_th]:bg-[#e9f3ff] [&_th]:px-4 [&_th]:py-3 [&_th]:text-left [&_th]:font-semibold" }), isReviewMode && Object.keys(question.answers).length > 0 && (_jsxs("div", { className: "rounded-xl border border-[#d9e4f2] bg-white/70 p-3", children: [_jsx("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-[#64748b]", children: "Correct answers" }), _jsx("div", { className: "mt-1 flex flex-wrap gap-1.5", children: Object.entries(question.answers).map(([key, val]) => (_jsxs("span", { className: "rounded border border-[#b9edc8] bg-[#ddf8e7] px-2 py-0.5 text-xs font-semibold text-[#15803d]", children: [key, ": ", val] }, key))) })] }))] }));
164
+ });
165
+ /**
166
+ * Branded English Certification renderer for `WORD_FILL_STRUCTURED_FORM`.
167
+ *
168
+ * Replaces the shared Movers two-column grid (which squeezed IELTS tables into
169
+ * narrow columns) with a full-width branded layout. Reuses
170
+ * {@link transformWordFillStructuredForm} so the answer shape
171
+ * (`{ [questionId]: { [blankKey]: value } }`) stays compatible with the autosave
172
+ * and grading store.
173
+ */
174
+ export function EcWordFillStructuredForm({ part, answers, onAnswerChange, isReviewMode = false, }) {
175
+ const { questions, instruction, groupTitle, groupContent, groupImageUrl } = useMemo(() => transformWordFillStructuredForm(part.questions), [part.questions]);
176
+ const answersRef = useRef(answers);
177
+ answersRef.current = answers;
178
+ const onAnswerChangeRef = useRef(onAnswerChange);
179
+ onAnswerChangeRef.current = onAnswerChange;
180
+ const handleBlankChange = useCallback((questionId, key, value) => {
181
+ const currentAnswer = answersRef.current[questionId] || {};
182
+ onAnswerChangeRef.current(questionId, { ...currentAnswer, [key]: value });
183
+ }, []);
184
+ const blankChangeCallbacks = useMemo(() => {
185
+ const map = {};
186
+ questions.forEach((q) => {
187
+ map[q.id] = (key, value) => handleBlankChange(q.id, key, value);
188
+ });
189
+ return map;
190
+ }, [questions, handleBlankChange]);
191
+ return (_jsxs("div", { className: "flex flex-col gap-4", children: [instruction && (_jsx("div", { className: "rounded-xl border border-[#d9e4f2] bg-[#e9f3ff] px-4 py-2.5 text-sm font-semibold uppercase tracking-wide text-[#0b57d0]", children: instruction })), groupTitle && (_jsx("h2", { className: "text-center text-lg font-bold uppercase text-[#0b57d0]", children: groupTitle })), groupImageUrl && (_jsx("div", { className: "flex items-center justify-center", children: _jsx(ResolvedImage, { src: groupImageUrl, alt: groupTitle || 'Group image', className: "max-h-[300px] rounded-lg object-contain shadow-md" }) })), _jsx("div", { className: "flex flex-col gap-4", children: questions.map((q) => (_jsxs("div", { id: englishCertQuestionDomId(q.id), className: "scroll-mt-24 rounded-2xl border border-[#d9e4f2] bg-white/90 p-4 shadow-[0_14px_35px_rgba(13,71,161,0.10)] backdrop-blur-md", children: [_jsx("div", { className: "mb-2 flex items-center gap-2", children: _jsx("span", { className: "inline-flex items-center rounded-full bg-gradient-to-b from-[#1f7cff] to-[#0b57d0] px-3 py-1 text-xs font-bold text-white shadow-sm", children: formatBlankRange(q.answers, q.questionNumber) }) }), _jsx(StructuredFormBody, { question: q, initialAnswers: answers[q.id] || EMPTY_OBJECT, onBlankChange: blankChangeCallbacks[q.id], isReviewMode: isReviewMode })] }, q.id))) })] }));
192
+ }
@@ -1 +1,60 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useMemo as r}from"react";import{ListChecks as s,PenLine as l,CheckCircle2 as a,AlertCircle as d}from"lucide-react";import{ResolvedImage as i}from"../../../components/common/ResolvedImage";import{HtmlOrText as o}from"../../../shared/lib/html-content";import{cn as n}from"../../../shared/lib/utils";import{transformWriteShortLetter as c}from"../../../components/exams/take/utils/question-transformers";import{englishCertQuestionDomId as m}from"./english-cert-scroll";export function EcWritingTask({part:f,answers:x,onAnswerChange:p,isReviewMode:u=!1}){const b=f.questions[0],h=r(()=>b?c(b):null,[b]),g=h?.minWords,w=h?.maxWords,v=b&&x[b.id]||"",N=function(e){const t=e.trim();return t?t.split(/\s+/).length:0}(v),y=r(()=>{if(g&&N<g){const e=g-N;return{text:`Add ${e} more word${1===e?"":"s"} to reach the minimum`,ok:!1}}if(w&&N>w){const e=N-w;return{text:`${e} word${1===e?"":"s"} over the maximum limit`,ok:!1}}return g||w?{text:"Word count requirement met",ok:!0}:null},[N,g,w]);if(!b||!h)return null;const{prompt:k,requirements:$,guideImages:j}=h,W=(!g||N>=g)&&(!w||N<=w),_=w||g||0,q=_?Math.min(100,Math.round(N/_*100)):0;return t("div",{id:m(b.id),className:"scroll-mt-24 flex flex-col gap-5",children:[t("div",{className:"flex flex-col gap-5",children:[t("div",{className:"overflow-hidden rounded-2xl border border-[#cfe0f7] bg-white shadow-[0_18px_40px_rgba(13,71,161,0.10)]",children:[k&&e(o,{value:k,as:"div",className:"whitespace-pre-line px-5 pb-4 pt-5 text-base leading-relaxed text-[#0f1730]"}),$&&$.length>0&&e("div",{className:n("px-5 pb-4",!k&&"pt-5"),children:t("div",{className:"rounded-xl border border-[#e3edfb] bg-[#f6faff] p-4",children:[t("div",{className:"flex items-center gap-1.5 text-[11px] font-bold uppercase tracking-wider text-[#0b57d0]",children:[e(s,{className:"h-3.5 w-3.5"}),"Requirements"]}),e("ul",{className:"mt-2 list-inside list-disc space-y-1.5 text-sm text-[#0f1730]",children:$.map((t,r)=>e("li",{children:e(o,{value:t,as:"span"})},r))})]})}),g&&t("div",{className:"border-t border-[#e3edfb] bg-[#f9fbfe] px-5 py-3 text-sm font-semibold text-[#0f1730]",children:["Write at least ",g," words."]})]}),j&&j.length>0&&e("div",{className:"flex flex-col items-center justify-center gap-4 rounded-2xl border border-[#d9e4f2] bg-white/60 p-4",children:j.map((t,r)=>e(i,{src:t,alt:`Guide ${r+1}`,className:"w-full max-w-3xl rounded-xl bg-white object-contain shadow-sm"},r))})]}),t("div",{className:"overflow-hidden rounded-2xl border border-[#cfe0f7] bg-white shadow-[0_18px_40px_rgba(13,71,161,0.10)]",children:[t("div",{className:"flex items-center gap-1.5 border-b border-[#e3edfb] bg-[#f6faff] px-4 py-2.5 text-xs font-bold uppercase tracking-wider text-[#0b57d0]",children:[e(l,{className:"h-3.5 w-3.5"}),"Your answer"]}),e("textarea",{value:v,disabled:u,placeholder:"Write your essay here…",onChange:e=>p(b.id,e.target.value),rows:14,className:n("w-full resize-y border-0 bg-white px-5 py-4 text-sm leading-relaxed text-[#0f1730] outline-none transition-all placeholder:text-[#9aa9c2] focus:bg-[#fbfdff]",u&&"cursor-not-allowed opacity-80")}),t("div",{className:"flex flex-col gap-2 border-t border-[#e3edfb] bg-[#f9fbfe] px-4 py-3",children:[t("div",{className:"flex items-center justify-between text-xs",children:[t("span",{className:n("inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 font-semibold",W?"bg-[#ddf8e7] text-[#15803d]":"bg-[#fde8e8] text-[#ef4444]"),children:[N," words"]}),(g||w)&&t("span",{className:"font-medium text-[#64748b]",children:[g?`Min ${g}`:"",g&&w?" · ":"",w?`Max ${w}`:""]})]}),_>0&&e("div",{className:"h-1.5 w-full overflow-hidden rounded-full bg-[#e3edfb]",children:e("div",{className:n("h-full rounded-full transition-all",W?"bg-[#15803d]":"bg-[#1167e8]"),style:{width:`${q}%`}})}),y&&t("div",{className:n("flex items-center gap-1.5 text-xs font-medium",y.ok?"text-[#15803d]":"text-[#64748b]"),children:[y.ok?e(a,{className:"h-3.5 w-3.5 shrink-0"}):e(d,{className:"h-3.5 w-3.5 shrink-0"}),y.text]})]})]})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useMemo } from 'react';
4
+ import { ListChecks, PenLine, CheckCircle2, AlertCircle } from 'lucide-react';
5
+ import { ResolvedImage } from '../../../components/common/ResolvedImage';
6
+ import { HtmlOrText } from '../../../shared/lib/html-content';
7
+ import { cn } from '../../../shared/lib/utils';
8
+ import { transformWriteShortLetter } from '../../../components/exams/take/utils/question-transformers';
9
+ import { englishCertQuestionDomId } from './english-cert-scroll';
10
+ /** Count words in a free-text answer (collapses whitespace). */
11
+ function countWords(text) {
12
+ const trimmed = text.trim();
13
+ if (!trimmed)
14
+ return 0;
15
+ return trimmed.split(/\s+/).length;
16
+ }
17
+ /**
18
+ * Branded English Certification renderer for `WRITE_A_SHORT_LETTER`.
19
+ *
20
+ * Shows the prompt, optional requirements/guide images and a large branded
21
+ * textarea with a live word counter. The stored answer is a plain string at
22
+ * `answers[questionId]`, matching the shared writing renderer so the grading
23
+ * store stays compatible.
24
+ */
25
+ export function EcWritingTask({ part, answers, onAnswerChange, isReviewMode = false, }) {
26
+ const firstQuestion = part.questions[0];
27
+ const data = useMemo(() => (firstQuestion ? transformWriteShortLetter(firstQuestion) : null), [firstQuestion]);
28
+ const minWords = data?.minWords;
29
+ const maxWords = data?.maxWords;
30
+ const answer = (firstQuestion && answers[firstQuestion.id]) || '';
31
+ const wordCount = countWords(answer);
32
+ const statusMessage = useMemo(() => {
33
+ if (minWords && wordCount < minWords) {
34
+ const remaining = minWords - wordCount;
35
+ return { text: `Add ${remaining} more word${remaining === 1 ? '' : 's'} to reach the minimum`, ok: false };
36
+ }
37
+ if (maxWords && wordCount > maxWords) {
38
+ const over = wordCount - maxWords;
39
+ return { text: `${over} word${over === 1 ? '' : 's'} over the maximum limit`, ok: false };
40
+ }
41
+ if (minWords || maxWords) {
42
+ return { text: 'Word count requirement met', ok: true };
43
+ }
44
+ return null;
45
+ }, [wordCount, minWords, maxWords]);
46
+ if (!firstQuestion || !data) {
47
+ return null;
48
+ }
49
+ const { prompt, requirements, guideImages } = data;
50
+ const meetsMin = minWords ? wordCount >= minWords : true;
51
+ const withinMax = maxWords ? wordCount <= maxWords : true;
52
+ const isValid = meetsMin && withinMax;
53
+ const progressTarget = maxWords || minWords || 0;
54
+ const progressPct = progressTarget
55
+ ? Math.min(100, Math.round((wordCount / progressTarget) * 100))
56
+ : 0;
57
+ return (_jsxs("div", { id: englishCertQuestionDomId(firstQuestion.id), className: "scroll-mt-24 flex flex-col gap-5", children: [_jsxs("div", { className: "flex flex-col gap-5", children: [_jsxs("div", { className: "overflow-hidden rounded-2xl border border-[#cfe0f7] bg-white shadow-[0_18px_40px_rgba(13,71,161,0.10)]", children: [prompt && (_jsx(HtmlOrText, { value: prompt, as: "div", className: "whitespace-pre-line px-5 pb-4 pt-5 text-base leading-relaxed text-[#0f1730]" })), requirements && requirements.length > 0 && (_jsx("div", { className: cn('px-5 pb-4', !prompt && 'pt-5'), children: _jsxs("div", { className: "rounded-xl border border-[#e3edfb] bg-[#f6faff] p-4", children: [_jsxs("div", { className: "flex items-center gap-1.5 text-[11px] font-bold uppercase tracking-wider text-[#0b57d0]", children: [_jsx(ListChecks, { className: "h-3.5 w-3.5" }), "Requirements"] }), _jsx("ul", { className: "mt-2 list-inside list-disc space-y-1.5 text-sm text-[#0f1730]", children: requirements.map((req, idx) => (_jsx("li", { children: _jsx(HtmlOrText, { value: req, as: "span" }) }, idx))) })] }) })), minWords && (_jsxs("div", { className: "border-t border-[#e3edfb] bg-[#f9fbfe] px-5 py-3 text-sm font-semibold text-[#0f1730]", children: ["Write at least ", minWords, " words."] }))] }), guideImages && guideImages.length > 0 && (_jsx("div", { className: "flex flex-col items-center justify-center gap-4 rounded-2xl border border-[#d9e4f2] bg-white/60 p-4", children: guideImages.map((img, idx) => (_jsx(ResolvedImage, { src: img, alt: `Guide ${idx + 1}`, className: "w-full max-w-3xl rounded-xl bg-white object-contain shadow-sm" }, idx))) }))] }), _jsxs("div", { className: "overflow-hidden rounded-2xl border border-[#cfe0f7] bg-white shadow-[0_18px_40px_rgba(13,71,161,0.10)]", children: [_jsxs("div", { className: "flex items-center gap-1.5 border-b border-[#e3edfb] bg-[#f6faff] px-4 py-2.5 text-xs font-bold uppercase tracking-wider text-[#0b57d0]", children: [_jsx(PenLine, { className: "h-3.5 w-3.5" }), "Your answer"] }), _jsx("textarea", { value: answer, disabled: isReviewMode, placeholder: "Write your essay here\u2026", onChange: (e) => onAnswerChange(firstQuestion.id, e.target.value), rows: 14, className: cn('w-full resize-y border-0 bg-white px-5 py-4 text-sm leading-relaxed text-[#0f1730] outline-none transition-all placeholder:text-[#9aa9c2] focus:bg-[#fbfdff]', isReviewMode && 'cursor-not-allowed opacity-80') }), _jsxs("div", { className: "flex flex-col gap-2 border-t border-[#e3edfb] bg-[#f9fbfe] px-4 py-3", children: [_jsxs("div", { className: "flex items-center justify-between text-xs", children: [_jsxs("span", { className: cn('inline-flex items-center gap-1.5 rounded-full px-2.5 py-1 font-semibold', isValid
58
+ ? 'bg-[#ddf8e7] text-[#15803d]'
59
+ : 'bg-[#fde8e8] text-[#ef4444]'), children: [wordCount, " words"] }), (minWords || maxWords) && (_jsxs("span", { className: "font-medium text-[#64748b]", children: [minWords ? `Min ${minWords}` : '', minWords && maxWords ? ' · ' : '', maxWords ? `Max ${maxWords}` : ''] }))] }), progressTarget > 0 && (_jsx("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-[#e3edfb]", children: _jsx("div", { className: cn('h-full rounded-full transition-all', isValid ? 'bg-[#15803d]' : 'bg-[#1167e8]'), style: { width: `${progressPct}%` } }) })), statusMessage && (_jsxs("div", { className: cn('flex items-center gap-1.5 text-xs font-medium', statusMessage.ok ? 'text-[#15803d]' : 'text-[#64748b]'), children: [statusMessage.ok ? (_jsx(CheckCircle2, { className: "h-3.5 w-3.5 shrink-0" })) : (_jsx(AlertCircle, { className: "h-3.5 w-3.5 shrink-0" })), statusMessage.text] }))] })] })] }));
60
+ }
@@ -1 +1,108 @@
1
- "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{useRef as t,useState as a,useEffect as i}from"react";import{Play as d,Pause as l,RefreshCw as n,Lock as o}from"lucide-react";import{usePresignedFileUrl as s}from"../../../shared/lib/hooks/usePresignedFileUrl";import{cn as c}from"../../../shared/lib/utils";export function EnglishCertificationAudioPlayer({audioUrl:u,partNumber:m=0,isLoading:p=!1,isLocked:f=!1,onFirstPlay:b}){const g=t(null),{previewUrl:x}=s(u||void 0),[h,v]=a(!1),[y,w]=a(0),[N,L]=a(0),[E,_]=a(null),k=t(!1),P=p||!u||!!E||f,A=e=>{if(!e||isNaN(e))return"00:00";const r=Math.floor(e/60),t=Math.floor(e%60);return`${r.toString().padStart(2,"0")}:${t.toString().padStart(2,"0")}`};i(()=>{const e=g.current;if(!e)return;const r=()=>w(e.currentTime),t=()=>{L(e.duration),_(null)},a=()=>v(!1),i=()=>{_("Failed to load audio file"),v(!1)};return e.addEventListener("timeupdate",r),e.addEventListener("loadedmetadata",t),e.addEventListener("ended",a),e.addEventListener("error",i),()=>{e.removeEventListener("timeupdate",r),e.removeEventListener("loadedmetadata",t),e.removeEventListener("ended",a),e.removeEventListener("error",i)}},[]),i(()=>{g.current&&x&&(g.current.src=x,w(0),v(!1),_(null))},[x]);const $=N>0?y/N*100:0,j=f?"Already played":E?"Audio unavailable":p?"Loading...":h?"Playing...":y>0?"Paused":"Ready to play";return r("div",{className:"grid w-full grid-cols-[64px_1fr] items-center gap-4 rounded-[10px] border border-[#d9e4f2] bg-white/90 p-4 shadow-[0_5px_16px_rgba(13,71,161,0.08)] sm:gap-[18px] sm:px-5",children:[e("audio",{ref:g}),e("button",{type:"button",onClick:()=>{g.current&&!f&&(h?(g.current.pause(),v(!1)):(k.current||(k.current=!0,b?.()),g.current.play().catch(e=>{_(`Failed to play audio: ${e.message}`)}),v(!0)))},disabled:P,className:"grid h-14 w-14 place-items-center rounded-full text-2xl text-white shadow-[0_12px_22px_rgba(11,87,208,0.22)] transition-transform hover:scale-[1.03] disabled:cursor-not-allowed disabled:opacity-50",style:{background:"linear-gradient(180deg, #1f7cff, #0b57d0)"},"aria-label":f?"Audio locked, already played":h?"Pause audio":"Play audio",children:f?e(o,{size:22}):h?e(l,{size:24}):e(d,{size:24,className:"ml-0.5"})}),r("div",{className:"min-w-0",children:[e("div",{className:"flex items-center justify-between gap-3",children:r("span",{className:"text-[18px] font-black text-[#0f1730]",children:["Listening Audio",m>0&&` · Part ${m}`]})}),r("div",{className:"mt-1 flex items-center gap-2.5",children:[e("span",{className:c("text-sm font-bold",E?"text-red-500":"text-[#64748b]"),children:E??j}),E&&r("button",{type:"button",onClick:()=>{const e=g.current;e&&x&&(_(null),w(0),v(!1),e.src=x,e.load())},disabled:!u,className:"inline-flex items-center gap-1.5 rounded-full border border-red-200 bg-red-50 px-3 py-1 text-xs font-bold text-red-600 transition-colors hover:bg-red-100 disabled:cursor-not-allowed disabled:opacity-50","aria-label":"Retry loading audio",children:[e(n,{size:13}),"Retry"]})]}),r("div",{className:"mt-2 grid grid-cols-[46px_1fr_46px] items-center gap-2.5 text-sm font-bold tabular-nums text-[#0f1730]",children:[e("span",{children:A(y)}),e("div",{className:"relative h-2 w-full overflow-hidden rounded-full bg-[#dfe9f6]",role:"progressbar","aria-label":"Audio progress","aria-valuemin":0,"aria-valuemax":N||0,"aria-valuenow":y,children:e("div",{className:"absolute inset-y-0 left-0 rounded-full",style:{width:`${$}%`,background:"linear-gradient(90deg, #1f7cff, #0b57d0)"}})}),e("span",{children:N>0?A(N):"00:00"})]})]})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useRef, useState, useEffect } from 'react';
4
+ import { Play, Pause, RefreshCw, Lock } from 'lucide-react';
5
+ import { usePresignedFileUrl } from '../../../shared/lib/hooks/usePresignedFileUrl';
6
+ import { cn } from '../../../shared/lib/utils';
7
+ /**
8
+ * Branded Ocean Edu audio bar for English Certification Listening sections.
9
+ *
10
+ * Enforces the TOEIC "play once" rule: no seeking/scrubbing and no replay —
11
+ * once playback starts, `onFirstPlay` locks the part permanently (survives
12
+ * reload via the caller's persisted state).
13
+ */
14
+ export function EnglishCertificationAudioPlayer({ audioUrl, partNumber = 0, isLoading = false, isLocked = false, onFirstPlay, }) {
15
+ const audioRef = useRef(null);
16
+ // Resolve S3 storage keys to presigned URLs (HTTP URLs pass through unchanged)
17
+ const { previewUrl: resolvedAudioUrl } = usePresignedFileUrl(audioUrl || undefined);
18
+ const [isPlaying, setIsPlaying] = useState(false);
19
+ const [currentTime, setCurrentTime] = useState(0);
20
+ const [duration, setDuration] = useState(0);
21
+ const [error, setError] = useState(null);
22
+ const hasNotifiedFirstPlayRef = useRef(false);
23
+ const isControlsDisabled = isLoading || !audioUrl || !!error || isLocked;
24
+ const togglePlayPause = () => {
25
+ if (!audioRef.current || isLocked)
26
+ return;
27
+ if (isPlaying) {
28
+ audioRef.current.pause();
29
+ setIsPlaying(false);
30
+ }
31
+ else {
32
+ if (!hasNotifiedFirstPlayRef.current) {
33
+ hasNotifiedFirstPlayRef.current = true;
34
+ onFirstPlay?.();
35
+ }
36
+ audioRef.current.play().catch((err) => {
37
+ setError(`Failed to play audio: ${err.message}`);
38
+ });
39
+ setIsPlaying(true);
40
+ }
41
+ };
42
+ const handleRetry = () => {
43
+ const audio = audioRef.current;
44
+ if (!audio || !resolvedAudioUrl)
45
+ return;
46
+ setError(null);
47
+ setCurrentTime(0);
48
+ setIsPlaying(false);
49
+ audio.src = resolvedAudioUrl;
50
+ audio.load();
51
+ };
52
+ const formatTime = (seconds) => {
53
+ if (!seconds || isNaN(seconds))
54
+ return '00:00';
55
+ const mins = Math.floor(seconds / 60);
56
+ const secs = Math.floor(seconds % 60);
57
+ return `${mins.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
58
+ };
59
+ useEffect(() => {
60
+ const audio = audioRef.current;
61
+ if (!audio)
62
+ return;
63
+ const handleTimeUpdate = () => setCurrentTime(audio.currentTime);
64
+ const handleLoadedMetadata = () => {
65
+ setDuration(audio.duration);
66
+ setError(null);
67
+ };
68
+ const handleEnded = () => setIsPlaying(false);
69
+ const handleError = () => {
70
+ setError('Failed to load audio file');
71
+ setIsPlaying(false);
72
+ };
73
+ audio.addEventListener('timeupdate', handleTimeUpdate);
74
+ audio.addEventListener('loadedmetadata', handleLoadedMetadata);
75
+ audio.addEventListener('ended', handleEnded);
76
+ audio.addEventListener('error', handleError);
77
+ return () => {
78
+ audio.removeEventListener('timeupdate', handleTimeUpdate);
79
+ audio.removeEventListener('loadedmetadata', handleLoadedMetadata);
80
+ audio.removeEventListener('ended', handleEnded);
81
+ audio.removeEventListener('error', handleError);
82
+ };
83
+ }, []);
84
+ useEffect(() => {
85
+ if (audioRef.current && resolvedAudioUrl) {
86
+ audioRef.current.src = resolvedAudioUrl;
87
+ setCurrentTime(0);
88
+ setIsPlaying(false);
89
+ setError(null);
90
+ }
91
+ }, [resolvedAudioUrl]);
92
+ const progressPercent = duration > 0 ? (currentTime / duration) * 100 : 0;
93
+ const statusLabel = isLocked
94
+ ? 'Already played'
95
+ : error
96
+ ? 'Audio unavailable'
97
+ : isLoading
98
+ ? 'Loading...'
99
+ : isPlaying
100
+ ? 'Playing...'
101
+ : currentTime > 0
102
+ ? 'Paused'
103
+ : 'Ready to play';
104
+ return (_jsxs("div", { className: "grid w-full grid-cols-[64px_1fr] items-center gap-4 rounded-[10px] border border-[#d9e4f2] bg-white/90 p-4 shadow-[0_5px_16px_rgba(13,71,161,0.08)] sm:gap-[18px] sm:px-5", children: [_jsx("audio", { ref: audioRef }), _jsx("button", { type: "button", onClick: togglePlayPause, disabled: isControlsDisabled, className: "grid h-14 w-14 place-items-center rounded-full text-2xl text-white shadow-[0_12px_22px_rgba(11,87,208,0.22)] transition-transform hover:scale-[1.03] disabled:cursor-not-allowed disabled:opacity-50", style: { background: 'linear-gradient(180deg, #1f7cff, #0b57d0)' }, "aria-label": isLocked ? 'Audio locked, already played' : isPlaying ? 'Pause audio' : 'Play audio', children: isLocked ? (_jsx(Lock, { size: 22 })) : isPlaying ? (_jsx(Pause, { size: 24 })) : (_jsx(Play, { size: 24, className: "ml-0.5" })) }), _jsxs("div", { className: "min-w-0", children: [_jsx("div", { className: "flex items-center justify-between gap-3", children: _jsxs("span", { className: "text-[18px] font-black text-[#0f1730]", children: ["Listening Audio", partNumber > 0 && ` · Part ${partNumber}`] }) }), _jsxs("div", { className: "mt-1 flex items-center gap-2.5", children: [_jsx("span", { className: cn('text-sm font-bold', error ? 'text-red-500' : 'text-[#64748b]'), children: error ?? statusLabel }), error && (_jsxs("button", { type: "button", onClick: handleRetry, disabled: !audioUrl, className: "inline-flex items-center gap-1.5 rounded-full border border-red-200 bg-red-50 px-3 py-1 text-xs font-bold text-red-600 transition-colors hover:bg-red-100 disabled:cursor-not-allowed disabled:opacity-50", "aria-label": "Retry loading audio", children: [_jsx(RefreshCw, { size: 13 }), "Retry"] }))] }), _jsxs("div", { className: "mt-2 grid grid-cols-[46px_1fr_46px] items-center gap-2.5 text-sm font-bold tabular-nums text-[#0f1730]", children: [_jsx("span", { children: formatTime(currentTime) }), _jsx("div", { className: "relative h-2 w-full overflow-hidden rounded-full bg-[#dfe9f6]", role: "progressbar", "aria-label": "Audio progress", "aria-valuemin": 0, "aria-valuemax": duration || 0, "aria-valuenow": currentTime, children: _jsx("div", { className: "absolute inset-y-0 left-0 rounded-full", style: {
105
+ width: `${progressPercent}%`,
106
+ background: 'linear-gradient(90deg, #1f7cff, #0b57d0)',
107
+ } }) }), _jsx("span", { children: duration > 0 ? formatTime(duration) : '00:00' })] })] })] }));
108
+ }
@@ -1 +1,11 @@
1
- "use client";import{jsx as e}from"react/jsx-runtime";import i from"next/image";import{cn as a}from"../../../shared/lib/utils";export function EnglishCertificationBrand({className:t}){return e("div",{className:a("flex items-center",t),"aria-label":"Ocean Edu",children:e(i,{src:"/images/logo-ngang.png",alt:"Ocean Edu",width:180,height:40,priority:!0,className:"h-10 w-auto object-contain"})})}
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import Image from 'next/image';
4
+ import { cn } from '../../../shared/lib/utils';
5
+ /**
6
+ * Ocean Edu brand lockup: horizontal logo image.
7
+ * Used in the English Certification header.
8
+ */
9
+ export function EnglishCertificationBrand({ className }) {
10
+ return (_jsx("div", { className: cn('flex items-center', className), "aria-label": "Ocean Edu", children: _jsx(Image, { src: "/images/logo-ngang.png", alt: "Ocean Edu", width: 180, height: 40, priority: true, className: "h-10 w-auto object-contain" }) }));
11
+ }
@@ -1 +1,34 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useState as i}from"react";import{List as r}from"lucide-react";import{Sheet as o,SheetContent as l,SheetHeader as n,SheetTitle as a}from"../../../components/ui/sheet";import{EnglishCertificationHeader as s}from"./EnglishCertificationHeader";import{EnglishCertificationNavigationPanel as c}from"./EnglishCertificationNavigationPanel";import{EnglishCertificationFooterBar as d}from"./EnglishCertificationFooterBar";import{ENGLISH_CERT_APP_BACKGROUND as h,ENGLISH_CERT_PRIMARY_GRADIENT as m}from"./english-certification-theme";export function EnglishCertificationExamLayout({children:f,headerProps:x,navigationProps:p,footerProps:b}){const[g,u]=i(!0),[w,k]=i(!1),v={...p,onQuestionClick:e=>{p.onQuestionClick(e),k(!1)},onPartClick:e=>{p.onPartClick(e),k(!1)}};return t("div",{className:"flex h-screen flex-col overflow-hidden text-[#0f1730]",style:{background:h},children:[e(s,{...x}),t("div",{className:"flex flex-1 gap-4 overflow-hidden px-4 py-4 lg:px-6",children:[e("main",{id:"exam-main-scroll-area",className:"min-w-0 flex-1 overflow-y-auto",children:f}),g&&t("aside",{className:"hidden w-[360px] shrink-0 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 lg:flex","aria-label":"Answer sheet navigation",children:[t("div",{className:"flex h-14 items-center justify-between px-5 text-white",style:{background:m},children:[e("h3",{className:"text-[18px] font-black tracking-[0.1em]",children:"ANSWER SHEET"}),e("button",{type:"button",onClick:()=>u(!1),className:"text-sm font-bold text-white/90 hover:text-white",children:"Hide ≪"})]}),e("div",{className:"flex-1 overflow-hidden",children:e(c,{...v})})]}),!g&&e("button",{type:"button",onClick:()=>u(!0),className:"fixed right-0 top-24 z-30 hidden rounded-l-lg border border-r-0 border-[#d9e4f2] bg-white px-2 py-3 text-sm font-bold text-[#0b57d0] shadow-sm hover:bg-blue-50 lg:block","aria-label":"Show answer sheet",children:"≫"})]}),b&&e(d,{...b}),e("button",{type:"button",onClick:()=>k(!0),className:"fixed bottom-24 right-4 z-30 flex h-12 w-12 items-center justify-center rounded-full text-white shadow-lg lg:hidden",style:{background:m},"aria-label":"Open answer sheet",children:e(r,{className:"h-5 w-5"})}),e(o,{open:w,onOpenChange:k,children:t(l,{side:"bottom",className:"h-[78vh] p-0",children:[e(n,{className:"flex h-14 flex-row items-center px-5 text-white",style:{background:m},children:e(a,{className:"text-[18px] font-black tracking-[0.1em] text-white",children:"ANSWER SHEET"})}),e("div",{className:"h-[calc(78vh-3.5rem)] overflow-hidden",children:e(c,{...v})})]})})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import { List } from 'lucide-react';
5
+ import { Sheet, SheetContent, SheetHeader, SheetTitle, } from '../../../components/ui/sheet';
6
+ import { EnglishCertificationHeader, } from './EnglishCertificationHeader';
7
+ import { EnglishCertificationNavigationPanel } from './EnglishCertificationNavigationPanel';
8
+ import { EnglishCertificationFooterBar, } from './EnglishCertificationFooterBar';
9
+ import { ENGLISH_CERT_APP_BACKGROUND, ENGLISH_CERT_PRIMARY_GRADIENT, } from './english-certification-theme';
10
+ /**
11
+ * Ocean Edu branded exam-taking shell for English Certification exams.
12
+ *
13
+ * Layout: branded sticky header · main content + collapsible "Answer Sheet"
14
+ * sidebar · branded wave footer with navigation actions. On mobile the sidebar
15
+ * is replaced by a bottom-sheet opened via a floating button.
16
+ */
17
+ export function EnglishCertificationExamLayout({ children, headerProps, navigationProps, footerProps, }) {
18
+ const [isSidebarOpen, setIsSidebarOpen] = useState(true);
19
+ const [isMobileSheetOpen, setIsMobileSheetOpen] = useState(false);
20
+ // Close the mobile Answer Sheet after the student picks a question/part so
21
+ // the chosen card is visible behind the sheet.
22
+ const panelProps = {
23
+ ...navigationProps,
24
+ onQuestionClick: (globalIndex) => {
25
+ navigationProps.onQuestionClick(globalIndex);
26
+ setIsMobileSheetOpen(false);
27
+ },
28
+ onPartClick: (partIndex) => {
29
+ navigationProps.onPartClick(partIndex);
30
+ setIsMobileSheetOpen(false);
31
+ },
32
+ };
33
+ return (_jsxs("div", { className: "flex h-screen flex-col overflow-hidden text-[#0f1730]", style: { background: ENGLISH_CERT_APP_BACKGROUND }, children: [_jsx(EnglishCertificationHeader, { ...headerProps }), _jsxs("div", { className: "flex flex-1 gap-4 overflow-hidden px-4 py-4 lg:px-6", children: [_jsx("main", { id: "exam-main-scroll-area", className: "min-w-0 flex-1 overflow-y-auto", children: children }), isSidebarOpen && (_jsxs("aside", { className: "hidden w-[360px] shrink-0 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 lg:flex", "aria-label": "Answer sheet navigation", children: [_jsxs("div", { className: "flex h-14 items-center justify-between px-5 text-white", style: { background: ENGLISH_CERT_PRIMARY_GRADIENT }, children: [_jsx("h3", { className: "text-[18px] font-black tracking-[0.1em]", children: "ANSWER SHEET" }), _jsx("button", { type: "button", onClick: () => setIsSidebarOpen(false), className: "text-sm font-bold text-white/90 hover:text-white", children: "Hide \u226A" })] }), _jsx("div", { className: "flex-1 overflow-hidden", children: _jsx(EnglishCertificationNavigationPanel, { ...panelProps }) })] })), !isSidebarOpen && (_jsx("button", { type: "button", onClick: () => setIsSidebarOpen(true), className: "fixed right-0 top-24 z-30 hidden rounded-l-lg border border-r-0 border-[#d9e4f2] bg-white px-2 py-3 text-sm font-bold text-[#0b57d0] shadow-sm hover:bg-blue-50 lg:block", "aria-label": "Show answer sheet", children: "\u226B" }))] }), footerProps && _jsx(EnglishCertificationFooterBar, { ...footerProps }), _jsx("button", { type: "button", onClick: () => setIsMobileSheetOpen(true), className: "fixed bottom-24 right-4 z-30 flex h-12 w-12 items-center justify-center rounded-full text-white shadow-lg lg:hidden", style: { background: ENGLISH_CERT_PRIMARY_GRADIENT }, "aria-label": "Open answer sheet", children: _jsx(List, { className: "h-5 w-5" }) }), _jsx(Sheet, { open: isMobileSheetOpen, onOpenChange: setIsMobileSheetOpen, children: _jsxs(SheetContent, { side: "bottom", className: "h-[78vh] p-0", children: [_jsx(SheetHeader, { className: "flex h-14 flex-row items-center px-5 text-white", style: { background: ENGLISH_CERT_PRIMARY_GRADIENT }, children: _jsx(SheetTitle, { className: "text-[18px] font-black tracking-[0.1em] text-white", children: "ANSWER SHEET" }) }), _jsx("div", { className: "h-[calc(78vh-3.5rem)] overflow-hidden", children: _jsx(EnglishCertificationNavigationPanel, { ...panelProps }) })] }) })] }));
34
+ }
@@ -1 +1,15 @@
1
- "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{cn as t}from"../../../shared/lib/utils";export function EnglishCertificationFlagButton({isFlagged:a,onToggle:s,size:i="default",className:n}){const o="sm"===i;return r("button",{type:"button",onClick:s,className:t("inline-flex items-center gap-1.5 rounded-md border font-bold transition-colors",o?"px-2 py-1 text-xs":"px-3 py-1.5 text-sm",a?"border-[#ff8a00] bg-[#fff1db] text-[#ff8a00]":"border-[#d9e4f2] bg-white text-[#64748b] hover:text-[#ff8a00]",n),"aria-label":a?"Unmark for review":"Mark for review","aria-pressed":a,children:[e("span",{className:"h-[14px] w-2 shrink-0 bg-current",style:{clipPath:"polygon(0 0, 100% 0, 100% 100%, 50% 70%, 0 100%)"},"aria-hidden":"true"}),!o&&e("span",{children:a?"Marked":"Mark"})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { cn } from '../../../shared/lib/utils';
4
+ /**
5
+ * Shared Ocean Edu "Mark for Review" control.
6
+ *
7
+ * Single source of truth for the mark/flag affordance so the bookmark flag
8
+ * looks identical on single-question and grouped-reading screens.
9
+ */
10
+ export function EnglishCertificationFlagButton({ isFlagged, onToggle, size = 'default', className, }) {
11
+ const isCompact = size === 'sm';
12
+ return (_jsxs("button", { type: "button", onClick: onToggle, className: cn('inline-flex items-center gap-1.5 rounded-md border font-bold transition-colors', isCompact ? 'px-2 py-1 text-xs' : 'px-3 py-1.5 text-sm', isFlagged
13
+ ? 'border-[#ff8a00] bg-[#fff1db] text-[#ff8a00]'
14
+ : 'border-[#d9e4f2] bg-white text-[#64748b] hover:text-[#ff8a00]', className), "aria-label": isFlagged ? 'Unmark for review' : 'Mark for review', "aria-pressed": isFlagged, children: [_jsx("span", { className: "h-[14px] w-2 shrink-0 bg-current", style: { clipPath: 'polygon(0 0, 100% 0, 100% 100%, 50% 70%, 0 100%)' }, "aria-hidden": "true" }), !isCompact && _jsx("span", { children: isFlagged ? 'Marked' : 'Mark' })] }));
15
+ }
@@ -1 +1,19 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{cn as r}from"../../../shared/lib/utils";import{ENGLISH_CERT_FOOTER_NOTE as a,ENGLISH_CERT_FOOTER_WAVE as o,ENGLISH_CERT_PRIMARY_GRADIENT as l}from"./english-certification-theme";export function EnglishCertificationFooterBar({onPrevious:d,onClear:i,onToggleMark:s,onNext:n,isMarked:c=!1,isPreviousDisabled:x=!1,isNextDisabled:p=!1,showSecondaryActions:b=!0}){return t("footer",{className:"sticky bottom-0 z-40 flex flex-col gap-2 px-4 pt-3 lg:px-7",style:{background:`linear-gradient(180deg, rgba(255,255,255,0.94) 0 calc(100% - 64px), transparent calc(100% - 64px)), ${o} bottom/100% 64px no-repeat`},children:[t("div",{className:r("grid grid-cols-2 items-center gap-3 lg:gap-12",b?"lg:grid-cols-[240px_190px_1fr_200px]":"mx-auto w-full max-w-[520px] lg:grid-cols-2"),children:[e("button",{type:"button",onClick:d,disabled:x,className:r("h-[42px] rounded-md border border-[#d9e4f2] bg-white text-[15px] font-black text-[#0a3d9a] shadow-[0_5px_16px_rgba(13,71,161,0.08)] disabled:cursor-not-allowed disabled:opacity-50",!b&&"lg:w-[200px] lg:justify-self-start"),children:"←   Previous"}),b&&i&&e("button",{type:"button",onClick:i,className:"h-[42px] rounded-md border border-[#d9e4f2] bg-white text-[15px] font-black text-[#0a3d9a] shadow-[0_5px_16px_rgba(13,71,161,0.08)]",children:"◆   Clear Answer"}),b&&s&&t("button",{type:"button",onClick:s,className:r("h-[42px] rounded-md border bg-white text-[15px] font-black shadow-[0_5px_16px_rgba(13,71,161,0.08)] lg:w-[220px] lg:justify-self-center",c?"border-[#ff8a00] text-[#ff8a00]":"border-[#d9e4f2] text-[#0a3d9a]"),children:["▮   ",c?"Unmark Review":"Mark for Review"]}),e("button",{type:"button",onClick:n,disabled:p,className:r("h-[42px] rounded-md border border-[#0b57d0] text-[15px] font-black text-white shadow-[0_5px_16px_rgba(13,71,161,0.08)] disabled:cursor-not-allowed disabled:opacity-50",!b&&"lg:w-[200px] lg:justify-self-end"),style:{background:l},children:"Next   →"})]}),t("div",{className:"pointer-events-none hidden h-9 items-center gap-4 text-white lg:flex",children:[t("div",{className:"flex shrink-0 items-center gap-2 font-black tracking-wide",children:[e("span",{className:"grid h-[25px] w-[25px] place-items-center rounded-full border-2 border-white text-xs",children:"✦"}),"OCEAN EDU"]}),t("span",{className:"min-w-0 flex-1 text-center text-[13px] font-bold",children:["〰   ",a]})]})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { cn } from '../../../shared/lib/utils';
4
+ import { ENGLISH_CERT_FOOTER_NOTE, ENGLISH_CERT_FOOTER_WAVE, ENGLISH_CERT_PRIMARY_GRADIENT, } from './english-certification-theme';
5
+ /**
6
+ * Branded Ocean Edu footer action bar for English Certification exam taking.
7
+ *
8
+ * Provides Previous / Clear Answer / Mark for Review / Next actions plus the
9
+ * decorative wave, brand lockup and encouragement note.
10
+ */
11
+ export function EnglishCertificationFooterBar({ onPrevious, onClear, onToggleMark, onNext, isMarked = false, isPreviousDisabled = false, isNextDisabled = false, showSecondaryActions = true, }) {
12
+ return (_jsxs("footer", { className: "sticky bottom-0 z-40 flex flex-col gap-2 px-4 pt-3 lg:px-7", style: {
13
+ background: `linear-gradient(180deg, rgba(255,255,255,0.94) 0 calc(100% - 64px), transparent calc(100% - 64px)), ${ENGLISH_CERT_FOOTER_WAVE} bottom/100% 64px no-repeat`,
14
+ }, children: [_jsxs("div", { className: cn('grid grid-cols-2 items-center gap-3 lg:gap-12', showSecondaryActions
15
+ ? 'lg:grid-cols-[240px_190px_1fr_200px]'
16
+ : 'mx-auto w-full max-w-[520px] lg:grid-cols-2'), children: [_jsx("button", { type: "button", onClick: onPrevious, disabled: isPreviousDisabled, className: cn('h-[42px] rounded-md border border-[#d9e4f2] bg-white text-[15px] font-black text-[#0a3d9a] shadow-[0_5px_16px_rgba(13,71,161,0.08)] disabled:cursor-not-allowed disabled:opacity-50', !showSecondaryActions && 'lg:w-[200px] lg:justify-self-start'), children: "\u2190 \u00A0 Previous" }), showSecondaryActions && onClear && (_jsx("button", { type: "button", onClick: onClear, className: "h-[42px] rounded-md border border-[#d9e4f2] bg-white text-[15px] font-black text-[#0a3d9a] shadow-[0_5px_16px_rgba(13,71,161,0.08)]", children: "\u25C6 \u00A0 Clear Answer" })), showSecondaryActions && onToggleMark && (_jsxs("button", { type: "button", onClick: onToggleMark, className: cn('h-[42px] rounded-md border bg-white text-[15px] font-black shadow-[0_5px_16px_rgba(13,71,161,0.08)] lg:w-[220px] lg:justify-self-center', isMarked
17
+ ? 'border-[#ff8a00] text-[#ff8a00]'
18
+ : 'border-[#d9e4f2] text-[#0a3d9a]'), children: ["\u25AE \u00A0 ", isMarked ? 'Unmark Review' : 'Mark for Review'] })), _jsx("button", { type: "button", onClick: onNext, disabled: isNextDisabled, className: cn('h-[42px] rounded-md border border-[#0b57d0] text-[15px] font-black text-white shadow-[0_5px_16px_rgba(13,71,161,0.08)] disabled:cursor-not-allowed disabled:opacity-50', !showSecondaryActions && 'lg:w-[200px] lg:justify-self-end'), style: { background: ENGLISH_CERT_PRIMARY_GRADIENT }, children: "Next \u00A0 \u2192" })] }), _jsxs("div", { className: "pointer-events-none hidden h-9 items-center gap-4 text-white lg:flex", children: [_jsxs("div", { className: "flex shrink-0 items-center gap-2 font-black tracking-wide", children: [_jsx("span", { className: "grid h-[25px] w-[25px] place-items-center rounded-full border-2 border-white text-xs", children: "\u2726" }), "OCEAN EDU"] }), _jsxs("span", { className: "min-w-0 flex-1 text-center text-[13px] font-bold", children: ["\u3030 \u00A0 ", ENGLISH_CERT_FOOTER_NOTE] })] })] }));
19
+ }