@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,183 @@
1
- import{ResultTemplateType as t}from"../../../api/result-review";export const SPEAKING_ANSWER_COMPLETED="completed";export const SPEAKING_STORY_IMAGES_TYPE="LISTEN_AND_SPEAK_WITH_STORY_IMAGES";function e(t){if(null==t)return null;if("string"==typeof t)return t||null;if("object"==typeof t&&null!==t){const e=t;if("string"==typeof e.answer)return e.answer||null;if("string"==typeof e.audioKey)return e.audioKey||null;if("string"==typeof e.objectId)return"completed"}return null}function n(t){if(!t||"object"!=typeof t)return"";const e=t;return"string"==typeof e.audioUrl?e.audioUrl:"string"==typeof e.audio?e.audio:"string"==typeof e.questionAudio?e.questionAudio:"string"==typeof e.interlocutorAudioUrl?e.interlocutorAudioUrl:""}function o(t){if(!t||"object"!=typeof t)return"";const e=t;return"string"==typeof e.partnerAudioUrl?e.partnerAudioUrl:""}function r(t){const e=null!=t.sectionId?`_${t.sectionId}`:"";return`${t.partId??"unknown"}_${t.questionType}${e}`}function s(t,e,n){if(n<=1)return`Part ${t}`;return`Part ${t}${String.fromCharCode(97+e)}`}export function transformSpeakingResultForReview(t,i,u){const{answers:a,examName:c,submittedAt:l,totalScore:p,maxScore:d,isPendingGrading:f}=t,m=Boolean(f),g=new Map;for(const t of a){const e=r(t),n=g.get(e);n?n.push(t):g.set(e,[t])}const A=new Map,S=new Map;for(const[,t]of g){const e=t[0]?.partNo??0;S.set(e,(S.get(e)??0)+1)}const q=[];for(const[,t]of g){t.sort((t,e)=>t.questionNumber-e.questionNumber);const r=t[0];if(!r)continue;const i=r.partId??"unknown",a=r.partNo??0,c=r.questionType,l=r.sectionId,p=A.get(a)??0;A.set(a,p+1);const d=u?.find(t=>String(t.id)===String(i));let f,g=d?.instructions??"";if(d?.sections&&null!=l){const t=d.sections.find(t=>t.sectionId===l);if(t?.name)f=t.name;else{f=s(a,p,S.get(a)??1)}t?.instructions&&(g=t.instructions)}else{f=s(a,p,S.get(a)??1)}const N=`Part ${a}`,y=t.map((t,r)=>{const s=(i=t.studentAnswer)&&"object"==typeof i?i:null;var i;return{questionId:t.questionId,answerId:t.id??t.questionId,questionNumber:r+1,audioUrl:n(t.questionContent),studentAnswerKey:e(t.studentAnswer),isExample:t.isExample??!1,partnerAudioUrl:o(t.questionContent),score:t.score,maxScore:t.maxScore,isPendingGrading:m,isCorrect:t.isCorrect,questionType:t.questionType,questionContent:t.questionContent,studentAnswer:t.studentAnswer,correctAnswer:t.correctAnswer,questionGroup:t.questionGroup??null,aiAssessment:s?.aiAssessment,llmAssessment:s?.llmAssessment,classification:s?.classification,gradingStatus:t.gradingStatus??null}});let I=r.questionGroup?.payload??null;if(c===SPEAKING_STORY_IMAGES_TYPE&&!I){const t=r.questionContent;t?.images&&(I={images:t.images})}q.push({partId:i,partNo:a,partName:N,partSectionName:f,instructions:g,questionType:c,questions:y,groupPayload:I})}return q.sort((t,e)=>t.partNo!==e.partNo?t.partNo-e.partNo:(t.questions[0]?.questionNumber??0)-(e.questions[0]?.questionNumber??0)),{parts:q,examName:c??"",examSubTitle:t.roomName??"",studentName:i,submittedAt:String(l??""),totalScore:m?null:p,maxScore:d,isPendingGrading:m}}export function isSpeakingTemplateType(e){return e===t.SPEAKING}
1
+ /**
2
+ * Transform DetailedResult API response to SpeakingReviewData format.
3
+ * Source: oe-exam-fe/.../speaking-review/_utils/transform-speaking-review.ts
4
+ */
5
+ import { ResultTemplateType, } from '../../../api/result-review';
6
+ export const SPEAKING_ANSWER_COMPLETED = 'completed';
7
+ export const SPEAKING_STORY_IMAGES_TYPE = 'LISTEN_AND_SPEAK_WITH_STORY_IMAGES';
8
+ function extractStudentAnswerKey(studentAnswer) {
9
+ if (studentAnswer === null || studentAnswer === undefined) {
10
+ return null;
11
+ }
12
+ if (typeof studentAnswer === 'string') {
13
+ return studentAnswer || null;
14
+ }
15
+ if (typeof studentAnswer === 'object' && studentAnswer !== null) {
16
+ const obj = studentAnswer;
17
+ if (typeof obj['answer'] === 'string') {
18
+ return obj['answer'] || null;
19
+ }
20
+ if (typeof obj['audioKey'] === 'string') {
21
+ return obj['audioKey'] || null;
22
+ }
23
+ if (typeof obj['objectId'] === 'string')
24
+ return SPEAKING_ANSWER_COMPLETED;
25
+ }
26
+ return null;
27
+ }
28
+ function extractQuestionAudioUrl(questionContent) {
29
+ if (!questionContent || typeof questionContent !== 'object')
30
+ return '';
31
+ const content = questionContent;
32
+ if (typeof content['audioUrl'] === 'string')
33
+ return content['audioUrl'];
34
+ if (typeof content['audio'] === 'string')
35
+ return content['audio'];
36
+ if (typeof content['questionAudio'] === 'string')
37
+ return content['questionAudio'];
38
+ if (typeof content['interlocutorAudioUrl'] === 'string')
39
+ return content['interlocutorAudioUrl'];
40
+ return '';
41
+ }
42
+ function extractPartnerAudioUrl(questionContent) {
43
+ if (!questionContent || typeof questionContent !== 'object')
44
+ return '';
45
+ const content = questionContent;
46
+ if (typeof content['partnerAudioUrl'] === 'string')
47
+ return content['partnerAudioUrl'];
48
+ return '';
49
+ }
50
+ function buildSectionKey(answer) {
51
+ const sectionSuffix = answer.sectionId != null ? `_${answer.sectionId}` : '';
52
+ return `${answer.partId ?? 'unknown'}_${answer.questionType}${sectionSuffix}`;
53
+ }
54
+ function deriveSectionName(partNo, sectionIdx, totalSections) {
55
+ if (totalSections <= 1) {
56
+ return `Part ${partNo}`;
57
+ }
58
+ const suffix = String.fromCharCode(97 + sectionIdx);
59
+ return `Part ${partNo}${suffix}`;
60
+ }
61
+ function asAnswerRecord(value) {
62
+ if (!value || typeof value !== 'object')
63
+ return null;
64
+ return value;
65
+ }
66
+ /**
67
+ * Main transformer: convert ResultReviewDetail to SpeakingReviewData.
68
+ */
69
+ export function transformSpeakingResultForReview(result, studentName, templateParts) {
70
+ const { answers, examName, submittedAt, totalScore, maxScore, isPendingGrading } = result;
71
+ const pending = Boolean(isPendingGrading);
72
+ const sectionMap = new Map();
73
+ for (const answer of answers) {
74
+ const key = buildSectionKey(answer);
75
+ const bucket = sectionMap.get(key);
76
+ if (bucket) {
77
+ bucket.push(answer);
78
+ }
79
+ else {
80
+ sectionMap.set(key, [answer]);
81
+ }
82
+ }
83
+ const partSectionCounters = new Map();
84
+ const partSectionTotals = new Map();
85
+ for (const [, sectionAnswers] of sectionMap) {
86
+ const partNo = sectionAnswers[0]?.partNo ?? 0;
87
+ partSectionTotals.set(partNo, (partSectionTotals.get(partNo) ?? 0) + 1);
88
+ }
89
+ const parts = [];
90
+ for (const [, sectionAnswers] of sectionMap) {
91
+ sectionAnswers.sort((a, b) => a.questionNumber - b.questionNumber);
92
+ const firstAnswer = sectionAnswers[0];
93
+ if (!firstAnswer)
94
+ continue;
95
+ const partId = firstAnswer.partId ?? 'unknown';
96
+ const partNo = firstAnswer.partNo ?? 0;
97
+ const questionType = firstAnswer.questionType;
98
+ const sectionId = firstAnswer.sectionId;
99
+ const sectionIdx = partSectionCounters.get(partNo) ?? 0;
100
+ partSectionCounters.set(partNo, sectionIdx + 1);
101
+ const templatePart = templateParts?.find((tp) => String(tp.id) === String(partId));
102
+ let instructions = templatePart?.instructions ?? '';
103
+ let partSectionName;
104
+ if (templatePart?.sections && sectionId != null) {
105
+ const templateSection = templatePart.sections.find((section) => section.sectionId === sectionId);
106
+ if (templateSection?.name) {
107
+ partSectionName = templateSection.name;
108
+ }
109
+ else {
110
+ const totalSections = partSectionTotals.get(partNo) ?? 1;
111
+ partSectionName = deriveSectionName(partNo, sectionIdx, totalSections);
112
+ }
113
+ if (templateSection?.instructions) {
114
+ instructions = templateSection.instructions;
115
+ }
116
+ }
117
+ else {
118
+ const totalSections = partSectionTotals.get(partNo) ?? 1;
119
+ partSectionName = deriveSectionName(partNo, sectionIdx, totalSections);
120
+ }
121
+ const partName = `Part ${partNo}`;
122
+ const questions = sectionAnswers.map((answer, idx) => {
123
+ const studentAnswerRecord = asAnswerRecord(answer.studentAnswer);
124
+ return {
125
+ questionId: answer.questionId,
126
+ answerId: answer.id ?? answer.questionId,
127
+ questionNumber: idx + 1,
128
+ audioUrl: extractQuestionAudioUrl(answer.questionContent),
129
+ studentAnswerKey: extractStudentAnswerKey(answer.studentAnswer),
130
+ isExample: answer.isExample ?? false,
131
+ partnerAudioUrl: extractPartnerAudioUrl(answer.questionContent),
132
+ score: answer.score,
133
+ maxScore: answer.maxScore,
134
+ isPendingGrading: pending,
135
+ isCorrect: answer.isCorrect,
136
+ questionType: answer.questionType,
137
+ questionContent: answer.questionContent,
138
+ studentAnswer: answer.studentAnswer,
139
+ correctAnswer: answer.correctAnswer,
140
+ questionGroup: answer.questionGroup ?? null,
141
+ aiAssessment: studentAnswerRecord?.['aiAssessment'],
142
+ llmAssessment: studentAnswerRecord?.['llmAssessment'],
143
+ classification: studentAnswerRecord?.['classification'],
144
+ gradingStatus: answer.gradingStatus ?? null,
145
+ };
146
+ });
147
+ let groupPayload = firstAnswer.questionGroup?.payload ?? null;
148
+ if (questionType === SPEAKING_STORY_IMAGES_TYPE && !groupPayload) {
149
+ const content = firstAnswer.questionContent;
150
+ if (content?.['images']) {
151
+ groupPayload = { images: content['images'] };
152
+ }
153
+ }
154
+ parts.push({
155
+ partId,
156
+ partNo,
157
+ partName,
158
+ partSectionName,
159
+ instructions,
160
+ questionType,
161
+ questions,
162
+ groupPayload,
163
+ });
164
+ }
165
+ parts.sort((a, b) => {
166
+ if (a.partNo !== b.partNo)
167
+ return a.partNo - b.partNo;
168
+ return (a.questions[0]?.questionNumber ?? 0) - (b.questions[0]?.questionNumber ?? 0);
169
+ });
170
+ return {
171
+ parts,
172
+ examName: examName ?? '',
173
+ examSubTitle: result.roomName ?? '',
174
+ studentName,
175
+ submittedAt: String(submittedAt ?? ''),
176
+ totalScore: pending ? null : totalScore,
177
+ maxScore,
178
+ isPendingGrading: pending,
179
+ };
180
+ }
181
+ export function isSpeakingTemplateType(type) {
182
+ return type === ResultTemplateType.SPEAKING;
183
+ }
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ export interface RichTextEditorProps {
3
+ value?: string;
4
+ onChange: (html: string) => void;
5
+ label?: React.ReactNode;
6
+ disabled?: boolean;
7
+ minHeightClassName?: string;
8
+ }
9
+ export declare function RichTextEditor({ value, onChange, label, disabled, minHeightClassName, }: RichTextEditorProps): React.JSX.Element;
@@ -0,0 +1,47 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useEffect, useState } from 'react';
4
+ import { useEditor, EditorContent } from '@tiptap/react';
5
+ import StarterKit from '@tiptap/starter-kit';
6
+ const EMPTY_CONTENT = '<p></p>';
7
+ const HEADING_LEVELS = [1, 2, 3];
8
+ function getToolbarButtonClass(isActive) {
9
+ return `rounded px-3 py-1 text-sm ${isActive ? 'bg-blue-500 text-white' : 'border bg-white text-gray-700 hover:bg-gray-100'}`;
10
+ }
11
+ export function RichTextEditor({ value, onChange, label, disabled = false, minHeightClassName = 'min-h-[250px]', }) {
12
+ const [mounted, setMounted] = useState(false);
13
+ const editor = useEditor({
14
+ immediatelyRender: false,
15
+ extensions: [StarterKit],
16
+ editable: !disabled,
17
+ content: value || EMPTY_CONTENT,
18
+ onUpdate: ({ editor: currentEditor }) => {
19
+ onChange(currentEditor.getHTML());
20
+ },
21
+ editorProps: {
22
+ attributes: {
23
+ class: `tiptap-editor focus:outline-none ${minHeightClassName} p-4 bg-white border border-gray-200 rounded-b-md prose prose-sm max-w-none`,
24
+ },
25
+ },
26
+ });
27
+ useEffect(() => {
28
+ setMounted(true);
29
+ }, []);
30
+ useEffect(() => {
31
+ editor?.setEditable(!disabled);
32
+ }, [disabled, editor]);
33
+ useEffect(() => {
34
+ const nextValue = value || EMPTY_CONTENT;
35
+ if (editor && nextValue !== editor.getHTML()) {
36
+ setTimeout(() => editor.commands.setContent(nextValue), 0);
37
+ }
38
+ }, [value, editor]);
39
+ if (!mounted) {
40
+ return null;
41
+ }
42
+ return (_jsxs("div", { className: "flex w-full flex-col gap-4", children: [label ? _jsx("div", { children: label }) : null, _jsxs("div", { className: "flex flex-col rounded-md border border-gray-300 shadow-sm", children: [_jsxs("div", { className: "relative flex flex-wrap gap-2 rounded-t-md border-b border-gray-300 bg-gray-50 p-2", children: [_jsx("button", { type: "button", onClick: () => editor?.chain().focus().toggleBold().run(), disabled: disabled || !editor?.can().chain().focus().toggleBold().run(), className: `${getToolbarButtonClass(!!editor?.isActive('bold'))} font-bold`, children: "B" }), _jsx("button", { type: "button", onClick: () => editor?.chain().focus().toggleItalic().run(), disabled: disabled || !editor?.can().chain().focus().toggleItalic().run(), className: `${getToolbarButtonClass(!!editor?.isActive('italic'))} italic`, children: "I" }), _jsx("div", { className: "mx-1 h-6 w-px self-center bg-gray-300" }), HEADING_LEVELS.map((level) => (_jsxs("button", { type: "button", onClick: () => editor
43
+ ?.chain()
44
+ .focus()
45
+ .toggleHeading({ level })
46
+ .run(), disabled: disabled, className: `${getToolbarButtonClass(!!editor?.isActive('heading', { level }))} font-bold`, children: ["H", level] }, level))), _jsx("div", { className: "mx-1 h-6 w-px self-center bg-gray-300" }), _jsx("button", { type: "button", onClick: () => editor?.chain().focus().toggleBulletList().run(), disabled: disabled, className: getToolbarButtonClass(!!editor?.isActive('bulletList')), children: "\u2022 List" }), _jsx("button", { type: "button", onClick: () => editor?.chain().focus().toggleOrderedList().run(), disabled: disabled, className: getToolbarButtonClass(!!editor?.isActive('orderedList')), children: "1. List" })] }), _jsx(EditorContent, { editor: editor })] })] }));
47
+ }
@@ -1 +1,100 @@
1
- "use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import{useRef as t,useState as n,useEffect as r}from"react";import{Pause as i,Play as o}from"lucide-react";import{usePresignedFileUrl as d}from"../../../shared/lib/hooks/usePresignedFileUrl";import{useDelayedAutoPlay as l}from"../../../shared/lib/hooks/useDelayedAutoPlay";import s from"./SpeakingAudioPlayer.module.css";const u=[.4,.6,.85,.55,.75,1,.65,.45,.7,.9,.6,.5,.8,.65,.95,.7,.5,.6,.8,.45];export function SpeakingAudioPlayer({audioUrl:c,onPlayingChange:m,autoPlay:p=!1}){const v=t(null),[h,y]=n(!1),[g,f]=n(0),[L,E]=n(0),{previewUrl:N}=d(c||void 0);r(()=>{const e=v.current;if(!e)return;const a=()=>{e.duration&&!isNaN(e.duration)&&isFinite(e.duration)&&E(e.duration)},t=()=>{f(e.currentTime),a()},n=()=>{y(!0),m?.(!0)},r=()=>{y(!1),m?.(!1)},i=()=>{y(!1),f(0),m?.(!1)};return a(),e.addEventListener("loadedmetadata",a),e.addEventListener("durationchange",a),e.addEventListener("canplay",a),e.addEventListener("timeupdate",t),e.addEventListener("play",n),e.addEventListener("pause",r),e.addEventListener("ended",i),()=>{e.removeEventListener("loadedmetadata",a),e.removeEventListener("durationchange",a),e.removeEventListener("canplay",a),e.removeEventListener("timeupdate",t),e.removeEventListener("play",n),e.removeEventListener("pause",r),e.removeEventListener("ended",i)}},[N]),r(()=>{y(!1),f(0),E(0)},[N]),l({audioRef:v,audioUrl:N,enabled:p});const b=L>0?g/L:0,P=!N;return a("div",{className:s.audioPill,children:[e("audio",{ref:v,...N?{src:N}:{},preload:"metadata"}),e("button",{type:"button",className:`${s.playButton} ${h?s.playButtonPlaying:""} ${P?s.playButtonDisabled:""}`,onClick:P?void 0:()=>{const e=v.current;e&&(h?e.pause():e.play().catch(e=>{console.warn("Audio play request was interrupted:",e)}))},disabled:P,"aria-label":P?"Không có âm thanh đính kèm":h?"Tạm dừng":"Nghe câu hỏi",title:P?"Không có âm thanh đính kèm":h?"Tạm dừng":"Nghe câu hỏi",children:h?e(i,{className:s.playIcon,fill:"currentColor"}):e(o,{className:s.playIcon,fill:"currentColor",style:{marginLeft:"2px"}})}),e("div",{className:s.waveform,"aria-hidden":"true",children:u.map((a,t)=>{const n=(t+.5)/u.length<=b;return e("span",{className:`${s.waveBar} ${h&&n?s.waveBarPlaying:""}`,style:{height:100*a+"%",background:n?"linear-gradient(180deg, #ffc93c, #ff9900)":"#d8e0ea",animationDelay:h&&n?t%6*.1+"s":void 0}},t)})})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * SpeakingAudioPlayer
5
+ *
6
+ * A small, simple, kid-friendly "listen to the question" audio player for light
7
+ * backgrounds (Summer Sky theme). Renders a compact pill: a sky-blue play/replay
8
+ * button + a sunny-amber waveform that fills left→right with playback progress
9
+ * (display-only — no fiddly seek, no time digits). Optionally auto-plays a few
10
+ * seconds after a new question loads. Used by speaking renderers (e.g. describe-image).
11
+ */
12
+ import { useRef, useState, useEffect } from 'react';
13
+ import { Pause, Play } from 'lucide-react';
14
+ import { usePresignedFileUrl } from '../../../shared/lib/hooks/usePresignedFileUrl';
15
+ import { useDelayedAutoPlay } from '../../../shared/lib/hooks/useDelayedAutoPlay';
16
+ import styles from './SpeakingAudioPlayer.module.css';
17
+ // Fixed waveform shape (bar heights, 0–1). Bars span the full width; played
18
+ // bars are tinted amber, the rest stay grey.
19
+ const WAVEFORM = [
20
+ 0.4, 0.6, 0.85, 0.55, 0.75, 1, 0.65, 0.45, 0.7, 0.9,
21
+ 0.6, 0.5, 0.8, 0.65, 0.95, 0.7, 0.5, 0.6, 0.8, 0.45,
22
+ ];
23
+ export function SpeakingAudioPlayer({ audioUrl, onPlayingChange, autoPlay = false }) {
24
+ const audioRef = useRef(null);
25
+ const [isPlaying, setIsPlaying] = useState(false);
26
+ const [currentTime, setCurrentTime] = useState(0);
27
+ const [duration, setDuration] = useState(0);
28
+ const { previewUrl: resolvedAudioUrl } = usePresignedFileUrl(audioUrl || undefined);
29
+ useEffect(() => {
30
+ const audio = audioRef.current;
31
+ if (!audio)
32
+ return;
33
+ const updateDuration = () => {
34
+ if (audio.duration && !isNaN(audio.duration) && isFinite(audio.duration)) {
35
+ setDuration(audio.duration);
36
+ }
37
+ };
38
+ const handleTimeUpdate = () => {
39
+ setCurrentTime(audio.currentTime);
40
+ updateDuration();
41
+ };
42
+ // Drive playing state from the element itself so both manual clicks and
43
+ // delayed auto-play stay in sync with the UI.
44
+ const handlePlay = () => { setIsPlaying(true); onPlayingChange?.(true); };
45
+ const handlePause = () => { setIsPlaying(false); onPlayingChange?.(false); };
46
+ const handleEnded = () => {
47
+ setIsPlaying(false);
48
+ setCurrentTime(0);
49
+ onPlayingChange?.(false);
50
+ };
51
+ updateDuration();
52
+ audio.addEventListener('loadedmetadata', updateDuration);
53
+ audio.addEventListener('durationchange', updateDuration);
54
+ audio.addEventListener('canplay', updateDuration);
55
+ audio.addEventListener('timeupdate', handleTimeUpdate);
56
+ audio.addEventListener('play', handlePlay);
57
+ audio.addEventListener('pause', handlePause);
58
+ audio.addEventListener('ended', handleEnded);
59
+ return () => {
60
+ audio.removeEventListener('loadedmetadata', updateDuration);
61
+ audio.removeEventListener('durationchange', updateDuration);
62
+ audio.removeEventListener('canplay', updateDuration);
63
+ audio.removeEventListener('timeupdate', handleTimeUpdate);
64
+ audio.removeEventListener('play', handlePlay);
65
+ audio.removeEventListener('pause', handlePause);
66
+ audio.removeEventListener('ended', handleEnded);
67
+ };
68
+ }, [resolvedAudioUrl]);
69
+ useEffect(() => {
70
+ setIsPlaying(false);
71
+ setCurrentTime(0);
72
+ setDuration(0);
73
+ }, [resolvedAudioUrl]);
74
+ // Auto-play (default 3s delay) when a new question audio loads, if enabled.
75
+ // The hook retries on first interaction if the browser blocks autoplay.
76
+ useDelayedAutoPlay({ audioRef, audioUrl: resolvedAudioUrl, enabled: autoPlay });
77
+ const handlePlayPause = () => {
78
+ const audio = audioRef.current;
79
+ if (!audio)
80
+ return;
81
+ if (isPlaying) {
82
+ audio.pause();
83
+ }
84
+ else {
85
+ audio.play().catch((err) => {
86
+ console.warn('Audio play request was interrupted:', err);
87
+ });
88
+ }
89
+ };
90
+ const progressRatio = duration > 0 ? currentTime / duration : 0;
91
+ const isDisabled = !resolvedAudioUrl;
92
+ return (_jsxs("div", { className: styles.audioPill, children: [_jsx("audio", { ref: audioRef, ...(resolvedAudioUrl ? { src: resolvedAudioUrl } : {}), preload: "metadata" }), _jsx("button", { type: "button", className: `${styles.playButton} ${isPlaying ? styles.playButtonPlaying : ''} ${isDisabled ? styles.playButtonDisabled : ''}`, onClick: isDisabled ? undefined : handlePlayPause, disabled: isDisabled, "aria-label": isDisabled ? 'Không có âm thanh đính kèm' : isPlaying ? 'Tạm dừng' : 'Nghe câu hỏi', title: isDisabled ? 'Không có âm thanh đính kèm' : isPlaying ? 'Tạm dừng' : 'Nghe câu hỏi', children: isPlaying ? (_jsx(Pause, { className: styles.playIcon, fill: "currentColor" })) : (_jsx(Play, { className: styles.playIcon, fill: "currentColor", style: { marginLeft: '2px' } })) }), _jsx("div", { className: styles.waveform, "aria-hidden": "true", children: WAVEFORM.map((h, i) => {
93
+ const played = (i + 0.5) / WAVEFORM.length <= progressRatio;
94
+ return (_jsx("span", { className: `${styles.waveBar} ${isPlaying && played ? styles.waveBarPlaying : ''}`, style: {
95
+ height: `${h * 100}%`,
96
+ background: played ? 'linear-gradient(180deg, #ffc93c, #ff9900)' : '#d8e0ea',
97
+ animationDelay: isPlaying && played ? `${(i % 6) * 0.1}s` : undefined,
98
+ } }, i));
99
+ }) })] }));
100
+ }
@@ -1 +1,140 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useRef as a,useState as r,useEffect as l,useMemo as n}from"react";import{Pause as s,Play as i,RotateCcw as o}from"lucide-react";import{usePresignedFileUrl as d}from"../../../shared/lib/hooks/usePresignedFileUrl";const c=[.75,1,1.25,1.5];export default function u({audioSrc:u,onPlay:h,onPause:m,compact:b=!1,dense:f=!1}){const{previewUrl:p}=d(u),v=a(null),[x,g]=r(!1),[w,N]=r(0),[y,k]=r(0),[M,L]=r(1),E=n(()=>{const e=[];let t=7;for(let a=0;a<j;a++){t=(9301*t+49297)%233280;const r=.25+t/233280*.75,l=.25*Math.sin(a/(j-1)*Math.PI)+.75;e.push(Math.max(.2,r*l))}return e},[j=48]);var j;l(()=>{const e=v.current;if(!e)return;const t=()=>{e.duration&&!isNaN(e.duration)&&isFinite(e.duration)&&k(e.duration)},a=()=>{N(e.currentTime),t()},r=()=>{g(!1),N(0)};return t(),e.addEventListener("loadedmetadata",t),e.addEventListener("durationchange",t),e.addEventListener("canplay",t),e.addEventListener("timeupdate",a),e.addEventListener("ended",r),()=>{e.removeEventListener("loadedmetadata",t),e.removeEventListener("durationchange",t),e.removeEventListener("canplay",t),e.removeEventListener("timeupdate",a),e.removeEventListener("ended",r)}},[]),l(()=>{v.current&&(v.current.playbackRate=M)},[M]);const C=()=>{const e=v.current;e&&(x?(e.pause(),g(!1),m?.()):(e.play().catch(()=>{g(!1)}),g(!0),h?.()))},P=e=>{const t=Math.max(0,e),a=Math.floor(t/60),r=Math.floor(t%60);return`${String(a).padStart(2,"0")}:${String(r).padStart(2,"0")}`},S=y>0?w/y:0,T=100*S,$=Math.max(0,y-w);if(b)return t("div",{className:f?"relative w-full overflow-hidden rounded-2xl border-2 border-blue-200/90 bg-gradient-to-r from-blue-50/90 via-white to-blue-50/90 p-2.5 sm:p-3 shadow-xs transition-all hover:border-blue-400 hover:shadow-sm":"relative w-full overflow-hidden rounded-2xl border-2 border-blue-200/90 bg-gradient-to-r from-blue-50/90 via-white to-blue-50/90 p-3 sm:p-4 shadow-xs transition-all hover:border-blue-400 hover:shadow-sm",children:[e("audio",{ref:v,src:p||void 0,preload:"metadata"}),t("div",{className:"flex items-center gap-3 sm:gap-4",children:[t("button",{type:"button",onClick:C,className:(f?"h-10 w-10 sm:h-11 sm:w-11":"h-12 w-12")+" relative flex shrink-0 items-center justify-center rounded-full bg-gradient-to-tr from-blue-600 to-indigo-600 text-white shadow-md shadow-blue-500/30 transition-all duration-200 hover:scale-105 hover:from-blue-700 hover:to-indigo-700 active:scale-95","aria-label":x?"Pause":"Play",children:[x?e(s,{className:(f?"h-4 w-4 sm:h-5 sm:w-5":"h-5 w-5")+" fill-current"}):e(i,{className:(f?"ml-0.5 h-4 w-4 sm:h-5 sm:w-5":"ml-0.5 h-5 w-5")+" fill-current"}),x&&e("span",{className:"pointer-events-none absolute inset-0 animate-ping rounded-full bg-blue-400 opacity-40"})]}),t("div",{className:"flex min-w-0 flex-1 flex-col justify-center gap-1.5",children:[t("div",{className:"flex items-center justify-between px-0.5",children:[e("span",{className:"text-xs font-black uppercase tracking-widest text-blue-900",children:"AUDIO"}),t("div",{className:"flex items-center gap-1 rounded-md bg-white px-2 py-0.5 text-xs font-mono font-bold text-slate-600 border border-blue-100 shadow-2xs",children:[e("span",{className:"text-blue-600",children:P(w)}),e("span",{className:"text-slate-300",children:"/"}),e("span",{children:P(y)})]})]}),t("div",{className:"relative flex items-center",children:[e("div",{className:"h-2.5 w-full overflow-hidden rounded-full bg-blue-100/80 shadow-inner",children:e("div",{className:"h-full rounded-full bg-gradient-to-r from-blue-500 to-indigo-600 transition-[width] duration-150",style:{width:`${T}%`}})}),e("input",{type:"range",min:"0",max:y||0,step:"0.1",value:w,onChange:e=>{const t=parseFloat(e.target.value);v.current&&(v.current.currentTime=t),N(t)},className:"absolute inset-0 w-full cursor-pointer opacity-0","aria-label":"Seek"})]})]})]})]});const F=56,D=26.5,I=2*Math.PI*D;return t("div",{className:"relative overflow-hidden rounded-2xl border-2 border-blue-100 bg-white p-3.5 shadow-sm transition-shadow hover:border-blue-200 hover:shadow-md",children:[e("audio",{ref:v,src:p||void 0,preload:"metadata"}),e("div",{className:"pointer-events-none absolute -right-6 -top-6 h-20 w-20 rounded-full bg-blue-100 opacity-60 blur-xl"}),t("div",{className:"relative flex items-center gap-3",children:[t("button",{type:"button",onClick:()=>{const e=v.current;e&&(e.currentTime=Math.max(0,e.currentTime-5),N(e.currentTime))},className:"relative flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-slate-500 transition-all hover:bg-slate-100 hover:text-blue-600 active:scale-95","aria-label":"Rewind 5 seconds",title:"Quay lại 5 giây",children:[e(o,{className:"h-4 w-4",strokeWidth:2.5}),e("span",{className:"pointer-events-none absolute bottom-1 right-1 text-[8px] font-black leading-none",children:"5"})]}),t("div",{className:"relative shrink-0",style:{width:F,height:F},children:[t("svg",{width:F,height:F,className:"absolute inset-0 -rotate-90","aria-hidden":!0,children:[e("circle",{cx:28,cy:28,r:D,fill:"none",stroke:"rgb(239 246 255)",strokeWidth:3}),e("circle",{cx:28,cy:28,r:D,fill:"none",stroke:"url(#audio-ring-gradient)",strokeWidth:3,strokeLinecap:"round",strokeDasharray:I,strokeDashoffset:I*(1-S),className:"transition-[stroke-dashoffset] duration-150"}),e("defs",{children:t("linearGradient",{id:"audio-ring-gradient",x1:"0",y1:"0",x2:"1",y2:"1",children:[e("stop",{offset:"0%",stopColor:"#3B82F6"}),e("stop",{offset:"100%",stopColor:"#06b6d4"})]})})]}),t("button",{type:"button",onClick:C,className:"absolute inset-1.5 flex items-center justify-center rounded-full bg-gradient-to-tr from-blue-600 to-indigo-600 text-white shadow-lg shadow-blue-500/30 transition-all hover:shadow-xl hover:shadow-blue-500/50 hover:from-blue-700 hover:to-indigo-700 active:scale-95","aria-label":x?"Pause":"Play",children:[x?e(s,{className:"h-4 w-4 fill-current"}):e(i,{className:"ml-0.5 h-4 w-4 fill-current"}),x&&e("span",{className:"pointer-events-none absolute inset-0 animate-ping rounded-full bg-blue-400 opacity-40"})]})]}),t("div",{className:"flex min-w-0 flex-1 flex-col gap-1",children:[e("div",{onClick:e=>{const t=v.current;if(!t||!y)return;const a=e.currentTarget.getBoundingClientRect(),r=Math.min(1,Math.max(0,(e.clientX-a.left)/a.width))*y;t.currentTime=r,N(r)},className:"group/wave relative flex h-8 cursor-pointer items-center gap-[2px]",role:"slider","aria-label":"Seek","aria-valuemin":0,"aria-valuemax":y||0,"aria-valuenow":w,children:E.map((t,a)=>{const r=(a+.5)/48,l=r<=S,n=Math.abs(r-S)<1/48/2;return e("span",{className:`flex-1 rounded-full transition-all duration-200 ${l?"bg-blue-500":"bg-slate-200 group-hover/wave:bg-slate-300"} ${n&&x?"animate-wave-pulse":""}`,style:{height:100*t+"%"}},a)})}),t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-baseline gap-1.5",children:[t("span",{className:"font-mono text-sm font-bold tabular-nums text-slate-800",children:["−",P($)]}),t("span",{className:"font-mono text-[10px] tabular-nums text-slate-400",children:["/ ",P(y)]})]}),e("div",{className:"flex items-center gap-0.5 rounded-full border border-slate-200 bg-white p-0.5 shadow-sm",children:c.map(e=>t("button",{type:"button",onClick:()=>L(e),className:"rounded-full px-2 py-0.5 font-mono text-[10px] font-semibold tabular-nums transition-all "+(M===e?"bg-coral-500 text-white shadow-sm":"text-slate-500 hover:bg-slate-100 hover:text-slate-700"),children:[e,"×"]},e))})]})]})]})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useRef, useState, useEffect, useMemo } from 'react';
4
+ import { Pause, Play, RotateCcw } from 'lucide-react';
5
+ import { usePresignedFileUrl } from '../../../shared/lib/hooks/usePresignedFileUrl';
6
+ const PLAYBACK_SPEEDS = [0.75, 1, 1.25, 1.5];
7
+ const WAVEFORM_BARS = 48;
8
+ /** Deterministic pseudo-random bar heights — stable per render, no flicker on re-render. */
9
+ function useWaveformHeights(count) {
10
+ return useMemo(() => {
11
+ const heights = [];
12
+ let seed = 7;
13
+ for (let i = 0; i < count; i++) {
14
+ // LCG-like deterministic noise
15
+ seed = (seed * 9301 + 49297) % 233280;
16
+ const rnd = seed / 233280;
17
+ // Bias towards mid-range heights for a natural-looking waveform
18
+ const h = 0.25 + rnd * 0.75;
19
+ // Soft envelope — taller in the middle, shorter at edges
20
+ const envelope = Math.sin((i / (count - 1)) * Math.PI) * 0.25 + 0.75;
21
+ heights.push(Math.max(0.2, h * envelope));
22
+ }
23
+ return heights;
24
+ }, [count]);
25
+ }
26
+ export default function CambridgeYleAudioPlayer({ audioSrc, onPlay, onPause, compact = false, dense = false, }) {
27
+ const { previewUrl: resolvedAudioSrc } = usePresignedFileUrl(audioSrc);
28
+ const audioRef = useRef(null);
29
+ const [isPlaying, setIsPlaying] = useState(false);
30
+ const [currentTime, setCurrentTime] = useState(0);
31
+ const [duration, setDuration] = useState(0);
32
+ const [speed, setSpeed] = useState(1);
33
+ const barHeights = useWaveformHeights(WAVEFORM_BARS);
34
+ useEffect(() => {
35
+ const audio = audioRef.current;
36
+ if (!audio)
37
+ return;
38
+ const updateDuration = () => {
39
+ if (audio.duration && !isNaN(audio.duration) && isFinite(audio.duration)) {
40
+ setDuration(audio.duration);
41
+ }
42
+ };
43
+ const handleTimeUpdate = () => {
44
+ setCurrentTime(audio.currentTime);
45
+ updateDuration();
46
+ };
47
+ const handleEnded = () => {
48
+ setIsPlaying(false);
49
+ setCurrentTime(0);
50
+ };
51
+ updateDuration();
52
+ audio.addEventListener('loadedmetadata', updateDuration);
53
+ audio.addEventListener('durationchange', updateDuration);
54
+ audio.addEventListener('canplay', updateDuration);
55
+ audio.addEventListener('timeupdate', handleTimeUpdate);
56
+ audio.addEventListener('ended', handleEnded);
57
+ return () => {
58
+ audio.removeEventListener('loadedmetadata', updateDuration);
59
+ audio.removeEventListener('durationchange', updateDuration);
60
+ audio.removeEventListener('canplay', updateDuration);
61
+ audio.removeEventListener('timeupdate', handleTimeUpdate);
62
+ audio.removeEventListener('ended', handleEnded);
63
+ };
64
+ }, []);
65
+ useEffect(() => {
66
+ if (audioRef.current)
67
+ audioRef.current.playbackRate = speed;
68
+ }, [speed]);
69
+ const handlePlayPause = () => {
70
+ const audio = audioRef.current;
71
+ if (!audio)
72
+ return;
73
+ if (isPlaying) {
74
+ audio.pause();
75
+ setIsPlaying(false);
76
+ onPause?.();
77
+ }
78
+ else {
79
+ audio.play().catch(() => {
80
+ // Source unplayable — reset UI instead of leaving a floating rejection.
81
+ setIsPlaying(false);
82
+ });
83
+ setIsPlaying(true);
84
+ onPlay?.();
85
+ }
86
+ };
87
+ const handleRewind = () => {
88
+ const audio = audioRef.current;
89
+ if (!audio)
90
+ return;
91
+ audio.currentTime = Math.max(0, audio.currentTime - 5);
92
+ setCurrentTime(audio.currentTime);
93
+ };
94
+ const handleWaveformClick = (e) => {
95
+ const audio = audioRef.current;
96
+ if (!audio || !duration)
97
+ return;
98
+ const rect = e.currentTarget.getBoundingClientRect();
99
+ const ratio = Math.min(1, Math.max(0, (e.clientX - rect.left) / rect.width));
100
+ const newTime = ratio * duration;
101
+ audio.currentTime = newTime;
102
+ setCurrentTime(newTime);
103
+ };
104
+ const formatTime = (time) => {
105
+ const safe = Math.max(0, time);
106
+ const minutes = Math.floor(safe / 60);
107
+ const seconds = Math.floor(safe % 60);
108
+ return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
109
+ };
110
+ const progress = duration > 0 ? currentTime / duration : 0;
111
+ const progressPct = progress * 100;
112
+ const remaining = Math.max(0, duration - currentTime);
113
+ // --------------------- COMPACT MODE ---------------------
114
+ if (compact) {
115
+ return (_jsxs("div", { className: dense
116
+ ? 'relative w-full overflow-hidden rounded-2xl border-2 border-blue-200/90 bg-gradient-to-r from-blue-50/90 via-white to-blue-50/90 p-2.5 sm:p-3 shadow-xs transition-all hover:border-blue-400 hover:shadow-sm'
117
+ : 'relative w-full overflow-hidden rounded-2xl border-2 border-blue-200/90 bg-gradient-to-r from-blue-50/90 via-white to-blue-50/90 p-3 sm:p-4 shadow-xs transition-all hover:border-blue-400 hover:shadow-sm', children: [_jsx("audio", { ref: audioRef, src: resolvedAudioSrc || undefined, preload: "metadata" }), _jsxs("div", { className: "flex items-center gap-3 sm:gap-4", children: [_jsxs("button", { type: "button", onClick: handlePlayPause, className: `${dense ? 'h-10 w-10 sm:h-11 sm:w-11' : 'h-12 w-12'} relative flex shrink-0 items-center justify-center rounded-full bg-gradient-to-tr from-blue-600 to-indigo-600 text-white shadow-md shadow-blue-500/30 transition-all duration-200 hover:scale-105 hover:from-blue-700 hover:to-indigo-700 active:scale-95`, "aria-label": isPlaying ? 'Pause' : 'Play', children: [isPlaying ? (_jsx(Pause, { className: `${dense ? 'h-4 w-4 sm:h-5 sm:w-5' : 'h-5 w-5'} fill-current` })) : (_jsx(Play, { className: `${dense ? 'ml-0.5 h-4 w-4 sm:h-5 sm:w-5' : 'ml-0.5 h-5 w-5'} fill-current` })), isPlaying && (_jsx("span", { className: "pointer-events-none absolute inset-0 animate-ping rounded-full bg-blue-400 opacity-40" }))] }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-1.5", children: [_jsxs("div", { className: "flex items-center justify-between px-0.5", children: [_jsx("span", { className: "text-xs font-black uppercase tracking-widest text-blue-900", children: "AUDIO" }), _jsxs("div", { className: "flex items-center gap-1 rounded-md bg-white px-2 py-0.5 text-xs font-mono font-bold text-slate-600 border border-blue-100 shadow-2xs", children: [_jsx("span", { className: "text-blue-600", children: formatTime(currentTime) }), _jsx("span", { className: "text-slate-300", children: "/" }), _jsx("span", { children: formatTime(duration) })] })] }), _jsxs("div", { className: "relative flex items-center", children: [_jsx("div", { className: "h-2.5 w-full overflow-hidden rounded-full bg-blue-100/80 shadow-inner", children: _jsx("div", { className: "h-full rounded-full bg-gradient-to-r from-blue-500 to-indigo-600 transition-[width] duration-150", style: { width: `${progressPct}%` } }) }), _jsx("input", { type: "range", min: "0", max: duration || 0, step: "0.1", value: currentTime, onChange: (e) => {
118
+ const newTime = parseFloat(e.target.value);
119
+ if (audioRef.current)
120
+ audioRef.current.currentTime = newTime;
121
+ setCurrentTime(newTime);
122
+ }, className: "absolute inset-0 w-full cursor-pointer opacity-0", "aria-label": "Seek" })] })] })] })] }));
123
+ }
124
+ // --------------------- FULL MODE ---------------------
125
+ // Circular progress ring math
126
+ const RING_SIZE = 56;
127
+ const RING_STROKE = 3;
128
+ const RING_RADIUS = (RING_SIZE - RING_STROKE) / 2;
129
+ const RING_CIRC = 2 * Math.PI * RING_RADIUS;
130
+ return (_jsxs("div", { className: "relative overflow-hidden rounded-2xl border-2 border-blue-100 bg-white p-3.5 shadow-sm transition-shadow hover:border-blue-200 hover:shadow-md", children: [_jsx("audio", { ref: audioRef, src: resolvedAudioSrc || undefined, preload: "metadata" }), _jsx("div", { className: "pointer-events-none absolute -right-6 -top-6 h-20 w-20 rounded-full bg-blue-100 opacity-60 blur-xl" }), _jsxs("div", { className: "relative flex items-center gap-3", children: [_jsxs("button", { type: "button", onClick: handleRewind, className: "relative flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-slate-500 transition-all hover:bg-slate-100 hover:text-blue-600 active:scale-95", "aria-label": "Rewind 5 seconds", title: "Quay l\u1EA1i 5 gi\u00E2y", children: [_jsx(RotateCcw, { className: "h-4 w-4", strokeWidth: 2.5 }), _jsx("span", { className: "pointer-events-none absolute bottom-1 right-1 text-[8px] font-black leading-none", children: "5" })] }), _jsxs("div", { className: "relative shrink-0", style: { width: RING_SIZE, height: RING_SIZE }, children: [_jsxs("svg", { width: RING_SIZE, height: RING_SIZE, className: "absolute inset-0 -rotate-90", "aria-hidden": true, children: [_jsx("circle", { cx: RING_SIZE / 2, cy: RING_SIZE / 2, r: RING_RADIUS, fill: "none", stroke: "rgb(239 246 255)", strokeWidth: RING_STROKE }), _jsx("circle", { cx: RING_SIZE / 2, cy: RING_SIZE / 2, r: RING_RADIUS, fill: "none", stroke: "url(#audio-ring-gradient)", strokeWidth: RING_STROKE, strokeLinecap: "round", strokeDasharray: RING_CIRC, strokeDashoffset: RING_CIRC * (1 - progress), className: "transition-[stroke-dashoffset] duration-150" }), _jsx("defs", { children: _jsxs("linearGradient", { id: "audio-ring-gradient", x1: "0", y1: "0", x2: "1", y2: "1", children: [_jsx("stop", { offset: "0%", stopColor: "#3B82F6" }), _jsx("stop", { offset: "100%", stopColor: "#06b6d4" })] }) })] }), _jsxs("button", { type: "button", onClick: handlePlayPause, className: "absolute inset-1.5 flex items-center justify-center rounded-full bg-gradient-to-tr from-blue-600 to-indigo-600 text-white shadow-lg shadow-blue-500/30 transition-all hover:shadow-xl hover:shadow-blue-500/50 hover:from-blue-700 hover:to-indigo-700 active:scale-95", "aria-label": isPlaying ? 'Pause' : 'Play', children: [isPlaying ? (_jsx(Pause, { className: "h-4 w-4 fill-current" })) : (_jsx(Play, { className: "ml-0.5 h-4 w-4 fill-current" })), isPlaying && (_jsx("span", { className: "pointer-events-none absolute inset-0 animate-ping rounded-full bg-blue-400 opacity-40" }))] })] }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [_jsx("div", { onClick: handleWaveformClick, className: "group/wave relative flex h-8 cursor-pointer items-center gap-[2px]", role: "slider", "aria-label": "Seek", "aria-valuemin": 0, "aria-valuemax": duration || 0, "aria-valuenow": currentTime, children: barHeights.map((h, i) => {
131
+ const barPos = (i + 0.5) / WAVEFORM_BARS;
132
+ const isFilled = barPos <= progress;
133
+ const isCurrent = Math.abs(barPos - progress) < 1 / WAVEFORM_BARS / 2;
134
+ return (_jsx("span", { className: `flex-1 rounded-full transition-all duration-200 ${isFilled
135
+ ? 'bg-blue-500'
136
+ : 'bg-slate-200 group-hover/wave:bg-slate-300'} ${isCurrent && isPlaying ? 'animate-wave-pulse' : ''}`, style: { height: `${h * 100}%` } }, i));
137
+ }) }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-baseline gap-1.5", children: [_jsxs("span", { className: "font-mono text-sm font-bold tabular-nums text-slate-800", children: ["\u2212", formatTime(remaining)] }), _jsxs("span", { className: "font-mono text-[10px] tabular-nums text-slate-400", children: ["/ ", formatTime(duration)] })] }), _jsx("div", { className: "flex items-center gap-0.5 rounded-full border border-slate-200 bg-white p-0.5 shadow-sm", children: PLAYBACK_SPEEDS.map((s) => (_jsxs("button", { type: "button", onClick: () => setSpeed(s), className: `rounded-full px-2 py-0.5 font-mono text-[10px] font-semibold tabular-nums transition-all ${speed === s
138
+ ? 'bg-coral-500 text-white shadow-sm'
139
+ : 'text-slate-500 hover:bg-slate-100 hover:text-slate-700'}`, children: [s, "\u00D7"] }, s))) })] })] })] })] }));
140
+ }
@@ -1 +1,10 @@
1
- import{jsx as e,jsxs as t}from"react/jsx-runtime";import{ChevronLeft as o,ChevronRight as r}from"lucide-react";import{cn as s}from"../../../shared/lib/utils";import a from"next/image";import i from"./assets/images/rangsanho.png";export function CambridgeYleBottomNav({onPrevious:n,onNext:l,isFirstQuestion:d,isLastQuestion:c,isNavDisabled:b,className:m}){return t("div",{className:s("flex items-center justify-between py-2 relative",m),children:[t("button",{onClick:n,disabled:d||b,className:s("flex items-center gap-2 px-8 py-3 rounded-full font-bold text-[15px] transition-all duration-300 z-10",d||b?"opacity-50 cursor-not-allowed bg-white border-2 border-gray-200 text-gray-400":"bg-white border-2 border-blue-200 text-blue-600 hover:bg-blue-50 hover:shadow-md cursor-pointer active:scale-95 shadow-sm"),children:[e(o,{size:20,strokeWidth:3}),"Previous"]}),e("div",{className:"absolute left-1/2 bottom-[-4px] -translate-x-1/2 flex items-end pointer-events-none z-0",children:e(a,{src:i,alt:"Rặng san hô",className:"h-[72px] w-auto object-contain"})}),t("button",{onClick:l,disabled:c||b,className:s("flex items-center gap-2 px-10 py-3 rounded-full font-bold text-[15px] transition-all duration-300 shadow-md z-10 border-2 border-transparent",c||b?"opacity-50 cursor-not-allowed bg-gray-300 text-gray-500 shadow-none":"bg-blue-600 text-white hover:bg-blue-700 hover:shadow-lg cursor-pointer active:scale-95"),children:["Next",e(r,{size:20,strokeWidth:3})]})]})}
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ChevronLeft, ChevronRight } from 'lucide-react';
3
+ import { cn } from '../../../shared/lib/utils';
4
+ export function CambridgeYleBottomNav({ onPrevious, onNext, isFirstQuestion, isLastQuestion, isNavDisabled, className, }) {
5
+ return (_jsxs("div", { className: cn("flex items-center justify-between gap-3 py-0.5", className), children: [_jsxs("button", { onClick: onPrevious, disabled: isFirstQuestion || isNavDisabled, className: cn("flex items-center justify-center gap-1 px-3 py-1.5 sm:px-4 rounded-full font-bold text-[12px] sm:text-[13px] transition-all duration-300", (isFirstQuestion || isNavDisabled)
6
+ ? "opacity-50 cursor-not-allowed bg-white border border-gray-200 text-gray-400"
7
+ : "bg-white border border-blue-200 text-blue-600 hover:bg-blue-50 hover:shadow-md cursor-pointer active:scale-95 shadow-sm"), children: [_jsx(ChevronLeft, { className: "h-4 w-4", strokeWidth: 3 }), "Previous"] }), _jsxs("button", { onClick: onNext, disabled: isLastQuestion || isNavDisabled, className: cn("flex items-center justify-center gap-1 px-4 py-1.5 sm:px-5 rounded-full font-bold text-[12px] sm:text-[13px] transition-all duration-300 shadow-md border border-transparent", (isLastQuestion || isNavDisabled)
8
+ ? "opacity-50 cursor-not-allowed bg-gray-300 text-gray-500 shadow-none"
9
+ : "bg-blue-600 text-white hover:bg-blue-700 hover:shadow-lg cursor-pointer active:scale-95"), children: ["Next", _jsx(ChevronRight, { className: "h-4 w-4", strokeWidth: 3 })] })] }));
10
+ }
@@ -1,2 +1,2 @@
1
1
  import type { StandardExamLayoutProps } from '../types';
2
- export declare function CambridgeYleExamLayout({ children, onPrevious, onNext, onClose, isFirstQuestion, isLastQuestion, isNavDisabled, headerProps, sidebarProps, partName, }: StandardExamLayoutProps): import("react").JSX.Element;
2
+ export declare function CambridgeYleExamLayout({ children, onPrevious, onNext, onClose, isFirstQuestion, isLastQuestion, isNavDisabled, headerProps, sidebarProps, }: StandardExamLayoutProps): import("react").JSX.Element;
@@ -1 +1,18 @@
1
- import{jsx as e,jsxs as r}from"react/jsx-runtime";import{CambridgeYleHeader as i}from"./CambridgeYleHeader";import{CambridgeYleSidebar as l}from"./CambridgeYleSidebar";import{CambridgeYleBottomNav as s}from"./CambridgeYleBottomNav";import{CambridgeYleNavButton as a}from"./CambridgeYleNavButton";import{cn as d}from"../../../shared/lib/utils";import o from"../summer-sky/assets/images/background-v2.png";import n from"../summer-sky/assets/images/ocean-frame.png";export function CambridgeYleExamLayout({children:t,onPrevious:m,onNext:c,onClose:p,isFirstQuestion:x,isLastQuestion:f,isNavDisabled:u,headerProps:b,sidebarProps:h,partName:v}){return r("div",{className:"relative flex h-screen flex-col overflow-hidden bg-cover bg-center bg-no-repeat",style:{backgroundImage:`url(${o.src})`},children:[e("div",{className:"absolute inset-0 bg-black/10"}),e("div",{className:"relative z-50",children:e(i,{...b,onClose:p})}),r("div",{className:"relative z-10 flex flex-1 min-h-0 items-stretch justify-center p-2 pt-0 pb-2 sm:px-4 sm:pt-0 sm:pb-4 lg:px-[2%] lg:pt-0 lg:pb-[20px] max-w-[1500px] mx-auto w-full gap-2 xl:gap-4",children:[e("div",{className:"hidden xl:flex shrink-0 items-center",children:e(a,{direction:"prev",onClick:m,disabled:x||u})}),r("div",{className:"flex flex-1 w-full relative drop-shadow-xl min-h-0",children:[e("div",{className:"absolute inset-0 pointer-events-none",style:{background:`url(${n.src}) no-repeat center center`,backgroundSize:"100% 100%",zIndex:0}}),r("div",{className:"flex flex-1 w-full relative z-10 p-[12px] sm:p-[16px] md:p-[24px]",children:[h&&e("div",{className:"hidden lg:flex w-[30%] shrink-0 bg-[#eef7ff] rounded-l-[20px] md:rounded-l-[32px] border-r-2 border-blue-100 flex-col overflow-hidden",children:e(l,{...h})}),r("div",{className:d("flex w-full min-h-0 flex-col flex-1 relative bg-white overflow-hidden",h?"rounded-r-[20px] md:rounded-r-[32px]":"rounded-[20px] md:rounded-[32px]"),children:[e("div",{id:"exam-main-scroll-area",className:"flex-1 min-h-0 overflow-auto",children:t}),e("div",{className:"hidden shrink-0 bg-white p-1 sm:px-6 border-t border-blue-200/50",children:e(s,{onPrevious:m,onNext:c,isFirstQuestion:x,isLastQuestion:f,isNavDisabled:u})})]})]})]}),e("div",{className:"hidden xl:flex shrink-0 items-center",children:e(a,{direction:"next",onClick:c,disabled:f||u})})]})]})}
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { CambridgeYleHeader } from './CambridgeYleHeader';
3
+ import { CambridgeYleSidebar } from './CambridgeYleSidebar';
4
+ import { CambridgeYleBottomNav } from './CambridgeYleBottomNav';
5
+ import { CambridgeYleNavButton } from './CambridgeYleNavButton';
6
+ import { cn } from '../../../shared/lib/utils';
7
+ import backgroundSummerSky from '../summer-sky/assets/images/background-v2.png';
8
+ import oceanFrameImg from '../summer-sky/assets/images/ocean-frame.png';
9
+ export function CambridgeYleExamLayout({ children, onPrevious, onNext, onClose, isFirstQuestion, isLastQuestion, isNavDisabled, headerProps, sidebarProps, }) {
10
+ // No longer using dynamic decor images for title badge
11
+ return (_jsxs("div", { className: "relative flex h-screen flex-col overflow-hidden bg-cover bg-center bg-no-repeat", style: {
12
+ backgroundImage: `url(${backgroundSummerSky.src})`,
13
+ }, children: [_jsx("div", { className: "absolute inset-0 bg-black/10" }), _jsx("div", { className: "relative z-50", style: { marginBottom: '10px' }, children: _jsx(CambridgeYleHeader, { ...headerProps, onClose: onClose }) }), _jsxs("div", { className: "relative z-10 flex flex-1 min-h-0 items-stretch justify-center p-2 pt-0 pb-2 sm:px-4 sm:pt-0 sm:pb-4 lg:px-[2%] lg:pt-0 lg:pb-[20px] max-w-[1500px] mx-auto w-full gap-2 xl:gap-4", children: [_jsx("div", { className: "hidden lg:flex shrink-0 items-center self-stretch", children: _jsx(CambridgeYleNavButton, { direction: "prev", onClick: onPrevious, disabled: isFirstQuestion || isNavDisabled }) }), _jsxs("div", { className: "relative flex min-h-0 min-w-0 flex-1 drop-shadow-xl", children: [_jsx("div", { className: "absolute inset-0 pointer-events-none", style: {
14
+ background: `url(${oceanFrameImg.src}) no-repeat center center`,
15
+ backgroundSize: '100% 100%',
16
+ zIndex: 0
17
+ } }), _jsxs("div", { className: "flex flex-1 w-full relative z-10 p-[6px] sm:p-[12px] md:p-[18px] xl:p-[24px]", children: [sidebarProps && (_jsx("div", { className: "hidden lg:flex w-[250px] xl:w-[290px] 2xl:w-[330px] shrink-0 bg-[#eef7ff] rounded-l-[20px] md:rounded-l-[32px] border-r-2 border-blue-100 flex-col overflow-hidden", children: _jsx(CambridgeYleSidebar, { ...sidebarProps }) })), _jsxs("div", { className: cn("flex w-full min-h-0 flex-col flex-1 relative bg-white overflow-hidden", sidebarProps ? "rounded-r-[20px] md:rounded-r-[32px]" : "rounded-[20px] md:rounded-[32px]"), children: [_jsx("div", { id: "exam-main-scroll-area", className: "flex-1 min-h-0 overflow-auto", children: children }), _jsx("div", { className: "lg:hidden shrink-0 bg-white px-3 py-1 border-t border-blue-200/50", children: _jsx(CambridgeYleBottomNav, { onPrevious: onPrevious, onNext: onNext, isFirstQuestion: isFirstQuestion, isLastQuestion: isLastQuestion, isNavDisabled: isNavDisabled }) })] })] })] }), _jsx("div", { className: "hidden lg:flex shrink-0 items-center self-stretch", children: _jsx(CambridgeYleNavButton, { direction: "next", onClick: onNext, disabled: isLastQuestion || isNavDisabled }) })] })] }));
18
+ }
@@ -1 +1,11 @@
1
- import{jsx as e,jsxs as n,Fragment as s}from"react/jsx-runtime";export default function t({text:t,highlightedWord:a}){return n("div",{className:"text-base",children:[e("span",{className:"font-bold",children:"Example:"})," ",(()=>{if(!a)return e("span",{children:t});const r=t.split(a);return e(s,{children:r.map((s,t)=>n("span",{children:[s,t<r.length-1&&e("span",{className:"font-bold",children:a})]},t))})})()]})}
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ export default function CambridgeYleExampleText({ text, highlightedWord, }) {
3
+ const renderText = () => {
4
+ if (!highlightedWord) {
5
+ return _jsx("span", { children: text });
6
+ }
7
+ const parts = text.split(highlightedWord);
8
+ return (_jsx(_Fragment, { children: parts.map((part, index) => (_jsxs("span", { children: [part, index < parts.length - 1 && (_jsx("span", { className: "font-bold", children: highlightedWord }))] }, index))) }));
9
+ };
10
+ return (_jsxs("div", { className: "text-base", children: [_jsx("span", { className: "font-bold", children: "Example:" }), " ", renderText()] }));
11
+ }
@@ -1 +1,11 @@
1
- import{jsx as e,jsxs as s}from"react/jsx-runtime";import t from"next/image";import{CambridgeYleTimer as r}from"./CambridgeYleTimer";import{Loader2 as a}from"lucide-react";import i from"../summer-sky/assets/images/arrow-left.png";import n from"../summer-sky/assets/images/title-logo.png";import o from"../summer-sky/assets/images/submit.png";import m from"../summer-sky/assets/images/has_answered.png";export function CambridgeYleHeader({totalQuestions:l,answeredCount:p,remainingSeconds:x,isSubmitting:d,isCameraActive:c,onSubmit:h,onClose:u}){return s("header",{className:"w-full bg-transparent px-4 pt-4 pb-0 sm:px-8 relative flex flex-wrap sm:flex-nowrap items-start sm:items-center justify-between z-50",children:[e("div",{onClick:u,className:"cursor-pointer hover:scale-105 active:scale-95 transition-transform shrink-0",title:"Thoát bài thi",children:e(t,{src:i,alt:"Quay lại",className:"w-[50px] sm:w-[80px]"})}),e("div",{className:"absolute left-1/2 -translate-x-1/2 -top-2 sm:top-0 pointer-events-none hidden sm:block",children:e(t,{src:n,alt:"Title Logo",className:"w-[200px] sm:w-[320px] h-auto"})}),e("div",{className:"sm:hidden flex-1 flex justify-center pointer-events-none",children:e(t,{src:n,alt:"Title Logo",className:"w-[150px] h-auto -mt-2"})}),s("div",{className:"absolute right-4 sm:right-8 top-8 flex flex-row items-center gap-3 sm:gap-4 z-[100]",children:[c&&e("div",{className:"flex items-center justify-center rounded-full bg-white/20 p-2 backdrop-blur-sm shrink-0",title:"Camera đang ghi hình",children:e("div",{className:"h-3 w-3 animate-pulse rounded-full bg-red-500"})}),e(r,{remainingSeconds:x,totalQuestions:l,answeredCount:p}),s("div",{className:"flex items-center w-[130px] sm:w-[170px] h-[40px] sm:h-[50px] rounded-[33.5px] bg-[#FF9900] shadow-[0_4px_20px_rgba(0,0,0,0.25)] border-none shrink-0",title:"Số câu hỏi đã hoàn thành",children:[e(t,{src:m,alt:"Progress",width:75,height:75,className:"w-[50px] sm:w-[65px] -ml-[5px] sm:-ml-[10px] -mt-[10px] sm:-mt-[15px] shrink-0 drop-shadow-[0_2px_6px_rgba(0,0,0,0.2)] pointer-events-none z-10"}),s("span",{className:"flex-1 text-center font-bold text-[18px] sm:text-[25px] leading-none tracking-[-0.03em] uppercase text-white pr-[10px] sm:pr-[16px]",style:{fontFamily:'"SVN-Volte Rounded", "Quicksand", sans-serif',textShadow:"0 2px 4px rgba(0,0,0,0.2)"},children:[p,"/",l]})]}),s("button",{onClick:h,disabled:d,className:"group cursor-pointer flex items-center h-[40px] sm:h-[50px] w-[130px] sm:w-[170px] rounded-[33.5px] shadow-[0_4px_20px_rgba(0,0,0,0.25)] transition-all duration-300 hover:scale-105 active:scale-95 disabled:pointer-events-none disabled:opacity-60 border-none shrink-0 "+(d?"bg-slate-400":"bg-[#16CA3B] hover:bg-[#15b736]"),children:[e(t,{src:o,alt:"Submit",width:75,height:75,className:"w-[50px] sm:w-[65px] -ml-[5px] sm:-ml-[10px] -mt-[10px] sm:-mt-[15px] shrink-0 drop-shadow-[0_2px_6px_rgba(0,0,0,0.2)] pointer-events-none transition-transform duration-300 group-hover:scale-110 z-10"}),d?e("div",{className:"flex-1 flex justify-center pr-[10px] sm:pr-[16px]",children:e(a,{className:"h-6 w-6 animate-spin text-white"})}):e("span",{className:"flex-1 text-center font-bold text-[18px] sm:text-[20px] leading-none tracking-[-0.03em] uppercase text-white pr-[10px] sm:pr-[16px]",style:{fontFamily:'"SVN-Volte Rounded", "Quicksand", sans-serif',textShadow:"0 2px 4px rgba(0,0,0,0.2)"},children:"Nộp bài"})]})]})]})}
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import Image from 'next/image';
3
+ import { CambridgeYleTimer } from './CambridgeYleTimer';
4
+ import { Loader2 } from 'lucide-react';
5
+ import arrowLeftImg from '../summer-sky/assets/images/arrow-left.png';
6
+ import titleLogoImg from '../summer-sky/assets/images/title-logo.png';
7
+ import submitImg from '../summer-sky/assets/images/submit.png';
8
+ import hasAnsweredImg from '../summer-sky/assets/images/has_answered.png';
9
+ export function CambridgeYleHeader({ totalQuestions, answeredCount, remainingSeconds, isSubmitting, isCameraActive, onSubmit, onClose, }) {
10
+ return (_jsxs("header", { className: "w-full bg-transparent px-2 pt-2 sm:px-4 sm:pt-2.5 lg:px-8 lg:pt-3 pb-0 relative flex items-center justify-between z-50", children: [_jsx("div", { onClick: onClose, className: "cursor-pointer hover:scale-105 active:scale-95 transition-transform shrink-0", title: "Tho\u00E1t b\u00E0i thi", children: _jsx(Image, { src: arrowLeftImg, alt: "Quay l\u1EA1i", className: "w-[20px] h-auto sm:w-[24px] md:w-[32px] lg:w-[44px] xl:w-[64px]" }) }), _jsx("div", { className: "absolute left-1/2 -translate-x-1/2 top-0 pointer-events-none hidden lg:block", children: _jsx(Image, { src: titleLogoImg, alt: "Title Logo", className: "w-[150px] lg:w-[200px] xl:w-[280px] h-auto" }) }), _jsx("div", { className: "lg:hidden flex-1 flex justify-center pointer-events-none", children: _jsx(Image, { src: titleLogoImg, alt: "Title Logo", className: "w-[110px] sm:w-[130px] h-auto -mt-1" }) }), _jsxs("div", { className: "absolute right-2 sm:right-4 md:right-6 lg:right-8 top-2 sm:top-3 lg:top-5 flex flex-row items-center gap-1 sm:gap-1.5 md:gap-2 lg:gap-3 z-[100]", children: [isCameraActive && (_jsx("div", { className: "flex items-center justify-center rounded-full bg-white/20 p-1 sm:p-1.5 backdrop-blur-sm shrink-0", title: "Camera \u0111ang ghi h\u00ECnh", children: _jsx("div", { className: "h-2 w-2 sm:h-2.5 sm:w-2.5 animate-pulse rounded-full bg-red-500" }) })), _jsx(CambridgeYleTimer, { remainingSeconds: remainingSeconds, totalQuestions: totalQuestions, answeredCount: answeredCount }), _jsxs("div", { className: "flex items-center w-[76px] sm:w-[86px] md:w-[94px] lg:w-[100px] xl:w-[140px] 2xl:w-[160px] h-[26px] sm:h-[29px] md:h-[32px] lg:h-[34px] xl:h-[46px] rounded-[33.5px] bg-[#FF9900] shadow-[0_4px_20px_rgba(0,0,0,0.25)] border-none shrink-0", title: "S\u1ED1 c\u00E2u h\u1ECFi \u0111\u00E3 ho\u00E0n th\u00E0nh", children: [_jsx(Image, { src: hasAnsweredImg, alt: "Progress", width: 75, height: 75, className: "w-[29px] sm:w-[33px] md:w-[37px] lg:w-[42px] xl:w-[60px] -ml-[2px] sm:-ml-[3px] md:-ml-[4px] lg:-ml-[6px] -mt-[3px] sm:-mt-[4px] md:-mt-[6px] lg:-mt-[8px] shrink-0 drop-shadow-[0_2px_6px_rgba(0,0,0,0.2)] pointer-events-none z-10" }), _jsxs("span", { className: "flex-1 text-center font-bold text-[10px] sm:text-[11px] md:text-[13px] lg:text-[14px] xl:text-[21px] 2xl:text-[23px] leading-none tracking-[-0.03em] uppercase text-white pr-[3px] sm:pr-[4px] md:pr-[5px] lg:pr-[7px] xl:pr-[12px]", style: { fontFamily: '"SVN-Volte Rounded", "Quicksand", sans-serif', textShadow: '0 2px 4px rgba(0,0,0,0.2)' }, children: [answeredCount, "/", totalQuestions] })] }), _jsxs("button", { onClick: onSubmit, disabled: isSubmitting, className: `group cursor-pointer flex items-center h-[26px] sm:h-[29px] md:h-[32px] lg:h-[34px] xl:h-[46px] w-[76px] sm:w-[86px] md:w-[94px] lg:w-[100px] xl:w-[140px] 2xl:w-[160px] rounded-[33.5px] shadow-[0_4px_20px_rgba(0,0,0,0.25)] transition-all duration-300 hover:scale-105 active:scale-95 disabled:pointer-events-none disabled:opacity-60 border-none shrink-0 ${isSubmitting ? 'bg-slate-400' : 'bg-[#16CA3B] hover:bg-[#15b736]'}`, children: [_jsx(Image, { src: submitImg, alt: "Submit", width: 75, height: 75, className: "w-[29px] sm:w-[33px] md:w-[37px] lg:w-[42px] xl:w-[60px] -ml-[2px] sm:-ml-[3px] md:-ml-[4px] lg:-ml-[6px] -mt-[3px] sm:-mt-[4px] md:-mt-[6px] lg:-mt-[8px] shrink-0 drop-shadow-[0_2px_6px_rgba(0,0,0,0.2)] pointer-events-none transition-transform duration-300 group-hover:scale-110" }), isSubmitting ? (_jsx("div", { className: "flex-1 flex justify-center pr-[3px] sm:pr-[4px] md:pr-[5px] lg:pr-[7px] xl:pr-[12px]", children: _jsx(Loader2, { className: "h-3 w-3 sm:h-4 sm:w-4 animate-spin text-white" }) })) : (_jsx("span", { className: "flex-1 text-center font-bold text-[10px] sm:text-[11px] md:text-[13px] lg:text-[14px] xl:text-[21px] 2xl:text-[23px] leading-none tracking-[-0.03em] uppercase text-white pr-[3px] sm:pr-[4px] md:pr-[5px] lg:pr-[7px] xl:pr-[12px]", style: { fontFamily: '"SVN-Volte Rounded", "Quicksand", sans-serif', textShadow: '0 2px 4px rgba(0,0,0,0.2)' }, children: "N\u1ED9p" }))] })] })] }));
11
+ }