@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,171 @@
1
- "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{X as t}from"lucide-react";import{useMemo as a}from"react";import{ResolvedImage as l}from"../../../../../components/common/ResolvedImage";import n from"../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import s from"../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";import{cn as i}from"../../../../../shared/lib/utils";function o({item:t,isUsed:a,isExample:n}){return r("div",{className:i("flex flex-col items-center justify-between overflow-hidden rounded-2xl border-2 bg-white p-3 shadow-sm transition-all duration-300",n?"border-amber-200 bg-amber-50/50":a?"border-emerald-200 bg-emerald-50/50 opacity-60":"border-slate-200"),children:[t.imageUrl&&e("div",{className:"flex h-24 sm:h-28 w-full items-center justify-center overflow-hidden rounded-xl bg-slate-50/50 p-2 mb-3",children:e(l,{src:t.imageUrl,alt:t.word,className:"h-full w-full object-contain drop-shadow-sm"})}),r("div",{className:i("flex w-full flex-col items-center",!t.imageUrl&&"mt-0"),children:[e("span",{className:"text-base font-extrabold tracking-wide text-slate-800",children:t.word}),n&&e("span",{className:"mt-1 rounded-full bg-amber-100 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider text-amber-700",children:"Example"}),a&&!n&&e("span",{className:"mt-1 rounded-full bg-emerald-100 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider text-emerald-700",children:"Used"})]})]})}function d({blankIndex:a,value:l,isExampleBlank:n,isReviewMode:s,onInputChange:o,onClear:d}){const c=!!l;return n?e("span",{className:"mx-1 inline-flex min-w-[110px] items-center justify-center border-0 border-b-2 border-amber-400 bg-transparent px-2 py-0.5 align-baseline text-sm font-bold text-amber-700",children:l}):r("span",{className:"mx-1 inline-flex items-end gap-1.5 align-baseline",children:[e("input",{type:"text","aria-label":`Blank ${a+1}`,value:l,onChange:e=>o(e.target.value),disabled:s,placeholder:"_____",className:i("h-8 min-w-[90px] max-w-[150px] rounded-none border-0 border-b-2 bg-transparent px-1 pb-0.5 text-center text-sm font-semibold shadow-none outline-none transition-all",c?"border-emerald-500 text-emerald-700":"border-slate-400 text-slate-500 placeholder:text-slate-300","focus:border-blue-500 focus:text-blue-700",s?"cursor-default":"cursor-text")}),!s&&c&&e("button",{type:"button",onClick:d,className:"inline-flex h-6 w-6 items-center justify-center rounded-full text-slate-400 transition-colors hover:bg-red-50 hover:text-red-500","aria-label":`Clear blank ${a+1}`,children:e(t,{className:"h-3.5 w-3.5"})})]})}export function MoversWordFillParagraphRenderer({partNumber:t,partName:c,questionCount:m,instruction:p,questionId:x,paragraph:b,title:u,imageUrl:h,wordBank:g,viewMode:f,answers:w,onAnswerChange:N,exampleAnswer:v,isReviewMode:y=!1,emailFrom:_,emailTo:k,correctAnswerTextMap:C}){const I=w[x],j=Array.isArray(I)?I:I&&"object"==typeof I?Object.keys(I).filter(e=>/^\d+$/.test(e)).sort((e,r)=>Number(e)-Number(r)).map(e=>I[e]):"string"==typeof I?[I]:[],$=a(()=>Array.isArray(g)?g:[],[g]),M=a(()=>{if(v&&v.length>0)return v;const e=[];let r=!1;for(let t=0;t<$.length;t++)$[t]?.isExample?(r=!0,y&&C?e.push(C[`${x}-blank-${t}`]||$[t]?.word||""):e.push($[t]?.word||"")):e.push(void 0);return r?e:void 0},[v,$,y,C,x]),R=b.split(/(\{\d+\})/),A=e=>Boolean(M?.[e]),B=(e,r)=>{if(y||A(e))return;const t=[...j];t[e]=r,N(x,t)},E=e=>{if(y||A(e))return;const r=[...j];r[e]="",N(x,r)},T=new Set(j.filter(Boolean));return r("div","EMAIL"===f?{className:"grid h-full min-h-0 grid-cols-1 gap-4 sm:gap-6 "+(y?"":"lg:grid-cols-2"),children:[r("div",{className:"flex min-h-0 flex-col gap-4 overflow-y-auto p-3 sm:pb-6 sm:pr-6 sm:pt-6",children:[e(n,{partNumber:t,questionCount:m,partName:c,isReviewMode:y}),e(s,{instruction:p,isReviewMode:y}),h&&e("div",{className:"flex items-center justify-center",children:e(l,{src:h,alt:"Illustration",className:"max-h-[300px] rounded-lg object-contain shadow-md"})})]}),r("div",{className:"flex min-h-0 flex-col gap-4 p-3 sm:gap-5 sm:pb-6 sm:pl-6 sm:pt-6",children:[r("div",{className:"shrink-0 overflow-hidden rounded-2xl border border-sky-100 bg-white shadow-[0_10px_30px_rgba(15,23,42,0.06)]",children:[e("div",{className:"bg-gradient-to-r from-sky-100 via-blue-50 to-sky-100 px-5 py-3",children:e("span",{className:"text-[15px] font-extrabold uppercase tracking-[0.14em] text-sky-900",children:"Email"})}),r("div",{className:"space-y-3 bg-gradient-to-b from-white to-slate-50/80 px-5 py-4",children:[r("div",{className:"grid grid-cols-[64px_1fr] items-center gap-4",children:[e("span",{className:"text-[15px] font-semibold text-slate-700",children:"From:"}),e("div",{className:"rounded-xl border border-slate-200 bg-white px-4 py-2.5 text-base font-medium leading-6 text-slate-800 shadow-[inset_0_1px_2px_rgba(15,23,42,0.04)]",children:_||"—"})]}),r("div",{className:"grid grid-cols-[64px_1fr] items-center gap-4",children:[e("span",{className:"text-[15px] font-semibold text-slate-700",children:"To:"}),e("div",{className:"rounded-xl border border-slate-200 bg-white px-4 py-2.5 text-base font-medium leading-6 text-slate-800 shadow-[inset_0_1px_2px_rgba(15,23,42,0.04)]",children:k||"—"})]})]})]}),e("div",{className:i("custom-scrollbar flex-1 min-h-0 overflow-y-auto rounded-2xl border border-slate-200 bg-white p-5 pr-3 text-base leading-loose text-gray-800 shadow-[0_8px_24px_rgba(15,23,42,0.04)]",!y&&"whitespace-pre-line"),children:R.map((t,a)=>{const l=t.match(/^\{(\d+)\}$/);if(!l)return e("span",{children:t},a);const n=parseInt(l[1],10),s=j[n]||"";if(y&&C){const t=C[`${x}-blank-${n}`]||"",l=!s,i=!l&&s.trim().toLowerCase()===t.trim().toLowerCase();return l?e("span",{className:"mx-1 inline-block min-w-[80px] border-b-2 border-blue-400 px-1 py-0.5 text-center text-blue-600",children:t||"___"},a):i?e("span",{className:"mx-1 inline-block min-w-[80px] border-b-2 border-green-500 px-1 py-0.5 text-center text-green-700",children:s},a):r("span",{className:"mx-1 inline-block min-w-[80px] border-b-2 border-red-400 px-1 py-0.5 text-center",children:[e("span",{className:"text-red-500 line-through",children:s}),t&&r("span",{className:"ml-1 text-green-600",children:["→ ",t]})]},a)}return r("span",{className:"mx-0.5 inline-flex items-baseline gap-0.5 align-baseline",children:[r("span",{className:"text-[11px] font-bold text-blue-400",children:["(",n+1,")"]}),e("input",{type:"text",value:s,onChange:e=>{const r=[...j];r[n]=e.target.value,N(x,r)},disabled:y,placeholder:"···",style:{width:`${Math.max(3,(s||"").length+1)}ch`},className:i("inline-block min-w-[3ch] max-w-[12ch] border-b-2 bg-transparent px-0.5 py-0 text-center text-sm font-medium outline-none transition-colors",s?"border-emerald-400 text-emerald-700":"border-slate-300 text-slate-500","focus:border-blue-500 focus:text-blue-700","disabled:opacity-60")})]},a)})})]})]}:{className:"grid h-full min-h-0 grid-cols-1 gap-4 sm:gap-6 "+(y?"":"lg:grid-cols-2"),children:[r("div",{className:"flex min-h-0 flex-col gap-4 overflow-y-auto p-3 sm:pb-6 sm:pr-6 sm:pt-6",children:[e(n,{partNumber:t,questionCount:m,partName:c,isReviewMode:y}),e(s,{instruction:p,isReviewMode:y}),u&&e("h2",{className:"shrink-0 text-center text-2xl font-bold uppercase text-coral-500",children:u}),h&&e("div",{className:"flex items-center justify-center",children:e(l,{src:h,alt:"Illustration",className:"max-h-[400px] w-full max-w-xl rounded-lg object-contain shadow-md"})}),"WITH_WORD_BANK"===f&&r("div",{className:"shrink-0 rounded-2xl border border-sky-100 bg-slate-50/50 p-4 sm:p-5 shadow-sm",children:[r("div",{className:"mb-4 flex items-center justify-center gap-2",children:[e("div",{className:"h-px flex-1 bg-gradient-to-r from-transparent to-sky-200"}),e("h3",{className:"text-sm font-extrabold uppercase tracking-widest text-sky-800",children:"WORD BANK (Reference)"}),e("div",{className:"h-px flex-1 bg-gradient-to-l from-transparent to-sky-200"})]}),e("div",{className:"grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-2 xl:grid-cols-3",children:$.map((r,t)=>{const a=T.has(r.word),l=r.isExample||M?.includes(r.word)||!1;return e(o,{item:r,isUsed:a,isExample:l},`${r.word}-${t}`)})})]})]}),e("div",{className:"custom-scrollbar flex min-h-0 flex-col gap-4 overflow-y-auto p-3 sm:gap-5 sm:pb-6 sm:pl-6 sm:pt-6",children:e("div",{className:i("shrink-0 rounded-2xl border border-slate-200 bg-white p-5 pr-3 text-base leading-loose text-gray-800 shadow-[0_8px_24px_rgba(15,23,42,0.04)]",!y&&"whitespace-pre-line"),children:R.map((t,a)=>{const l=t.match(/^\{(\d+)\}$/);if(!l)return e("span",{children:t},a);const n=parseInt(l[1],10),s=j[n]||"",o=M?.[n],c=Boolean(o),m=o||s;if(y&&!c&&C){const t=C[`${x}-blank-${n}`]||"",l=!s,i=!l&&s.trim().toLowerCase()===t.trim().toLowerCase();return l?e("span",{className:"mx-1 inline-block min-w-[80px] border-b-2 border-blue-400 px-1 py-0.5 text-center text-blue-600",children:t||"___"},a):i?e("span",{className:"mx-1 inline-block min-w-[80px] border-b-2 border-green-500 px-1 py-0.5 text-center text-green-700",children:s},a):r("span",{className:"mx-1 inline-block min-w-[80px] border-b-2 border-red-400 px-1 py-0.5 text-center",children:[e("span",{className:"text-red-500 line-through",children:s}),t&&r("span",{className:"ml-1 text-green-600",children:["→ ",t]})]},a)}if("WITH_OPTIONS"===f){if(c)return e("span",{className:"mx-1 inline-block min-w-[60px] border-b-2 border-amber-400 bg-amber-50 px-2 py-0.5 text-center text-sm font-bold text-amber-700 align-baseline",children:o||"___"},a);const t=$[n]?.options||[];return r("select",{value:s,onChange:e=>((e,r)=>{r?B(e,r):E(e)})(n,e.target.value),disabled:y,className:"mx-1 inline-block min-w-[100px] rounded border-2 border-blue-300 bg-blue-50 px-2 py-0.5 text-center text-sm font-medium outline-none focus:border-blue-500",children:[e("option",{value:"",children:"___"}),t.map(r=>e("option",{value:r,children:r},r))]},a)}if("DEFAULT"===f){const t=c?String(o||""):j[n]||"";return r("span",{className:"mx-0.5 inline-flex items-baseline gap-0.5 align-baseline",children:[r("span",{className:"text-[11px] font-bold text-blue-400",children:["(",n+1,")"]}),e("input",{type:"text",value:t,onChange:e=>{if(c)return;const r=[...j];r[n]=e.target.value,N(x,r)},disabled:y||!!c,placeholder:"·········",style:{width:`${Math.max(10,t.length+1)}ch`},className:i("inline-block min-w-[10ch] max-w-[12ch] border-b-2 bg-transparent px-0.5 py-0 text-center text-sm font-medium outline-none transition-colors",t?"border-emerald-400 text-emerald-700":"border-slate-300 text-slate-500","focus:border-blue-500 focus:text-blue-700","disabled:opacity-60")})]},a)}return"WITH_WORD_BANK"===f?e(d,{blankIndex:n,value:m||"",isExampleBlank:c,isReviewMode:y,onInputChange:e=>B(n,e),onClear:()=>E(n)},a):e("span",{children:t},a)})})})]})}
1
+ /**
2
+ * WORD_FILL_PARAGRAPH Renderer - Paragraph with blanks filled from word bank
3
+ * Supports WITH_WORD_BANK (input per blank) and WITH_OPTIONS (per-blank dropdown)
4
+ * Used in Starters P8, Movers P8/P9
5
+ */
6
+ 'use client';
7
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
8
+ import { X } from 'lucide-react';
9
+ import { useMemo } from 'react';
10
+ import { ResolvedImage } from '../../../../../components/common/ResolvedImage';
11
+ import CambridgeYlePartBanner from '../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner';
12
+ import CambridgeYleInstructionBanner from '../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner';
13
+ import { cn } from '../../../../../shared/lib/utils';
14
+ function WordReferenceTile({ item, isUsed, isExample }) {
15
+ return (_jsxs("div", { className: cn('flex flex-col items-center justify-between overflow-hidden rounded-2xl border-2 bg-white p-3 shadow-sm transition-all duration-300', isExample
16
+ ? 'border-amber-200 bg-amber-50/50'
17
+ : isUsed
18
+ ? 'border-emerald-200 bg-emerald-50/50 opacity-60'
19
+ : 'border-slate-200'), children: [item.imageUrl && (_jsx("div", { className: "flex h-24 sm:h-28 w-full items-center justify-center overflow-hidden rounded-xl bg-slate-50/50 p-2 mb-3", children: _jsx(ResolvedImage, { src: item.imageUrl, alt: item.word, className: "h-full w-full object-contain drop-shadow-sm" }) })), _jsxs("div", { className: cn("flex w-full flex-col items-center", !item.imageUrl && "mt-0"), children: [_jsx("span", { className: "text-base font-extrabold tracking-wide text-slate-800", children: item.word }), isExample && (_jsx("span", { className: "mt-1 rounded-full bg-amber-100 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider text-amber-700", children: "Example" })), isUsed && !isExample && (_jsx("span", { className: "mt-1 rounded-full bg-emerald-100 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider text-emerald-700", children: "Used" }))] })] }));
20
+ }
21
+ function InputBlank({ blankIndex, value, isExampleBlank, isReviewMode, onInputChange, onClear, }) {
22
+ const hasValue = !!value;
23
+ if (isExampleBlank) {
24
+ return (_jsx("span", { className: "mx-1 inline-flex min-w-[110px] items-center justify-center border-0 border-b-2 border-amber-400 bg-transparent px-2 py-0.5 align-baseline text-sm font-bold text-amber-700", children: value }));
25
+ }
26
+ return (_jsxs("span", { className: "mx-1 inline-flex items-end gap-1.5 align-baseline", children: [_jsx("input", { type: "text", "aria-label": `Blank ${blankIndex + 1}`, value: value, onChange: (e) => onInputChange(e.target.value), disabled: isReviewMode, placeholder: "_____", className: cn('h-8 min-w-[90px] max-w-[150px] rounded-none border-0 border-b-2 bg-transparent px-1 pb-0.5 text-center text-sm font-semibold shadow-none outline-none transition-all', hasValue
27
+ ? 'border-emerald-500 text-emerald-700'
28
+ : 'border-slate-400 text-slate-500 placeholder:text-slate-300', 'focus:border-blue-500 focus:text-blue-700', isReviewMode ? 'cursor-default' : 'cursor-text') }), !isReviewMode && hasValue && (_jsx("button", { type: "button", onClick: onClear, className: "inline-flex h-6 w-6 items-center justify-center rounded-full text-slate-400 transition-colors hover:bg-red-50 hover:text-red-500", "aria-label": `Clear blank ${blankIndex + 1}`, children: _jsx(X, { className: "h-3.5 w-3.5" }) }))] }));
29
+ }
30
+ export function MoversWordFillParagraphRenderer({ partNumber, partName, questionCount, instruction, questionId, paragraph, title, imageUrl, wordBank, viewMode, answers, onAnswerChange, exampleAnswer, isReviewMode = false, emailFrom, emailTo, correctAnswerTextMap, }) {
31
+ // Defensive: answers[questionId] may be a non-array (legacy object / string)
32
+ // for old data. Coerce to array so `.filter` and spread never crash.
33
+ const rawCurrentAnswer = answers[questionId];
34
+ const currentAnswers = Array.isArray(rawCurrentAnswer)
35
+ ? rawCurrentAnswer
36
+ : rawCurrentAnswer && typeof rawCurrentAnswer === 'object'
37
+ ? Object.keys(rawCurrentAnswer)
38
+ .filter((key) => /^\d+$/.test(key))
39
+ .sort((a, b) => Number(a) - Number(b))
40
+ .map((key) => rawCurrentAnswer[key])
41
+ : typeof rawCurrentAnswer === 'string'
42
+ ? [rawCurrentAnswer]
43
+ : [];
44
+ // Defensive: wordBank should always be an array; guard against undefined.
45
+ const safeWordBank = useMemo(() => Array.isArray(wordBank) ? wordBank : [], [wordBank]);
46
+ // Derive exampleAnswer from wordBank.isExample flags as fallback for existing questions
47
+ const effectiveExampleAnswer = useMemo(() => {
48
+ if (exampleAnswer && exampleAnswer.length > 0)
49
+ return exampleAnswer;
50
+ const arr = [];
51
+ let hasExample = false;
52
+ for (let i = 0; i < safeWordBank.length; i++) {
53
+ if (safeWordBank[i]?.isExample) {
54
+ hasExample = true;
55
+ if (isReviewMode && correctAnswerTextMap) {
56
+ arr.push(correctAnswerTextMap[`${questionId}-blank-${i}`] || safeWordBank[i]?.word || '');
57
+ }
58
+ else {
59
+ arr.push(safeWordBank[i]?.word || '');
60
+ }
61
+ }
62
+ else {
63
+ arr.push(undefined);
64
+ }
65
+ }
66
+ return hasExample ? arr : undefined;
67
+ }, [exampleAnswer, safeWordBank, isReviewMode, correctAnswerTextMap, questionId]);
68
+ // Parse paragraph: split by {0}, {1}, etc.
69
+ const segments = paragraph.split(/(\{\d+\})/);
70
+ const isExampleBlank = (blankIndex) => Boolean(effectiveExampleAnswer?.[blankIndex]);
71
+ const assignWordToBlank = (blankIndex, word) => {
72
+ if (isReviewMode || isExampleBlank(blankIndex))
73
+ return;
74
+ const newAnswers = [...currentAnswers];
75
+ newAnswers[blankIndex] = word;
76
+ onAnswerChange(questionId, newAnswers);
77
+ };
78
+ const clearBlank = (blankIndex) => {
79
+ if (isReviewMode || isExampleBlank(blankIndex))
80
+ return;
81
+ const newAnswers = [...currentAnswers];
82
+ newAnswers[blankIndex] = '';
83
+ onAnswerChange(questionId, newAnswers);
84
+ };
85
+ const handleOptionSelect = (blankIndex, option) => {
86
+ if (!option) {
87
+ clearBlank(blankIndex);
88
+ return;
89
+ }
90
+ assignWordToBlank(blankIndex, option);
91
+ };
92
+ const usedWords = new Set(currentAnswers.filter(Boolean));
93
+ if (viewMode === 'EMAIL') {
94
+ return (_jsxs("div", { className: `grid h-full min-h-0 grid-cols-1 gap-4 sm:gap-6 ${!isReviewMode ? 'lg:grid-cols-2' : ''}`, children: [_jsxs("div", { className: "flex min-h-0 flex-col gap-4 overflow-y-auto p-3 sm:pb-6 sm:pr-6 sm:pt-6", children: [_jsx(CambridgeYlePartBanner, { partNumber: partNumber, questionCount: questionCount, partName: partName, isReviewMode: isReviewMode }), _jsx(CambridgeYleInstructionBanner, { instruction: instruction, isReviewMode: isReviewMode }), imageUrl && (_jsx("div", { className: "flex items-center justify-center", children: _jsx(ResolvedImage, { src: imageUrl, alt: "Illustration", className: "max-h-[300px] rounded-lg object-contain shadow-md" }) }))] }), _jsxs("div", { className: "flex min-h-0 flex-col gap-4 p-3 sm:gap-5 sm:pb-6 sm:pl-6 sm:pt-6", children: [_jsxs("div", { className: "shrink-0 overflow-hidden rounded-2xl border border-sky-100 bg-white shadow-[0_10px_30px_rgba(15,23,42,0.06)]", children: [_jsx("div", { className: "bg-gradient-to-r from-sky-100 via-blue-50 to-sky-100 px-5 py-3", children: _jsx("span", { className: "text-[15px] font-extrabold uppercase tracking-[0.14em] text-sky-900", children: "Email" }) }), _jsxs("div", { className: "space-y-3 bg-gradient-to-b from-white to-slate-50/80 px-5 py-4", children: [_jsxs("div", { className: "grid grid-cols-[64px_1fr] items-center gap-4", children: [_jsx("span", { className: "text-[15px] font-semibold text-slate-700", children: "From:" }), _jsx("div", { className: "rounded-xl border border-slate-200 bg-white px-4 py-2.5 text-base font-medium leading-6 text-slate-800 shadow-[inset_0_1px_2px_rgba(15,23,42,0.04)]", children: emailFrom || '—' })] }), _jsxs("div", { className: "grid grid-cols-[64px_1fr] items-center gap-4", children: [_jsx("span", { className: "text-[15px] font-semibold text-slate-700", children: "To:" }), _jsx("div", { className: "rounded-xl border border-slate-200 bg-white px-4 py-2.5 text-base font-medium leading-6 text-slate-800 shadow-[inset_0_1px_2px_rgba(15,23,42,0.04)]", children: emailTo || '—' })] })] })] }), _jsx("div", { className: cn('custom-scrollbar flex-1 min-h-0 overflow-y-auto rounded-2xl border border-slate-200 bg-white p-5 pr-3 text-base leading-loose text-gray-800 shadow-[0_8px_24px_rgba(15,23,42,0.04)]', !isReviewMode && 'whitespace-pre-line'), children: segments.map((segment, idx) => {
95
+ const blankMatch = segment.match(/^\{(\d+)\}$/);
96
+ if (!blankMatch)
97
+ return _jsx("span", { children: segment }, idx);
98
+ const blankIndex = parseInt(blankMatch[1], 10);
99
+ const studentAnswer = currentAnswers[blankIndex] || '';
100
+ if (isReviewMode && correctAnswerTextMap) {
101
+ const correctAnswer = correctAnswerTextMap[`${questionId}-blank-${blankIndex}`] || '';
102
+ const isEmpty = !studentAnswer;
103
+ const isCorrect = !isEmpty && studentAnswer.trim().toLowerCase() === correctAnswer.trim().toLowerCase();
104
+ if (isEmpty) {
105
+ return (_jsx("span", { className: "mx-1 inline-block min-w-[80px] border-b-2 border-blue-400 px-1 py-0.5 text-center text-blue-600", children: correctAnswer || '___' }, idx));
106
+ }
107
+ if (isCorrect) {
108
+ return (_jsx("span", { className: "mx-1 inline-block min-w-[80px] border-b-2 border-green-500 px-1 py-0.5 text-center text-green-700", children: studentAnswer }, idx));
109
+ }
110
+ return (_jsxs("span", { className: "mx-1 inline-block min-w-[80px] border-b-2 border-red-400 px-1 py-0.5 text-center", children: [_jsx("span", { className: "text-red-500 line-through", children: studentAnswer }), correctAnswer && _jsxs("span", { className: "ml-1 text-green-600", children: ["\u2192 ", correctAnswer] })] }, idx));
111
+ }
112
+ return (_jsxs("span", { className: "mx-0.5 inline-flex items-baseline gap-0.5 align-baseline", children: [_jsxs("span", { className: "text-[11px] font-bold text-blue-400", children: ["(", blankIndex + 1, ")"] }), _jsx("input", { type: "text", value: studentAnswer, onChange: (e) => {
113
+ const newAnswers = [...currentAnswers];
114
+ newAnswers[blankIndex] = e.target.value;
115
+ onAnswerChange(questionId, newAnswers);
116
+ }, disabled: isReviewMode, placeholder: "\u00B7\u00B7\u00B7", style: { width: `${Math.max(3, (studentAnswer || '').length + 1)}ch` }, className: cn('inline-block min-w-[3ch] max-w-[12ch] border-b-2 bg-transparent px-0.5 py-0 text-center text-sm font-medium outline-none transition-colors', studentAnswer
117
+ ? 'border-emerald-400 text-emerald-700'
118
+ : 'border-slate-300 text-slate-500', 'focus:border-blue-500 focus:text-blue-700', 'disabled:opacity-60') })] }, idx));
119
+ }) })] })] }));
120
+ }
121
+ return (_jsxs("div", { className: `grid h-full min-h-0 grid-cols-1 gap-4 sm:gap-6 ${!isReviewMode ? 'lg:grid-cols-2' : ''}`, children: [_jsxs("div", { className: "flex min-h-0 flex-col gap-4 overflow-y-auto p-3 sm:pb-6 sm:pr-6 sm:pt-6", children: [_jsx(CambridgeYlePartBanner, { partNumber: partNumber, questionCount: questionCount, partName: partName, isReviewMode: isReviewMode }), _jsx(CambridgeYleInstructionBanner, { instruction: instruction, isReviewMode: isReviewMode }), title && _jsx("h2", { className: "shrink-0 text-center text-2xl font-bold uppercase text-coral-500", children: title }), imageUrl && (_jsx("div", { className: "flex items-center justify-center", children: _jsx(ResolvedImage, { src: imageUrl, alt: "Illustration", className: "max-h-[400px] w-full max-w-xl rounded-lg object-contain shadow-md" }) })), viewMode === 'WITH_WORD_BANK' && (_jsxs("div", { className: "shrink-0 rounded-2xl border border-sky-100 bg-slate-50/50 p-4 sm:p-5 shadow-sm", children: [_jsxs("div", { className: "mb-4 flex items-center justify-center gap-2", children: [_jsx("div", { className: "h-px flex-1 bg-gradient-to-r from-transparent to-sky-200" }), _jsx("h3", { className: "text-sm font-extrabold uppercase tracking-widest text-sky-800", children: "WORD BANK (Reference)" }), _jsx("div", { className: "h-px flex-1 bg-gradient-to-l from-transparent to-sky-200" })] }), _jsx("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-2 xl:grid-cols-3", children: safeWordBank.map((item, idx) => {
122
+ const isUsed = usedWords.has(item.word);
123
+ const isExample = item.isExample || effectiveExampleAnswer?.includes(item.word) || false;
124
+ return (_jsx(WordReferenceTile, { item: item, isUsed: isUsed, isExample: isExample }, `${item.word}-${idx}`));
125
+ }) })] }))] }), _jsx("div", { className: "custom-scrollbar flex min-h-0 flex-col gap-4 overflow-y-auto p-3 sm:gap-5 sm:pb-6 sm:pl-6 sm:pt-6", children: _jsx("div", { className: cn('shrink-0 rounded-2xl border border-slate-200 bg-white p-5 pr-3 text-base leading-loose text-gray-800 shadow-[0_8px_24px_rgba(15,23,42,0.04)]', !isReviewMode && 'whitespace-pre-line'), children: segments.map((segment, idx) => {
126
+ const blankMatch = segment.match(/^\{(\d+)\}$/);
127
+ if (!blankMatch)
128
+ return _jsx("span", { children: segment }, idx);
129
+ const blankIndex = parseInt(blankMatch[1], 10);
130
+ const answer = currentAnswers[blankIndex] || '';
131
+ const exampleBlankValue = effectiveExampleAnswer?.[blankIndex];
132
+ const isExampleSlot = Boolean(exampleBlankValue);
133
+ const displayValue = exampleBlankValue || answer;
134
+ // Review mode: show correct/incorrect feedback for non-example blanks
135
+ if (isReviewMode && !isExampleSlot && correctAnswerTextMap) {
136
+ const correctAnswer = correctAnswerTextMap[`${questionId}-blank-${blankIndex}`] || '';
137
+ const isEmpty = !answer;
138
+ const isCorrect = !isEmpty && answer.trim().toLowerCase() === correctAnswer.trim().toLowerCase();
139
+ if (isEmpty) {
140
+ return (_jsx("span", { className: "mx-1 inline-block min-w-[80px] border-b-2 border-blue-400 px-1 py-0.5 text-center text-blue-600", children: correctAnswer || '___' }, idx));
141
+ }
142
+ if (isCorrect) {
143
+ return (_jsx("span", { className: "mx-1 inline-block min-w-[80px] border-b-2 border-green-500 px-1 py-0.5 text-center text-green-700", children: answer }, idx));
144
+ }
145
+ return (_jsxs("span", { className: "mx-1 inline-block min-w-[80px] border-b-2 border-red-400 px-1 py-0.5 text-center", children: [_jsx("span", { className: "text-red-500 line-through", children: answer }), correctAnswer && _jsxs("span", { className: "ml-1 text-green-600", children: ["\u2192 ", correctAnswer] })] }, idx));
146
+ }
147
+ if (viewMode === 'WITH_OPTIONS') {
148
+ if (isExampleSlot) {
149
+ return (_jsx("span", { className: "mx-1 inline-block min-w-[60px] border-b-2 border-amber-400 bg-amber-50 px-2 py-0.5 text-center text-sm font-bold text-amber-700 align-baseline", children: exampleBlankValue || '___' }, idx));
150
+ }
151
+ const blankOptions = safeWordBank[blankIndex]?.options || [];
152
+ return (_jsxs("select", { value: answer, onChange: (e) => handleOptionSelect(blankIndex, e.target.value), disabled: isReviewMode, className: "mx-1 inline-block min-w-[100px] rounded border-2 border-blue-300 bg-blue-50 px-2 py-0.5 text-center text-sm font-medium outline-none focus:border-blue-500", children: [_jsx("option", { value: "", children: "___" }), blankOptions.map((opt) => (_jsx("option", { value: opt, children: opt }, opt)))] }, idx));
153
+ }
154
+ if (viewMode === 'DEFAULT') {
155
+ const inputValue = isExampleSlot ? String(exampleBlankValue || '') : (currentAnswers[blankIndex] || '');
156
+ return (_jsxs("span", { className: "mx-0.5 inline-flex items-baseline gap-0.5 align-baseline", children: [_jsxs("span", { className: "text-[11px] font-bold text-blue-400", children: ["(", blankIndex + 1, ")"] }), _jsx("input", { type: "text", value: inputValue, onChange: (e) => {
157
+ if (isExampleSlot)
158
+ return;
159
+ const newAnswers = [...currentAnswers];
160
+ newAnswers[blankIndex] = e.target.value;
161
+ onAnswerChange(questionId, newAnswers);
162
+ }, disabled: isReviewMode || !!isExampleSlot, placeholder: "\u00B7\u00B7\u00B7\u00B7\u00B7\u00B7\u00B7\u00B7\u00B7", style: { width: `${Math.max(10, inputValue.length + 1)}ch` }, className: cn('inline-block min-w-[10ch] max-w-[12ch] border-b-2 bg-transparent px-0.5 py-0 text-center text-sm font-medium outline-none transition-colors', inputValue
163
+ ? 'border-emerald-400 text-emerald-700'
164
+ : 'border-slate-300 text-slate-500', 'focus:border-blue-500 focus:text-blue-700', 'disabled:opacity-60') })] }, idx));
165
+ }
166
+ if (viewMode === 'WITH_WORD_BANK') {
167
+ return (_jsx(InputBlank, { blankIndex: blankIndex, value: displayValue || '', isExampleBlank: isExampleSlot, isReviewMode: isReviewMode, onInputChange: (value) => assignWordToBlank(blankIndex, value), onClear: () => clearBlank(blankIndex) }, idx));
168
+ }
169
+ return _jsx("span", { children: segment }, idx);
170
+ }) }) })] }));
171
+ }
@@ -1 +1,198 @@
1
- "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{useRef as t,useEffect as n,useMemo as a,useCallback as s,memo as l}from"react";import{ResolvedImage as i}from"../../../../../components/common/ResolvedImage";import o from"../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import d from"../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";import c from"../../../../../components/themes/cambridge-yle/CambridgeYleAudioPlayer";import{cn as m}from"../../../../../shared/lib/utils";function u(e,r){const t=e.trim();return r?t:t.toLowerCase()}const p={},b=l(function({question:s,initialAnswers:l,onBlankChange:o,isReviewMode:d}){const c=t(null),p=t(o);p.current=o;const b=t(!1),g=t(l),x=a(()=>s.content.replace(/\{([\w\d]+)\}/g,(e,r)=>`\n <span class="structured-blank-wrapper" data-blank-wrapper="${r}">\n <input type="text" data-blank-key="${r}" placeholder="" class="structured-blank-input" />\n <span class="structured-blank-hint" data-blank-hint="${r}"></span>\n </span>\n `),[s.content]);return n(()=>{const e=c.current;if(!e)return;b.current||(e.innerHTML=x,b.current=!0);const r=e.querySelectorAll("input.structured-blank-input"),t=[];return r.forEach(r=>{const n=r.getAttribute("data-blank-key")||"",a=e.querySelector(`[data-blank-wrapper="${n}"]`),l=e.querySelector(`[data-blank-hint="${n}"]`),i=g.current[n]||"",o=s.answers[n]||"",c=i.trim().length>0,b=c&&u(i,s.caseSensitive)===u(o,s.caseSensitive),x=d&&!b&&o.trim().length>0;r.value=x?`${c?i:"(trống)"} → ${o}`:i,r.disabled=d,r.className=m("structured-blank-input inline-block min-w-[90px] max-w-[200px] rounded-t border-b-2 bg-slate-50/40 px-2 py-0.5 text-sm font-semibold outline-none transition-all align-baseline hover:bg-slate-100/60 focus:bg-teal-50/60 focus:ring-0",d?b?"border-emerald-500 bg-emerald-50 text-emerald-800 font-bold":c?"border-rose-500 bg-rose-50 text-rose-800 font-bold":"border-amber-500 bg-amber-50 text-amber-800 font-bold":"border-slate-400 text-slate-800 focus:border-teal-600"),a&&(a.className=m("structured-blank-wrapper inline-flex items-baseline align-baseline mx-1 my-0.5",d&&"mx-1 my-0.5")),l&&(l.textContent="",l.className="structured-blank-hint hidden");const h=e=>{const r=e.target;p.current(n,r.value)};r.addEventListener("input",h),t.push({el:r,handler:h})}),()=>{t.forEach(({el:e,handler:r})=>{e.removeEventListener("input",r)})}},[x,d]),r("div",{className:"space-y-3",children:[s.title&&e("h3",{className:"text-base font-semibold text-gray-800",children:s.title}),s.imageUrl&&e("div",{className:"flex justify-center",children:e("div",{className:"overflow-hidden rounded-lg border border-gray-200",children:e(i,{src:s.imageUrl,alt:s.title||"Question image",className:"max-h-48 w-auto object-contain"})})}),e("div",{ref:c,className:"structured-form-content rounded-lg border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4 text-sm leading-relaxed text-gray-800 [&_.structured-blank-wrapper]:inline-flex [&_.structured-blank-wrapper]:align-baseline [&_.structured-blank-wrapper]:items-baseline [&_.structured-blank-hint]:max-w-[180px]"}),d&&r("div",{className:"space-y-2 rounded-lg border border-slate-200 bg-slate-50 p-3",children:[r("div",{className:"flex flex-wrap gap-2 text-[11px] font-medium",children:[r("span",{className:"inline-flex items-center gap-1 rounded-full bg-green-100 px-2 py-1 text-green-700",children:[e("span",{className:"h-2 w-2 rounded-full bg-green-500"}),"Đúng"]}),r("span",{className:"inline-flex items-center gap-1 rounded-full bg-red-100 px-2 py-1 text-red-700",children:[e("span",{className:"h-2 w-2 rounded-full bg-red-500"}),"Sai"]}),r("span",{className:"inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-1 text-amber-700",children:[e("span",{className:"h-2 w-2 rounded-full bg-amber-500"}),"Chưa trả lời"]})]}),r("div",{children:[e("span",{className:"text-[10px] font-medium uppercase tracking-wider text-slate-500",children:"Đáp án đúng"}),e("div",{className:"mt-1 flex flex-wrap gap-1.5",children:Object.entries(s.answers).map(([e,t])=>r("span",{className:"rounded border border-green-300 bg-green-100 px-2 py-0.5 text-xs font-semibold text-green-800",children:[e,": ",t]},e))})]})]})]})});export function MoversWordFillStructuredFormRenderer({partNumber:n,partName:l,questionCount:m,instruction:u,audioUrl:g,groupTitle:x,groupContent:h,groupImageUrl:f,questions:w,answers:v,onAnswerChange:N,isReviewMode:y=!1}){const k=t(v);k.current=v;const C=t(N);C.current=N;const q=s((e,r,t)=>{const n=k.current[e]||{};C.current(e,{...n,[r]:t})},[]),j=a(()=>{const e={};return w.forEach(r=>{e[r.id]=(e,t)=>q(r.id,e,t)}),e},[w,q]);return r("div",{className:"flex flex-col h-full gap-4 sm:gap-6 overflow-y-auto custom-scrollbar",children:[r("div",{className:"flex flex-col gap-4 p-3 sm:px-6 sm:pt-2",children:[e(o,{partNumber:n,questionCount:m,partName:l,isReviewMode:y}),e(d,{instruction:u,isReviewMode:y}),x&&e("h2",{className:"text-center text-xl font-bold uppercase text-coral-500",children:x}),g&&e(c,{audioSrc:g,compact:!0}),f&&e("div",{className:"flex items-center justify-center",children:e(i,{src:f,alt:x||"Group image",className:"max-h-[300px] rounded-lg object-contain shadow-md"})}),h&&e("div",{className:"article-content rounded-lg bg-amber-50 p-4 text-sm leading-relaxed text-gray-700 max-h-[500px] overflow-y-auto pr-2 custom-scrollbar",dangerouslySetInnerHTML:{__html:h}})]}),w.length>0&&e("div",{className:"px-3 sm:px-6",children:e("hr",{className:"border-t border-slate-200/60 my-1"})}),e("div",{className:"grid grid-cols-1 gap-4 sm:gap-5 p-3 sm:px-6 sm:pb-6",children:w.map(t=>r("div",{id:`question-${t.id}`,className:"rounded-xl border border-gray-200 bg-white p-4 shadow-sm transition-all hover:border-indigo-200 hover:shadow-md",children:[e("div",{className:"mb-2 flex items-center gap-2",children:e("span",{className:"flex h-7 w-7 items-center justify-center rounded-full bg-gradient-to-br from-indigo-500 to-violet-600 text-xs font-bold text-white shadow-sm",children:t.questionNumber})}),e(b,{question:t,initialAnswers:v[t.id]||p,onBlankChange:j[t.id],isReviewMode:y})]},t.id))})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useRef, useEffect, useMemo, useCallback, memo } from 'react';
4
+ import { ResolvedImage } from '../../../../../components/common/ResolvedImage';
5
+ import CambridgeYlePartBanner from '../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner';
6
+ import CambridgeYleInstructionBanner from '../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner';
7
+ import CambridgeYleAudioPlayer from '../../../../../components/themes/cambridge-yle/CambridgeYleAudioPlayer';
8
+ import { cn } from '../../../../../shared/lib/utils';
9
+ /**
10
+ * Replace `{key}` placeholders with inline blank controls.
11
+ * WITH_OPTIONS blanks become <select>; free-type stay <input>.
12
+ */
13
+ function optionsForBlankKey(wordBank, key) {
14
+ if (!wordBank?.length)
15
+ return null;
16
+ const asNum = Number(key);
17
+ const entry = Number.isFinite(asNum) && asNum >= 0 && asNum < wordBank.length
18
+ ? wordBank[asNum]
19
+ : null;
20
+ if (entry == null || !('options' in entry) || !entry.options?.length)
21
+ return null;
22
+ return entry.options.length >= 2 ? entry.options : null;
23
+ }
24
+ function escapeHtmlAttr(value) {
25
+ return value
26
+ .replace(/&/g, '&' + 'amp;')
27
+ .replace(/"/g, '&' + 'quot;')
28
+ .replace(/</g, '&' + 'lt;')
29
+ .replace(/>/g, '&' + 'gt;');
30
+ }
31
+ function buildHtmlWithInputPlaceholders(html, wordBank) {
32
+ return html.replace(/\{([\w\d]+)\}/g, (_match, key) => {
33
+ const options = optionsForBlankKey(wordBank, key);
34
+ if (options) {
35
+ const optsHtml = [
36
+ '<option value="">—</option>',
37
+ ...options.map((opt) => `<option value="${escapeHtmlAttr(opt)}">${escapeHtmlAttr(opt)}</option>`),
38
+ ].join('');
39
+ return `
40
+ <span class="structured-blank-wrapper" data-blank-wrapper="${key}">
41
+ <select data-blank-key="${key}" class="structured-blank-input structured-blank-select">${optsHtml}</select>
42
+ <span class="structured-blank-hint" data-blank-hint="${key}"></span>
43
+ </span>
44
+ `;
45
+ }
46
+ return `
47
+ <span class="structured-blank-wrapper" data-blank-wrapper="${key}">
48
+ <input type="text" data-blank-key="${key}" placeholder="" class="structured-blank-input" />
49
+ <span class="structured-blank-hint" data-blank-hint="${key}"></span>
50
+ </span>
51
+ `;
52
+ });
53
+ }
54
+ function normalizeStructuredAnswer(value, caseSensitive) {
55
+ const normalized = value
56
+ .trim()
57
+ .replace(/[\p{P}]/gu, '')
58
+ .replace(/\s+/g, ' ')
59
+ .trim();
60
+ return caseSensitive ? normalized : normalized.toLowerCase();
61
+ }
62
+ // Stable empty object to avoid new {} on every render (breaks React.memo)
63
+ const emptyObj = {};
64
+ /**
65
+ * StructuredFormQuestion renders HTML content with inline input fields.
66
+ *
67
+ * CRITICAL FIX: We do NOT use dangerouslySetInnerHTML (which gets re-applied
68
+ * on every React re-render, destroying DOM inputs and losing focus).
69
+ * Instead, we set innerHTML imperatively via a ref exactly ONCE on mount,
70
+ * completely bypassing React's reconciliation for the form content.
71
+ */
72
+ const StructuredFormQuestion = memo(function StructuredFormQuestion({ question, initialAnswers, onBlankChange, isReviewMode, }) {
73
+ const containerRef = useRef(null);
74
+ const onBlankChangeRef = useRef(onBlankChange);
75
+ onBlankChangeRef.current = onBlankChange;
76
+ const mountedRef = useRef(false);
77
+ // Store initial answers in a ref so they're available on mount but don't trigger re-renders
78
+ const initialAnswersRef = useRef(initialAnswers);
79
+ const processedHtml = useMemo(() => buildHtmlWithInputPlaceholders(question.content, question.wordBank), [question.content, question.wordBank]);
80
+ // Set innerHTML ONCE via ref and attach event listeners.
81
+ // This completely bypasses React's reconciliation so inputs are never
82
+ // destroyed by re-renders.
83
+ useEffect(() => {
84
+ const container = containerRef.current;
85
+ if (!container)
86
+ return;
87
+ // Only set innerHTML on first mount or if HTML content actually changed
88
+ if (!mountedRef.current) {
89
+ container.innerHTML = processedHtml;
90
+ mountedRef.current = true;
91
+ }
92
+ const inputs = container.querySelectorAll('input.structured-blank-input, select.structured-blank-input');
93
+ const handlers = [];
94
+ inputs.forEach((input) => {
95
+ const key = input.getAttribute('data-blank-key') || '';
96
+ const wrapper = container.querySelector(`[data-blank-wrapper="${key}"]`);
97
+ const hint = container.querySelector(`[data-blank-hint="${key}"]`);
98
+ const userAnswer = initialAnswersRef.current[key] || '';
99
+ const correctAnswer = question.answers[key] || '';
100
+ const hasUserAnswer = userAnswer.trim().length > 0;
101
+ const isCorrect = hasUserAnswer
102
+ && normalizeStructuredAnswer(userAnswer, question.caseSensitive)
103
+ === normalizeStructuredAnswer(correctAnswer, question.caseSensitive);
104
+ // Restore saved answer value on mount
105
+ const shouldReplaceInputWithReviewText = isReviewMode && !isCorrect && correctAnswer.trim().length > 0;
106
+ input.value = shouldReplaceInputWithReviewText
107
+ ? `${hasUserAnswer ? userAnswer : '(trống)'} → ${correctAnswer}`
108
+ : userAnswer;
109
+ input.disabled = isReviewMode;
110
+ const isSelect = input.tagName === 'SELECT';
111
+ // Set classes from blankStyle (letter = square box)
112
+ const blankStyle = question.blankStyle || 'word';
113
+ const isLetter = blankStyle === 'letter';
114
+ const isPhrase = blankStyle === 'phrase';
115
+ if (isLetter && !isSelect && 'maxLength' in input) {
116
+ input.maxLength = Math.max(1, (correctAnswer || ' ').trim().length || 1);
117
+ }
118
+ input.className = cn('structured-blank-input inline-block bg-slate-50/40 text-sm font-semibold outline-none transition-all align-baseline hover:bg-slate-100/60 focus:bg-teal-50/60 focus:ring-0', isSelect
119
+ ? 'min-w-[5.5rem] max-w-[10rem] rounded border px-2 py-1 text-center'
120
+ : isLetter
121
+ ? 'h-7 w-7 rounded border-2 px-0 text-center uppercase'
122
+ : isPhrase
123
+ ? 'min-w-[9rem] max-w-[18rem] w-[14ch] rounded-t border-b-2 px-2 py-0.5 text-center'
124
+ : // blankStyle=word — short single-token field
125
+ 'min-w-[4.5rem] max-w-[7.5rem] w-[8ch] rounded-t border-b-2 px-1 py-0.5 text-center', isReviewMode
126
+ ? isCorrect
127
+ ? 'border-emerald-500 bg-emerald-50 text-emerald-800 font-bold'
128
+ : hasUserAnswer
129
+ ? 'border-rose-500 bg-rose-50 text-rose-800 font-bold'
130
+ : 'border-amber-500 bg-amber-50 text-amber-800 font-bold'
131
+ : 'border-slate-400 text-slate-800 focus:border-teal-600');
132
+ if (!isSelect && !isLetter && 'size' in input) {
133
+ input.size = isPhrase ? 18 : 10;
134
+ }
135
+ // letter inline box — host FE may not scan Tailwind h-7/w-7
136
+ if (isLetter) {
137
+ input.style.display = 'inline-block';
138
+ input.style.width = '1.75rem';
139
+ input.style.height = '1.75rem';
140
+ input.style.minWidth = '1.75rem';
141
+ input.style.maxWidth = '1.75rem';
142
+ input.style.padding = '0';
143
+ input.style.margin = '0 2px';
144
+ input.style.borderWidth = '2px';
145
+ input.style.borderStyle = 'solid';
146
+ input.style.borderRadius = '4px';
147
+ input.style.textAlign = 'center';
148
+ input.style.fontWeight = '700';
149
+ input.style.textTransform = 'uppercase';
150
+ input.style.boxSizing = 'border-box';
151
+ input.style.verticalAlign = 'baseline';
152
+ }
153
+ if (wrapper) {
154
+ wrapper.className = cn('structured-blank-wrapper inline-flex items-baseline align-baseline mx-1 my-0.5', isReviewMode && 'mx-1 my-0.5');
155
+ }
156
+ if (hint) {
157
+ hint.textContent = '';
158
+ hint.className = 'structured-blank-hint hidden';
159
+ }
160
+ const handler = (e) => {
161
+ const target = e.target;
162
+ onBlankChangeRef.current(key, target.value);
163
+ };
164
+ const eventName = isSelect ? 'change' : 'input';
165
+ input.addEventListener(eventName, handler);
166
+ handlers.push({ el: input, handler, eventName });
167
+ });
168
+ return () => {
169
+ handlers.forEach(({ el, handler, eventName }) => {
170
+ el.removeEventListener(eventName, handler);
171
+ });
172
+ };
173
+ // eslint-disable-next-line react-hooks/exhaustive-deps
174
+ }, [processedHtml, isReviewMode]);
175
+ return (_jsxs("div", { className: "space-y-3", children: [question.title && (_jsx("h3", { className: "text-base font-semibold text-gray-800", children: question.title })), question.imageUrl && (_jsx("div", { className: "flex justify-center", children: _jsx("div", { className: "overflow-hidden rounded-lg border border-gray-200", children: _jsx(ResolvedImage, { src: question.imageUrl, alt: question.title || 'Question image', className: "max-h-48 w-auto object-contain" }) }) })), _jsx("div", { ref: containerRef, className: cn('structured-form-content rounded-lg border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4 text-sm leading-relaxed text-gray-800', '[&_.structured-blank-wrapper]:inline-flex [&_.structured-blank-wrapper]:align-baseline [&_.structured-blank-wrapper]:items-baseline [&_.structured-blank-hint]:max-w-[180px]',
176
+ // Conversation: speaker column hug name (not equal 50/50 split)
177
+ '[&_table.dialogue]:w-full [&_table.dialogue]:border-collapse [&_table.dialogue]:table-fixed', '[&_table.dialogue_td.speaker]:w-[1%] [&_table.dialogue_td.speaker]:whitespace-nowrap', '[&_table.dialogue_td.speaker]:align-top [&_table.dialogue_td.speaker]:pr-3', '[&_table.dialogue_td.speaker]:font-semibold [&_table.dialogue_td.speaker]:text-slate-900', '[&_table.dialogue_td]:align-top [&_table.dialogue_td]:py-1.5') }), isReviewMode && (_jsxs("div", { className: "space-y-2 rounded-lg border border-slate-200 bg-slate-50 p-3", children: [_jsxs("div", { className: "flex flex-wrap gap-2 text-[11px] font-medium", children: [_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-green-100 px-2 py-1 text-green-700", children: [_jsx("span", { className: "h-2 w-2 rounded-full bg-green-500" }), "\u0110\u00FAng"] }), _jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-red-100 px-2 py-1 text-red-700", children: [_jsx("span", { className: "h-2 w-2 rounded-full bg-red-500" }), "Sai"] }), _jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-1 text-amber-700", children: [_jsx("span", { className: "h-2 w-2 rounded-full bg-amber-500" }), "Ch\u01B0a tr\u1EA3 l\u1EDDi"] })] }), _jsxs("div", { children: [_jsx("span", { className: "text-[10px] font-medium uppercase tracking-wider text-slate-500", children: "\u0110\u00E1p \u00E1n \u0111\u00FAng" }), _jsx("div", { className: "mt-1 flex flex-wrap gap-1.5", children: Object.entries(question.answers).map(([key, val]) => (_jsxs("span", { className: "rounded border border-green-300 bg-green-100 px-2 py-0.5 text-xs font-semibold text-green-800", children: [key, ": ", val] }, key))) })] })] }))] }));
178
+ });
179
+ export function MoversWordFillStructuredFormRenderer({ partNumber, partName, questionCount, instruction, audioUrl, groupTitle, groupContent, groupImageUrl, questions, answers, onAnswerChange, isReviewMode = false, }) {
180
+ const answersRef = useRef(answers);
181
+ answersRef.current = answers;
182
+ const onAnswerChangeRef = useRef(onAnswerChange);
183
+ onAnswerChangeRef.current = onAnswerChange;
184
+ // Stable callback — never changes identity, so child memo is never broken
185
+ const handleBlankChange = useCallback((questionId, key, value) => {
186
+ const currentAnswer = answersRef.current[questionId] || {};
187
+ onAnswerChangeRef.current(questionId, { ...currentAnswer, [key]: value });
188
+ }, []);
189
+ // Stable per-question callbacks — memoized so React.memo children won't re-render
190
+ const blankChangeCallbacks = useMemo(() => {
191
+ const map = {};
192
+ questions.forEach((q) => {
193
+ map[q.id] = (key, value) => handleBlankChange(q.id, key, value);
194
+ });
195
+ return map;
196
+ }, [questions, handleBlankChange]);
197
+ return (_jsxs("div", { className: "flex flex-col h-full gap-4 sm:gap-6 overflow-y-auto custom-scrollbar", children: [_jsxs("div", { className: "flex flex-col gap-4 p-3 sm:px-6 sm:pt-2", children: [_jsx(CambridgeYlePartBanner, { partNumber: partNumber, questionCount: questionCount, partName: partName, isReviewMode: isReviewMode }), _jsx(CambridgeYleInstructionBanner, { instruction: instruction, isReviewMode: isReviewMode }), groupTitle && (_jsx("h2", { className: "text-center text-xl font-bold uppercase text-coral-500", children: groupTitle })), audioUrl && _jsx(CambridgeYleAudioPlayer, { audioSrc: audioUrl, compact: true }), groupImageUrl && (_jsx("div", { className: "flex items-center justify-center", children: _jsx(ResolvedImage, { src: groupImageUrl, alt: groupTitle || 'Group image', className: "max-h-[300px] rounded-lg object-contain shadow-md" }) })), groupContent && (_jsx("div", { className: "article-content rounded-lg bg-amber-50 p-4 text-sm leading-relaxed text-gray-700 max-h-[500px] overflow-y-auto pr-2 custom-scrollbar", dangerouslySetInnerHTML: { __html: groupContent } }))] }), questions.length > 0 && (_jsx("div", { className: "px-3 sm:px-6", children: _jsx("hr", { className: "border-t border-slate-200/60 my-1" }) })), _jsx("div", { className: "grid grid-cols-1 gap-4 sm:gap-5 p-3 sm:px-6 sm:pb-6", children: questions.map((q) => (_jsxs("div", { id: `question-${q.id}`, className: "rounded-xl border border-gray-200 bg-white p-4 shadow-sm transition-all hover:border-indigo-200 hover:shadow-md", children: [_jsx("div", { className: "mb-2 flex items-center gap-2", children: _jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-full bg-gradient-to-br from-indigo-500 to-violet-600 text-xs font-bold text-white shadow-sm", children: q.questionNumber }) }), _jsx(StructuredFormQuestion, { question: q, initialAnswers: answers[q.id] || emptyObj, onBlankChange: blankChangeCallbacks[q.id], isReviewMode: isReviewMode })] }, q.id))) })] }));
198
+ }
@@ -0,0 +1,15 @@
1
+ import React from 'react';
2
+ import type { WordOrderAndMatchGroupQuestionItem } from '../../utils/question-transformers';
3
+ interface MoversWordOrderAndMatchGroupRendererProps {
4
+ partNumber: number;
5
+ partName?: string;
6
+ questionCount: number;
7
+ instruction: string;
8
+ matchOptions: string[];
9
+ questions: WordOrderAndMatchGroupQuestionItem[];
10
+ answers: Record<string, any>;
11
+ onAnswerChange: (questionId: string, value: any) => void;
12
+ isReviewMode?: boolean;
13
+ }
14
+ export declare function MoversWordOrderAndMatchGroupRenderer({ partNumber, partName, questionCount, instruction, matchOptions, questions, answers, onAnswerChange, isReviewMode, }: MoversWordOrderAndMatchGroupRendererProps): React.JSX.Element;
15
+ export {};
@@ -0,0 +1,36 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import CambridgeYlePartBanner from '../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner';
4
+ import CambridgeYleInstructionBanner from '../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner';
5
+ import { WordOrderAndMatchGroupClient } from '../../../../questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient';
6
+ export function MoversWordOrderAndMatchGroupRenderer({ partNumber, partName, questionCount, instruction, matchOptions, questions, answers, onAnswerChange, isReviewMode = false, }) {
7
+ const userAnswers = questions.map((q) => {
8
+ const raw = answers[q.id];
9
+ if (raw && typeof raw === 'object') {
10
+ return {
11
+ orderedPhrase: typeof raw.orderedPhrase === 'string' ? raw.orderedPhrase : '',
12
+ matchLabel: typeof raw.matchLabel === 'string' ? raw.matchLabel : '',
13
+ };
14
+ }
15
+ return undefined;
16
+ });
17
+ const handleItemAnswerChange = (index, answer) => {
18
+ const targetQuestion = questions[index];
19
+ if (targetQuestion) {
20
+ onAnswerChange(targetQuestion.id, answer);
21
+ }
22
+ };
23
+ const questionData = {
24
+ instruction,
25
+ matchOptions,
26
+ items: questions.map((q) => ({
27
+ scrambledWords: q.scrambledWords,
28
+ orderedPhrase: q.orderedPhrase,
29
+ matchLabel: q.matchLabel,
30
+ isExample: q.isExample,
31
+ points: q.points,
32
+ questionNumber: q.questionNumber,
33
+ })),
34
+ };
35
+ return (_jsxs("div", { className: "w-full space-y-4", children: [_jsx(CambridgeYlePartBanner, { partNumber: partNumber, partName: partName, questionCount: questionCount }), _jsx(CambridgeYleInstructionBanner, { instruction: instruction }), _jsx("div", { className: "mt-4", children: _jsx(WordOrderAndMatchGroupClient, { questionData: questionData, isReviewMode: isReviewMode, userAnswers: userAnswers, onAnswerChange: handleItemAnswerChange }) })] }));
36
+ }
@@ -1 +1,92 @@
1
- "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{ResolvedImage as t}from"../../../../../components/common/ResolvedImage";import a from"../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import s from"../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";import{CheckCircle2 as l,XCircle as o}from"lucide-react";export function MoversWordOrderingRenderer({partNumber:d,partName:i,questionCount:n,instruction:m,questions:c,answers:b,onAnswerChange:p,isReviewMode:x=!1,rawCorrectAnswerMap:g={},isCorrectMap:u={}}){return r("div",{className:"flex flex-col h-full gap-4 sm:gap-6 overflow-y-auto custom-scrollbar",children:[r("div",{className:"flex flex-col gap-2 sm:gap-3 p-3 sm:px-6 sm:pt-2",children:[e(a,{partNumber:d,questionCount:n,partName:i,isReviewMode:x}),e(s,{instruction:m,isReviewMode:x})]}),e("div",{className:"p-3 sm:pb-6 sm:px-6 grid grid-cols-1 gap-6 sm:grid-cols-2",children:c.map(a=>{const s=a.isExample?a.exampleAnswer:b[a.id],d=Array.isArray(s)?s:[],i=x||a.isExample,n=e=>e.trim().replace(/[.,?!]+$/,"").toLowerCase(),m=new Set;d.forEach(e=>{const r=a.words.findIndex((r,t)=>n(r)===n(e)&&!m.has(t));-1!==r&&m.add(r)});const c=x&&!a.isExample&&!0===u[a.id],f=x&&!a.isExample&&!1===u[a.id],h=a.isExample?"border-amber-200 bg-white/90 shadow-inner shadow-amber-100/60":c?"border-emerald-300 bg-emerald-50/80":f?"border-red-300 bg-red-50/80":"border-slate-100 bg-slate-50",w=a.isExample?"text-amber-600":c?"text-emerald-600":f?"text-red-500":"text-slate-400",v=a.isExample?"border-amber-400 bg-amber-100 text-amber-800":c?"border-emerald-400 bg-emerald-100 text-emerald-700":f?"border-red-400 bg-red-100 text-red-700":"border-emerald-400 bg-emerald-50 text-emerald-700 hover:bg-emerald-100";return r("div",{id:`question-${a.id}`,className:"relative flex flex-col items-center gap-4 rounded-2xl border-2 p-5 shadow-sm transition-all "+(a.isExample?"border-amber-300 bg-gradient-to-br from-amber-50 via-orange-50 to-white shadow-amber-100/70":c?"border-emerald-300 bg-gradient-to-br from-emerald-50/60 via-white to-white shadow-emerald-100/50":f?"border-red-200 bg-gradient-to-br from-red-50/40 via-white to-white shadow-red-100/40":"border-slate-200 bg-white hover:border-blue-200 hover:shadow-md"),children:[r("div",{className:"mb-2 flex w-full items-center justify-between",children:[e("span",{className:"flex h-8 w-8 items-center justify-center rounded-full text-sm font-bold text-white shadow-sm "+(a.isExample?"bg-gradient-to-br from-amber-400 to-orange-500":c?"bg-gradient-to-br from-emerald-400 to-green-500":f?"bg-gradient-to-br from-red-400 to-rose-500":"bg-blue-500"),children:a.questionNumber}),a.isExample&&e("span",{className:"inline-flex items-center rounded-full border border-amber-300 bg-gradient-to-r from-amber-100 to-orange-100 px-3 py-1 text-xs font-extrabold uppercase tracking-[0.16em] text-amber-700 shadow-sm",children:"Example"}),c&&r("span",{className:"inline-flex items-center gap-1 rounded-full border border-emerald-300 bg-gradient-to-r from-emerald-100 to-green-100 px-3 py-1 text-xs font-extrabold uppercase tracking-[0.16em] text-emerald-700 shadow-sm",children:[e(l,{className:"h-3.5 w-3.5"}),"Đúng"]}),f&&r("span",{className:"inline-flex items-center gap-1 rounded-full border border-red-300 bg-gradient-to-r from-red-100 to-rose-100 px-3 py-1 text-xs font-extrabold uppercase tracking-[0.16em] text-red-600 shadow-sm",children:[e(o,{className:"h-3.5 w-3.5"}),"Sai"]})]}),a.imageUrl&&e("div",{className:"mb-2 flex h-40 w-full justify-center sm:h-48",children:e(t,{src:a.imageUrl,alt:`Q${a.questionNumber}`,className:"h-full w-auto rounded-xl object-contain drop-shadow-sm"})}),e("div",{className:"flex flex-wrap justify-center gap-2",children:a.words.map((r,t)=>{const s=m.has(t);let l=r;if(a.isExample&&a.exampleAnswer){const e=a.exampleAnswer.find(e=>n(e)===n(r));e&&(l=e)}return e("button",{type:"button",disabled:i||s,onClick:()=>((e,r,t)=>{p(e,[...t,r])})(a.id,l,d),className:`flex min-w-[2.5rem] items-center justify-center rounded-lg border-2 px-3 py-2 text-lg font-bold transition-all ${s?"border-slate-200 bg-slate-100 text-slate-300":"border-blue-400 bg-blue-50 text-blue-700 shadow-sm hover:-translate-y-0.5 hover:bg-blue-100 hover:shadow"} ${i?"cursor-not-allowed":"cursor-pointer"}`,children:l},t)})}),r("div",{className:`mt-2 w-full rounded-xl border p-4 ${h}`,children:[e("div",{className:`mb-2 text-center text-xs font-medium uppercase tracking-wider ${w}`,children:"Your Answer"}),e("div",{className:"flex min-h-[3rem] flex-wrap items-center justify-center gap-2",children:d.length>0?d.map((r,t)=>e("button",{type:"button",disabled:i,onClick:()=>((e,r,t)=>{const a=[...t];a.splice(r,1),p(e,a)})(a.id,t,d),className:`flex min-w-[2.5rem] items-center justify-center rounded-lg border-2 px-3 py-2 text-lg font-bold shadow-sm transition-all disabled:cursor-not-allowed ${v}`,title:i?"":"Click to remove",children:r},t)):e("span",{className:"text-sm italic "+(a.isExample?"text-amber-500":"text-slate-400"),children:a.isExample?"Example answer is shown here":"Click letters above to answer"})})]}),f&&(()=>{const t=g[a.id]?.answer;return Array.isArray(t)&&0!==t.length?r("div",{className:"mt-3 w-full rounded-xl border-2 border-emerald-300 bg-gradient-to-br from-emerald-50 to-green-50 p-4",children:[r("div",{className:"mb-2 flex items-center justify-center gap-1.5 text-xs font-medium uppercase tracking-wider text-emerald-600",children:[e(l,{className:"h-3.5 w-3.5"}),"Đáp án đúng"]}),e("div",{className:"flex min-h-[3rem] flex-wrap items-center justify-center gap-2",children:t.map((r,t)=>e("div",{className:"flex min-w-[2.5rem] items-center justify-center rounded-lg border-2 border-emerald-400 bg-emerald-100 px-3 py-2 text-lg font-bold text-emerald-800 shadow-sm",children:r},t))})]}):null})()]},a.id)})})]})}
1
+ /**
2
+ * WORD_ORDERING Renderer - Click letters/words in order to form a word
3
+ * Used in Starters P7
4
+ */
5
+ 'use client';
6
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
+ import { ResolvedImage } from '../../../../../components/common/ResolvedImage';
8
+ import CambridgeYlePartBanner from '../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner';
9
+ import CambridgeYleInstructionBanner from '../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner';
10
+ import { CheckCircle2, XCircle } from 'lucide-react';
11
+ export function MoversWordOrderingRenderer({ partNumber, partName, questionCount, instruction, questions, answers, onAnswerChange, isReviewMode = false, rawCorrectAnswerMap = {}, isCorrectMap = {}, }) {
12
+ const handleWordClick = (questionId, word, currentAnswer) => {
13
+ // Add word to answer
14
+ onAnswerChange(questionId, [...currentAnswer, word]);
15
+ };
16
+ const handleRemoveWord = (questionId, index, currentAnswer) => {
17
+ // Remove word at index
18
+ const newAnswer = [...currentAnswer];
19
+ newAnswer.splice(index, 1);
20
+ onAnswerChange(questionId, newAnswer);
21
+ };
22
+ return (_jsxs("div", { className: "flex flex-col h-full gap-4 sm:gap-6 overflow-y-auto custom-scrollbar", children: [_jsxs("div", { className: "flex flex-col gap-2 sm:gap-3 p-3 sm:px-6 sm:pt-2", children: [_jsx(CambridgeYlePartBanner, { partNumber: partNumber, questionCount: questionCount, partName: partName, isReviewMode: isReviewMode }), _jsx(CambridgeYleInstructionBanner, { instruction: instruction, isReviewMode: isReviewMode })] }), _jsx("div", { className: "p-3 sm:pb-6 sm:px-6 grid grid-cols-1 gap-6 sm:grid-cols-2", children: questions.map((question) => {
23
+ // Defensive: answers[question.id] may be a non-array for legacy data.
24
+ // Coerce to array so `.forEach`/spread never crash.
25
+ const rawAnswer = question.isExample ? question.exampleAnswer : answers[question.id];
26
+ const currentAnswer = Array.isArray(rawAnswer) ? rawAnswer : [];
27
+ const isDisabled = isReviewMode || question.isExample;
28
+ const normalize = (str) => str.trim().replace(/[.,?!]+$/, '').toLowerCase();
29
+ const usedIndices = new Set();
30
+ currentAnswer.forEach((word) => {
31
+ const idx = question.words.findIndex((w, i) => normalize(w) === normalize(word) && !usedIndices.has(i));
32
+ if (idx !== -1)
33
+ usedIndices.add(idx);
34
+ });
35
+ // Determine answer status for review mode
36
+ const answerIsCorrect = isReviewMode && !question.isExample && isCorrectMap[question.id] === true;
37
+ const answerIsWrong = isReviewMode && !question.isExample && isCorrectMap[question.id] === false;
38
+ // Styling based on answer status
39
+ const answerSectionClasses = question.isExample
40
+ ? 'border-amber-200 bg-white/90 shadow-inner shadow-amber-100/60'
41
+ : answerIsCorrect
42
+ ? 'border-emerald-300 bg-emerald-50/80'
43
+ : answerIsWrong
44
+ ? 'border-red-300 bg-red-50/80'
45
+ : 'border-slate-100 bg-slate-50';
46
+ const answerLabelClasses = question.isExample
47
+ ? 'text-amber-600'
48
+ : answerIsCorrect
49
+ ? 'text-emerald-600'
50
+ : answerIsWrong
51
+ ? 'text-red-500'
52
+ : 'text-slate-400';
53
+ const answerChipClasses = question.isExample
54
+ ? 'border-amber-400 bg-amber-100 text-amber-800'
55
+ : answerIsCorrect
56
+ ? 'border-emerald-400 bg-emerald-100 text-emerald-700'
57
+ : answerIsWrong
58
+ ? 'border-red-400 bg-red-100 text-red-700'
59
+ : 'border-emerald-400 bg-emerald-50 text-emerald-700 hover:bg-emerald-100';
60
+ return (_jsxs("div", { id: `question-${question.id}`, className: `relative flex flex-col items-center gap-4 rounded-2xl border-2 p-5 shadow-sm transition-all ${question.isExample
61
+ ? 'border-amber-300 bg-gradient-to-br from-amber-50 via-orange-50 to-white shadow-amber-100/70'
62
+ : answerIsCorrect
63
+ ? 'border-emerald-300 bg-gradient-to-br from-emerald-50/60 via-white to-white shadow-emerald-100/50'
64
+ : answerIsWrong
65
+ ? 'border-red-200 bg-gradient-to-br from-red-50/40 via-white to-white shadow-red-100/40'
66
+ : 'border-slate-200 bg-white hover:border-blue-200 hover:shadow-md'}`, children: [_jsxs("div", { className: "mb-2 flex w-full items-center justify-between", children: [_jsx("span", { className: `flex h-8 w-8 items-center justify-center rounded-full text-sm font-bold text-white shadow-sm ${question.isExample
67
+ ? 'bg-gradient-to-br from-amber-400 to-orange-500'
68
+ : answerIsCorrect
69
+ ? 'bg-gradient-to-br from-emerald-400 to-green-500'
70
+ : answerIsWrong
71
+ ? 'bg-gradient-to-br from-red-400 to-rose-500'
72
+ : 'bg-blue-500'}`, children: question.questionNumber }), question.isExample && (_jsx("span", { className: "inline-flex items-center rounded-full border border-amber-300 bg-gradient-to-r from-amber-100 to-orange-100 px-3 py-1 text-xs font-extrabold uppercase tracking-[0.16em] text-amber-700 shadow-sm", children: "Example" })), answerIsCorrect && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full border border-emerald-300 bg-gradient-to-r from-emerald-100 to-green-100 px-3 py-1 text-xs font-extrabold uppercase tracking-[0.16em] text-emerald-700 shadow-sm", children: [_jsx(CheckCircle2, { className: "h-3.5 w-3.5" }), "\u0110\u00FAng"] })), answerIsWrong && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full border border-red-300 bg-gradient-to-r from-red-100 to-rose-100 px-3 py-1 text-xs font-extrabold uppercase tracking-[0.16em] text-red-600 shadow-sm", children: [_jsx(XCircle, { className: "h-3.5 w-3.5" }), "Sai"] }))] }), question.imageUrl && (_jsx("div", { className: "mb-2 flex h-40 w-full justify-center sm:h-48", children: _jsx(ResolvedImage, { src: question.imageUrl, alt: `Q${question.questionNumber}`, className: "h-full w-auto rounded-xl object-contain drop-shadow-sm" }) })), _jsx("div", { className: "flex flex-wrap justify-center gap-2", children: question.words.map((word, idx) => {
73
+ const isUsed = usedIndices.has(idx);
74
+ // Use example answer punctuation if available for display
75
+ let displayWord = word;
76
+ if (question.isExample && question.exampleAnswer) {
77
+ const matchedAns = question.exampleAnswer.find(a => normalize(a) === normalize(word));
78
+ if (matchedAns)
79
+ displayWord = matchedAns;
80
+ }
81
+ return (_jsx("button", { type: "button", disabled: isDisabled || isUsed, onClick: () => handleWordClick(question.id, displayWord, currentAnswer), className: `flex min-w-[2.5rem] items-center justify-center rounded-lg border-2 px-3 py-2 text-lg font-bold transition-all ${isUsed
82
+ ? 'border-slate-200 bg-slate-100 text-slate-300'
83
+ : 'border-blue-400 bg-blue-50 text-blue-700 shadow-sm hover:-translate-y-0.5 hover:bg-blue-100 hover:shadow'} ${isDisabled ? 'cursor-not-allowed' : 'cursor-pointer'}`, children: displayWord }, idx));
84
+ }) }), _jsxs("div", { className: `mt-2 w-full rounded-xl border p-4 ${answerSectionClasses}`, children: [_jsx("div", { className: `mb-2 text-center text-xs font-medium uppercase tracking-wider ${answerLabelClasses}`, children: "Your Answer" }), _jsx("div", { className: "flex min-h-[3rem] flex-wrap items-center justify-center gap-2", children: currentAnswer.length > 0 ? (currentAnswer.map((word, idx) => (_jsx("button", { type: "button", disabled: isDisabled, onClick: () => handleRemoveWord(question.id, idx, currentAnswer), className: `flex min-w-[2.5rem] items-center justify-center rounded-lg border-2 px-3 py-2 text-lg font-bold shadow-sm transition-all disabled:cursor-not-allowed ${answerChipClasses}`, title: !isDisabled ? "Click to remove" : "", children: word }, idx)))) : (_jsx("span", { className: `text-sm italic ${question.isExample ? 'text-amber-500' : 'text-slate-400'}`, children: question.isExample ? 'Example answer is shown here' : 'Click letters above to answer' })) })] }), answerIsWrong && ((() => {
85
+ const correctAnswerArray = rawCorrectAnswerMap[question.id]?.answer;
86
+ if (!Array.isArray(correctAnswerArray) || correctAnswerArray.length === 0) {
87
+ return null;
88
+ }
89
+ return (_jsxs("div", { className: "mt-3 w-full rounded-xl border-2 border-emerald-300 bg-gradient-to-br from-emerald-50 to-green-50 p-4", children: [_jsxs("div", { className: "mb-2 flex items-center justify-center gap-1.5 text-xs font-medium uppercase tracking-wider text-emerald-600", children: [_jsx(CheckCircle2, { className: "h-3.5 w-3.5" }), "\u0110\u00E1p \u00E1n \u0111\u00FAng"] }), _jsx("div", { className: "flex min-h-[3rem] flex-wrap items-center justify-center gap-2", children: correctAnswerArray.map((word, idx) => (_jsx("div", { className: "flex min-w-[2.5rem] items-center justify-center rounded-lg border-2 border-emerald-400 bg-emerald-100 px-3 py-2 text-lg font-bold text-emerald-800 shadow-sm", children: word }, idx))) })] }));
90
+ })())] }, question.id));
91
+ }) })] }));
92
+ }