@tinyweb_dev/oe-exam-sdk 0.1.28 → 0.1.29

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (796) hide show
  1. package/README.md +4 -4
  2. package/dist/api/contest-rooms.js +54 -1
  3. package/dist/api/contest-rooms.types.js +1 -1
  4. package/dist/api/exam-create.js +179 -1
  5. package/dist/api/exam-create.types.js +1 -1
  6. package/dist/api/exam-entry.js +68 -1
  7. package/dist/api/exam-entry.types.js +1 -1
  8. package/dist/api/exam-questions.js +149 -1
  9. package/dist/api/exam-questions.types.js +1 -1
  10. package/dist/api/exam-taking.js +84 -1
  11. package/dist/api/exam-taking.types.d.ts +7 -0
  12. package/dist/api/exam-taking.types.js +1 -1
  13. package/dist/api/index.js +8 -1
  14. package/dist/api/result-review.js +62 -1
  15. package/dist/api/result-review.types.js +16 -1
  16. package/dist/components/CueCardEditor.js +58 -1
  17. package/dist/components/common/ResolvedImage.js +15 -1
  18. package/dist/components/exams/CompletionDialog.js +8 -1
  19. package/dist/components/exams/CreateExamInfoForm.js +84 -1
  20. package/dist/components/exams/CreateExamPageContainer.js +110 -1
  21. package/dist/components/exams/ExamCreator.js +287 -1
  22. package/dist/components/exams/ExamPartTabs.js +138 -1
  23. package/dist/components/exams/ExamPreviewDialog.d.ts +6 -7
  24. package/dist/components/exams/ExamPreviewDialog.js +121 -1
  25. package/dist/components/exams/ExamPreviewPartContent.d.ts +18 -0
  26. package/dist/components/exams/ExamPreviewPartContent.js +27 -0
  27. package/dist/components/exams/ExamPreviewSidebar.d.ts +17 -0
  28. package/dist/components/exams/ExamPreviewSidebar.js +37 -0
  29. package/dist/components/exams/ExamQuestionGrid.js +37 -1
  30. package/dist/components/exams/ExamQuestionsPageContainer.js +112 -1
  31. package/dist/components/exams/ExamViewDialog.js +61 -1
  32. package/dist/components/exams/PartAudioUploader.js +9 -1
  33. package/dist/components/exams/TemplateSelector.js +76 -1
  34. package/dist/components/exams/UnsavedChangesDialog.js +7 -1
  35. package/dist/components/exams/api.js +3 -1
  36. package/dist/components/exams/create-exam.texts.js +1 -1
  37. package/dist/components/exams/create-exam.validation.js +18 -1
  38. package/dist/components/exams/entry/EntryRestartDialog.js +11 -1
  39. package/dist/components/exams/entry/StudentExamEntryPageContainer.js +358 -1
  40. package/dist/components/exams/entry/exam-entry.states.js +26 -1
  41. package/dist/components/exams/entry/exam-entry.utils.js +55 -1
  42. package/dist/components/exams/entry/fullscreen-loading.js +18 -1
  43. package/dist/components/exams/entry/index.js +4 -1
  44. package/dist/components/exams/exam-preview.types.d.ts +30 -0
  45. package/dist/components/exams/exam-preview.types.js +62 -0
  46. package/dist/components/exams/exam-question-status.js +45 -1
  47. package/dist/components/exams/exam-template.utils.js +18 -1
  48. package/dist/components/exams/index.d.ts +1 -0
  49. package/dist/components/exams/index.js +19 -1
  50. package/dist/components/exams/rooms/StudentContestRoomsPageContainer.js +183 -1
  51. package/dist/components/exams/rooms/contest-rooms.utils.js +93 -1
  52. package/dist/components/exams/rooms/index.js +2 -1
  53. package/dist/components/exams/take/ExamTakingApiContext.js +25 -1
  54. package/dist/components/exams/take/ExamTakingPageContainer.js +955 -1
  55. package/dist/components/exams/take/GenericQuestionRenderer.js +14 -1
  56. package/dist/components/exams/take/components/QuestionRenderer.js +207 -1
  57. package/dist/components/exams/take/components/index.js +11 -1
  58. package/dist/components/exams/take/components/question-renderers/ArticlesViewer.js +8 -1
  59. package/dist/components/exams/take/components/question-renderers/MoversAnswerTheQuestionRenderer.js +30 -1
  60. package/dist/components/exams/take/components/question-renderers/MoversChooseAdjectiveRenderer.js +63 -1
  61. package/dist/components/exams/take/components/question-renderers/MoversChooseBestAnswerRenderer.js +136 -1
  62. package/dist/components/exams/take/components/question-renderers/MoversColoringRenderer.js +379 -1
  63. package/dist/components/exams/take/components/question-renderers/MoversCrossOutWordGroupRenderer.d.ts +20 -0
  64. package/dist/components/exams/take/components/question-renderers/MoversCrossOutWordGroupRenderer.js +141 -0
  65. package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.js +131 -1
  66. package/dist/components/exams/take/components/question-renderers/MoversGridFillRenderer.js +76 -1
  67. package/dist/components/exams/take/components/question-renderers/MoversLabelThePictureRenderer.js +68 -1
  68. package/dist/components/exams/take/components/question-renderers/MoversListenAndWriteRenderer.js +42 -1
  69. package/dist/components/exams/take/components/question-renderers/MoversMatchByWritingAnswerRenderer.js +131 -1
  70. package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.js +76 -1
  71. package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.js +68 -1
  72. package/dist/components/exams/take/components/question-renderers/MoversReadingPassageRenderer.js +62 -1
  73. package/dist/components/exams/take/components/question-renderers/MoversTrueFalseRenderer.d.ts +1 -1
  74. package/dist/components/exams/take/components/question-renderers/MoversTrueFalseRenderer.js +51 -1
  75. package/dist/components/exams/take/components/question-renderers/MoversWordFillParagraphRenderer.js +171 -1
  76. package/dist/components/exams/take/components/question-renderers/MoversWordFillStructuredFormRenderer.js +198 -1
  77. package/dist/components/exams/take/components/question-renderers/MoversWordOrderAndMatchGroupRenderer.d.ts +15 -0
  78. package/dist/components/exams/take/components/question-renderers/MoversWordOrderAndMatchGroupRenderer.js +36 -0
  79. package/dist/components/exams/take/components/question-renderers/MoversWordOrderingRenderer.js +92 -1
  80. package/dist/components/exams/take/components/question-renderers/MoversWriteSentencesRenderer.js +11 -1
  81. package/dist/components/exams/take/components/question-renderers/MoversWritingRenderer.js +56 -1
  82. package/dist/components/exams/take/components/question-renderers/index.d.ts +2 -0
  83. package/dist/components/exams/take/components/question-renderers/index.js +25 -1
  84. package/dist/components/exams/take/components/speaking/SpeakingAudioPlayer.js +85 -1
  85. package/dist/components/exams/take/components/speaking/SpeakingContentFrame.js +10 -1
  86. package/dist/components/exams/take/components/speaking/SpeakingExamLayout.js +15 -1
  87. package/dist/components/exams/take/components/speaking/SpeakingHeader.js +6 -1
  88. package/dist/components/exams/take/components/speaking/SpeakingInstructionBar.js +8 -1
  89. package/dist/components/exams/take/components/speaking/SpeakingMicButton.js +59 -1
  90. package/dist/components/exams/take/components/speaking/SpeakingNavButton.js +7 -1
  91. package/dist/components/exams/take/components/speaking/SpeakingPartBadge.js +5 -1
  92. package/dist/components/exams/take/components/speaking/SpeakingReadDisplayedContent.module.css +120 -0
  93. package/dist/components/exams/take/components/speaking/SpeakingRecordingPreview.js +73 -1
  94. package/dist/components/exams/take/components/speaking/SpeakingReportErrorButton.js +8 -1
  95. package/dist/components/exams/take/components/speaking/SpeakingSpontaneousQA.module.css +344 -0
  96. package/dist/components/exams/take/components/speaking/SpeakingVideoIntro.js +32 -1
  97. package/dist/components/exams/take/components/speaking/index.js +1 -1
  98. package/dist/components/exams/take/components/speaking/renderers/SpeakingChooseAnswerGroupRenderer.js +63 -1
  99. package/dist/components/exams/take/components/speaking/renderers/SpeakingChooseObjectsRenderer.js +95 -1
  100. package/dist/components/exams/take/components/speaking/renderers/SpeakingCompareImagesRenderer.js +35 -1
  101. package/dist/components/exams/take/components/speaking/renderers/SpeakingConversationRenderer.js +147 -1
  102. package/dist/components/exams/take/components/speaking/renderers/SpeakingCueCardRenderer.js +232 -1
  103. package/dist/components/exams/take/components/speaking/renderers/SpeakingDescribeImageRenderer.js +193 -1
  104. package/dist/components/exams/take/components/speaking/renderers/SpeakingDragObjectsRenderer.js +155 -1
  105. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.js +212 -1
  106. package/dist/components/exams/take/components/speaking/renderers/SpeakingInfoExchangeRenderer.js +77 -1
  107. package/dist/components/exams/take/components/speaking/renderers/SpeakingListenAndSpeakAnswerRenderer.js +64 -1
  108. package/dist/components/exams/take/components/speaking/renderers/SpeakingOddOneOutRenderer.js +69 -1
  109. package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionListRenderer.js +64 -1
  110. package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionRenderer.js +92 -1
  111. package/dist/components/exams/take/components/speaking/renderers/SpeakingReadDisplayedContentRenderer.js +202 -1
  112. package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQARenderer.js +339 -1
  113. package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQaGroupRenderer.d.ts +19 -0
  114. package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQaGroupRenderer.js +76 -0
  115. package/dist/components/exams/take/components/speaking/renderers/SpeakingStoryImagesRenderer.js +56 -1
  116. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQACambridgeLayout.d.ts +24 -0
  117. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQACambridgeLayout.js +120 -0
  118. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQAInlineAudioPlayer.d.ts +8 -0
  119. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQAInlineAudioPlayer.js +94 -0
  120. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQASummerSkyAudioPlayer.d.ts +8 -0
  121. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQASummerSkyAudioPlayer.js +101 -0
  122. package/dist/components/exams/take/components/speaking/renderers/index.d.ts +1 -0
  123. package/dist/components/exams/take/components/speaking/renderers/index.js +14 -1
  124. package/dist/components/exams/take/exam-taking.utils.d.ts +13 -0
  125. package/dist/components/exams/take/exam-taking.utils.js +203 -1
  126. package/dist/components/exams/take/hooks/index.js +2 -1
  127. package/dist/components/exams/take/hooks/useAnswerAutosave.js +71 -1
  128. package/dist/components/exams/take/hooks/useAttemptSubmit.js +38 -1
  129. package/dist/components/exams/take/hooks/useAutoSave.js +83 -1
  130. package/dist/components/exams/take/hooks/useExamCountdown.js +35 -1
  131. package/dist/components/exams/take/hooks/useExamTimer.js +97 -1
  132. package/dist/components/exams/take/hooks/useProctoringCamera.js +118 -1
  133. package/dist/components/exams/take/hooks/useProctoringSnapshot.js +76 -1
  134. package/dist/components/exams/take/hooks/useSpeakingConversationAudio.js +178 -1
  135. package/dist/components/exams/take/hooks/useSpeakingNavigation.js +51 -1
  136. package/dist/components/exams/take/hooks/useSpeakingRecorder.js +406 -1
  137. package/dist/components/exams/take/hooks/useSpeakingRendererSetup.js +136 -1
  138. package/dist/components/exams/take/hooks/useSpontaneousQaGroupRecorder.d.ts +11 -0
  139. package/dist/components/exams/take/hooks/useSpontaneousQaGroupRecorder.js +352 -0
  140. package/dist/components/exams/take/hooks/useSpontaneousQaGroupRendererSetup.d.ts +42 -0
  141. package/dist/components/exams/take/hooks/useSpontaneousQaGroupRendererSetup.js +166 -0
  142. package/dist/components/exams/take/hooks/useToeicAudio.js +35 -1
  143. package/dist/components/exams/take/index.js +6 -1
  144. package/dist/components/exams/take/types/speaking-cue-card.types.js +1 -1
  145. package/dist/components/exams/take/types/speaking.types.js +7 -1
  146. package/dist/components/exams/take/types.js +4 -1
  147. package/dist/components/exams/take/utils/answer-transformers.js +452 -1
  148. package/dist/components/exams/take/utils/cambridge-yle-inline-speaking.d.ts +21 -0
  149. package/dist/components/exams/take/utils/cambridge-yle-inline-speaking.js +44 -0
  150. package/dist/components/exams/take/utils/coloring-assignments.js +40 -1
  151. package/dist/components/exams/take/utils/cross-out-word-group.transformer.d.ts +21 -0
  152. package/dist/components/exams/take/utils/cross-out-word-group.transformer.js +83 -0
  153. package/dist/components/exams/take/utils/question-transformers.d.ts +43 -2
  154. package/dist/components/exams/take/utils/question-transformers.js +1137 -1
  155. package/dist/components/features/exam-entry/entry-state-cards.js +19 -1
  156. package/dist/components/features/exam-entry/index.js +2 -1
  157. package/dist/components/features/exam-entry/themed/CambridgeYleWaitingRoom.js +121 -1
  158. package/dist/components/features/exam-entry/themed/ThemedWaitingRoom.js +37 -1
  159. package/dist/components/features/exam-entry/themed/ToeicWaitingRoom.js +15 -1
  160. package/dist/components/features/exam-entry/themed/cambridge-yle-waiting-parts.js +80 -1
  161. package/dist/components/features/exam-entry/themed/components/CloseButton.js +6 -1
  162. package/dist/components/features/exam-entry/themed/components/ExamInfoPanel.js +17 -1
  163. package/dist/components/features/exam-entry/themed/components/ModeSelector.js +6 -1
  164. package/dist/components/features/exam-entry/themed/components/StartButton.js +12 -1
  165. package/dist/components/features/exam-entry/themed/components/index.js +4 -1
  166. package/dist/components/features/exam-entry/themed/index.js +3 -1
  167. package/dist/components/index.d.ts +1 -0
  168. package/dist/components/index.js +43 -1
  169. package/dist/components/questions/_shared/config/question-types.config.js +362 -1
  170. package/dist/components/questions/_shared/constants/color-palette.js +21 -1
  171. package/dist/components/questions/_shared/hooks/useAnswerGroupQuestionGroup.js +105 -1
  172. package/dist/components/questions/_shared/hooks/useBasicQuestionGroup.js +222 -1
  173. package/dist/components/questions/_shared/hooks/useCompareImagesGroup.js +140 -1
  174. package/dist/components/questions/_shared/hooks/useImageQuestionGroup.js +123 -1
  175. package/dist/components/questions/_shared/hooks/useInfoExchangeGroup.js +50 -1
  176. package/dist/components/questions/_shared/hooks/useQuestionFormState.js +54 -1
  177. package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +123 -1
  178. package/dist/components/questions/_shared/hooks/useQuestionListGroup.js +121 -1
  179. package/dist/components/questions/_shared/hooks/useQuestionOnChange.js +70 -1
  180. package/dist/components/questions/_shared/hooks/useSceneQuestionGroup.js +194 -1
  181. package/dist/components/questions/_shared/index.js +7 -1
  182. package/dist/components/questions/_shared/types/answer-the-question.type.js +1 -1
  183. package/dist/components/questions/_shared/types/arrange-letters-into-words.type.js +1 -1
  184. package/dist/components/questions/_shared/types/choose-correct-adjective.type.js +3 -1
  185. package/dist/components/questions/_shared/types/choose-the-correct-answer-group.type.d.ts +1 -0
  186. package/dist/components/questions/_shared/types/choose-the-correct-answer-group.type.js +1 -1
  187. package/dist/components/questions/_shared/types/color-region.config.js +1 -1
  188. package/dist/components/questions/_shared/types/cross-out-word-group.type.d.ts +2 -0
  189. package/dist/components/questions/_shared/types/cross-out-word-group.type.js +1 -1
  190. package/dist/components/questions/_shared/types/fill-in-blank.type.js +1 -1
  191. package/dist/components/questions/_shared/types/fill-missing-words-in-grid.type.js +1 -1
  192. package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +1 -1
  193. package/dist/components/questions/_shared/types/index.d.ts +1 -0
  194. package/dist/components/questions/_shared/types/index.js +14 -1
  195. package/dist/components/questions/_shared/types/label-the-picture.type.js +1 -1
  196. package/dist/components/questions/_shared/types/listen-and-choose-from-answer-group.type.js +1 -1
  197. package/dist/components/questions/_shared/types/listen-and-choose-objects-in-scene.type.js +1 -1
  198. package/dist/components/questions/_shared/types/listen-and-drag-objects-into-scene.type.js +1 -1
  199. package/dist/components/questions/_shared/types/listen-and-speak-answer.type.js +1 -1
  200. package/dist/components/questions/_shared/types/listen-and-speak-compare-images.type.js +1 -1
  201. package/dist/components/questions/_shared/types/listen-and-speak-image-group.type.js +1 -1
  202. package/dist/components/questions/_shared/types/listen-and-speak-info-exchange.type.js +1 -1
  203. package/dist/components/questions/_shared/types/listen-and-speak-odd-one-out.type.js +3 -1
  204. package/dist/components/questions/_shared/types/listen-and-speak-question-list.type.js +1 -1
  205. package/dist/components/questions/_shared/types/listen-and-speak-with-story-images.type.js +3 -1
  206. package/dist/components/questions/_shared/types/listen-and-tick-answer.type.js +1 -1
  207. package/dist/components/questions/_shared/types/listening-drawline.type.js +1 -1
  208. package/dist/components/questions/_shared/types/look-and-choose-answer.type.js +1 -1
  209. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +1 -1
  210. package/dist/components/questions/_shared/types/match-by-writing-answer.type.js +1 -1
  211. package/dist/components/questions/_shared/types/multiple-choice.type.d.ts +5 -0
  212. package/dist/components/questions/_shared/types/multiple-choice.type.js +1 -1
  213. package/dist/components/questions/_shared/types/question-group.type.js +11 -1
  214. package/dist/components/questions/_shared/types/read-and-color-objects.type.js +1 -1
  215. package/dist/components/questions/_shared/types/read-displayed-content.type.js +1 -1
  216. package/dist/components/questions/_shared/types/read-passage-and-answer-questions.type.js +1 -1
  217. package/dist/components/questions/_shared/types/speaking-describe-image.type.js +1 -1
  218. package/dist/components/questions/_shared/types/spontaneous-qa-group.type.d.ts +37 -0
  219. package/dist/components/questions/_shared/types/spontaneous-qa-group.type.js +1 -0
  220. package/dist/components/questions/_shared/types/spontaneous-qa.type.js +1 -1
  221. package/dist/components/questions/_shared/types/true-false-group.type.js +1 -1
  222. package/dist/components/questions/_shared/types/word-fill-paragraph-with-options.type.js +1 -1
  223. package/dist/components/questions/_shared/types/word-fill-paragraph.type.js +1 -1
  224. package/dist/components/questions/_shared/types/word-fill-structured-form.type.d.ts +20 -11
  225. package/dist/components/questions/_shared/types/word-fill-structured-form.type.js +1 -1
  226. package/dist/components/questions/_shared/types/word-order-and-match-group.type.d.ts +38 -0
  227. package/dist/components/questions/_shared/types/word-order-and-match-group.type.js +1 -0
  228. package/dist/components/questions/_shared/types/word-ordering.type.js +1 -1
  229. package/dist/components/questions/_shared/types/write-a-short-letter.type.js +1 -1
  230. package/dist/components/questions/_shared/types/write-correct-verb-form.type.js +3 -1
  231. package/dist/components/questions/_shared/types/write-sentences.type.js +1 -1
  232. package/dist/components/questions/_shared/types/write-short-paragraph.type.js +1 -1
  233. package/dist/components/questions/_shared/utils/answer-builder.js +79 -1
  234. package/dist/components/questions/_shared/utils/createColorRegionRegistration.js +39 -1
  235. package/dist/components/questions/_shared/utils/id-generator.js +3 -1
  236. package/dist/components/questions/_shared/utils/parseFillBlankStem.d.ts +30 -0
  237. package/dist/components/questions/_shared/utils/parseFillBlankStem.js +69 -0
  238. package/dist/components/questions/_shared/utils/question-validation.js +21 -1
  239. package/dist/components/questions/components/ColorNode.js +85 -1
  240. package/dist/components/questions/components/ColorRegionClient.js +545 -1
  241. package/dist/components/questions/components/ColorRegionCreator.js +420 -1
  242. package/dist/components/questions/components/ColorRegionNode.js +59 -1
  243. package/dist/components/questions/components/CreatorGuide.js +103 -1
  244. package/dist/components/questions/components/NameNode.js +26 -1
  245. package/dist/components/questions/components/QuestionBankOption.js +46 -1
  246. package/dist/components/questions/components/QuestionBasicInfoForm.js +9 -1
  247. package/dist/components/questions/components/QuestionCommonFields.js +33 -1
  248. package/dist/components/questions/components/QuestionSearchDropdown.js +258 -1
  249. package/dist/components/questions/components/QuestionTypeSelector.js +32 -1
  250. package/dist/components/questions/components/QuestionTypeSpecificForms.js +6 -1
  251. package/dist/components/questions/components/RegionNode.js +94 -1
  252. package/dist/components/questions/components/index.js +14 -1
  253. package/dist/components/questions/creator/QuestionCreator.js +90 -1
  254. package/dist/components/questions/creator/QuestionGroupCreator.js +119 -1
  255. package/dist/components/questions/creator/dedicated-component-router.js +79 -1
  256. package/dist/components/questions/creator/index.js +3 -1
  257. package/dist/components/questions/creator/question-type-registry.js +90 -1
  258. package/dist/components/questions/groups/ArticlesGroupCard.js +121 -1
  259. package/dist/components/questions/groups/BasicQuestionGroupCard.js +50 -1
  260. package/dist/components/questions/groups/DocumentGroupCard.js +114 -1
  261. package/dist/components/questions/groups/MatchAnswerGroupCard.js +25 -1
  262. package/dist/components/questions/index.d.ts +1 -0
  263. package/dist/components/questions/index.js +11 -1
  264. package/dist/components/questions/question-bank/EditQuestionBankDialog.d.ts +2 -0
  265. package/dist/components/questions/question-bank/EditQuestionBankDialog.js +114 -0
  266. package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +23 -0
  267. package/dist/components/questions/question-bank/QuestionEditorRenderer.js +75 -0
  268. package/dist/components/questions/question-bank/difficulty.d.ts +15 -0
  269. package/dist/components/questions/question-bank/difficulty.js +33 -0
  270. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.d.ts +5 -0
  271. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.js +101 -0
  272. package/dist/components/questions/question-bank/editor-adapters/index.d.ts +5 -0
  273. package/dist/components/questions/question-bank/editor-adapters/index.js +4 -0
  274. package/dist/components/questions/question-bank/editor-adapters/media-adapters.d.ts +6 -0
  275. package/dist/components/questions/question-bank/editor-adapters/media-adapters.js +140 -0
  276. package/dist/components/questions/question-bank/editor-adapters/types.d.ts +1 -0
  277. package/dist/components/questions/question-bank/editor-adapters/types.js +1 -0
  278. package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.d.ts +8 -0
  279. package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.js +6 -0
  280. package/dist/components/questions/question-bank/editor-adapters/writing-adapters.d.ts +5 -0
  281. package/dist/components/questions/question-bank/editor-adapters/writing-adapters.js +114 -0
  282. package/dist/components/questions/question-bank/index.d.ts +4 -0
  283. package/dist/components/questions/question-bank/index.js +3 -0
  284. package/dist/components/questions/question-bank/types.d.ts +59 -0
  285. package/dist/components/questions/question-bank/types.js +1 -0
  286. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +37 -1
  287. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +351 -1
  288. package/dist/components/questions/types/answer-the-question/register.js +67 -1
  289. package/dist/components/questions/types/answer-the-question/transform.js +36 -1
  290. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +141 -1
  291. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +118 -1
  292. package/dist/components/questions/types/arrange-letters-into-words/index.js +2 -1
  293. package/dist/components/questions/types/arrange-letters-into-words/register.js +4 -1
  294. package/dist/components/questions/types/arrange-letters-into-words/transform.js +24 -1
  295. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +74 -1
  296. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +169 -1
  297. package/dist/components/questions/types/choose-correct-adjective/index.js +2 -1
  298. package/dist/components/questions/types/choose-correct-adjective/register.js +55 -1
  299. package/dist/components/questions/types/choose-correct-adjective/transform.js +16 -1
  300. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +149 -1
  301. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +568 -1
  302. package/dist/components/questions/types/choose-the-correct-answer/index.js +3 -1
  303. package/dist/components/questions/types/choose-the-correct-answer/normalize.d.ts +9 -0
  304. package/dist/components/questions/types/choose-the-correct-answer/normalize.js +88 -1
  305. package/dist/components/questions/types/choose-the-correct-answer/register.js +99 -1
  306. package/dist/components/questions/types/choose-the-correct-answer/transform.js +70 -1
  307. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +22 -1
  308. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupCreator.js +163 -1
  309. package/dist/components/questions/types/choose-the-correct-answer-group/index.js +3 -1
  310. package/dist/components/questions/types/choose-the-correct-answer-group/map-choose-the-correct-answer-group-data.js +94 -1
  311. package/dist/components/questions/types/choose-the-correct-answer-group/register.js +36 -1
  312. package/dist/components/questions/types/choose-the-correct-answer-group/transform.js +55 -1
  313. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +27 -1
  314. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +231 -1
  315. package/dist/components/questions/types/cross-out-word-group/index.js +5 -1
  316. package/dist/components/questions/types/cross-out-word-group/map-cross-out-word-group-data.d.ts +1 -1
  317. package/dist/components/questions/types/cross-out-word-group/map-cross-out-word-group-data.js +110 -1
  318. package/dist/components/questions/types/cross-out-word-group/register.js +36 -1
  319. package/dist/components/questions/types/cross-out-word-group/transform.js +67 -1
  320. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +275 -1
  321. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +464 -1
  322. package/dist/components/questions/types/fill-in-blank/index.js +2 -1
  323. package/dist/components/questions/types/fill-in-blank/register.js +148 -1
  324. package/dist/components/questions/types/fill-in-blank/transform.js +88 -1
  325. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +117 -1
  326. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +671 -1
  327. package/dist/components/questions/types/fill-missing-words-in-grid/index.js +2 -1
  328. package/dist/components/questions/types/fill-missing-words-in-grid/register.js +37 -1
  329. package/dist/components/questions/types/fill-missing-words-in-grid/transform.js +15 -1
  330. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +262 -1
  331. package/dist/components/questions/types/gn-speaking-interview/register.js +121 -1
  332. package/dist/components/questions/types/gn-speaking-interview/transform.js +48 -1
  333. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingClient.js +16 -1
  334. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingCreator.js +27 -1
  335. package/dist/components/questions/types/image-object-matching/index.js +2 -1
  336. package/dist/components/questions/types/image-object-matching/register.js +3 -1
  337. package/dist/components/questions/types/image-object-matching/transform.js +2 -1
  338. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +102 -1
  339. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +344 -1
  340. package/dist/components/questions/types/label-the-picture/index.js +2 -1
  341. package/dist/components/questions/types/label-the-picture/register.js +54 -1
  342. package/dist/components/questions/types/label-the-picture/transform.js +28 -1
  343. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +78 -1
  344. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +232 -1
  345. package/dist/components/questions/types/listen-and-choose-from-answer-group/index.js +2 -1
  346. package/dist/components/questions/types/listen-and-choose-from-answer-group/register.js +82 -1
  347. package/dist/components/questions/types/listen-and-choose-from-answer-group/transform.js +27 -1
  348. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +70 -1
  349. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +386 -1
  350. package/dist/components/questions/types/listen-and-choose-objects-in-scene/index.js +2 -1
  351. package/dist/components/questions/types/listen-and-choose-objects-in-scene/register.js +100 -1
  352. package/dist/components/questions/types/listen-and-choose-objects-in-scene/transform.js +33 -1
  353. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +106 -1
  354. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +457 -1
  355. package/dist/components/questions/types/listen-and-drag-objects-into-scene/index.js +2 -1
  356. package/dist/components/questions/types/listen-and-drag-objects-into-scene/register.js +110 -1
  357. package/dist/components/questions/types/listen-and-drag-objects-into-scene/transform.js +35 -1
  358. package/dist/components/questions/types/listen-and-speak-answer/ArrowIndicator.js +8 -1
  359. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +22 -1
  360. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +291 -1
  361. package/dist/components/questions/types/listen-and-speak-answer/index.js +2 -1
  362. package/dist/components/questions/types/listen-and-speak-answer/register.js +84 -1
  363. package/dist/components/questions/types/listen-and-speak-answer/transform.js +27 -1
  364. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +32 -1
  365. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +295 -1
  366. package/dist/components/questions/types/listen-and-speak-compare-images/register.js +79 -1
  367. package/dist/components/questions/types/listen-and-speak-compare-images/transform.js +27 -1
  368. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +34 -1
  369. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +285 -1
  370. package/dist/components/questions/types/listen-and-speak-image-group/register.js +81 -1
  371. package/dist/components/questions/types/listen-and-speak-image-group/transform.js +26 -1
  372. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +38 -1
  373. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +385 -1
  374. package/dist/components/questions/types/listen-and-speak-info-exchange/register.js +87 -1
  375. package/dist/components/questions/types/listen-and-speak-info-exchange/transform.js +32 -1
  376. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +26 -1
  377. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +247 -1
  378. package/dist/components/questions/types/listen-and-speak-odd-one-out/register.js +59 -1
  379. package/dist/components/questions/types/listen-and-speak-odd-one-out/transform.js +18 -1
  380. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +26 -1
  381. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +256 -1
  382. package/dist/components/questions/types/listen-and-speak-question-list/register.js +81 -1
  383. package/dist/components/questions/types/listen-and-speak-question-list/transform.js +26 -1
  384. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +26 -1
  385. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +230 -1
  386. package/dist/components/questions/types/listen-and-speak-with-story-images/register.js +56 -1
  387. package/dist/components/questions/types/listen-and-speak-with-story-images/transform.js +16 -1
  388. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +72 -1
  389. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +230 -1
  390. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +1 -1
  391. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +66 -1
  392. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +42 -1
  393. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +96 -1
  394. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +238 -1
  395. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +2 -1
  396. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +76 -1
  397. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +19 -1
  398. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +134 -1
  399. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +617 -1
  400. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +2 -1
  401. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +82 -1
  402. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +29 -1
  403. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +38 -1
  404. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +359 -1
  405. package/dist/components/questions/types/match-by-writing-answer/index.js +3 -1
  406. package/dist/components/questions/types/match-by-writing-answer/register.js +58 -1
  407. package/dist/components/questions/types/match-by-writing-answer/transform.js +61 -1
  408. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsClient.js +16 -1
  409. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsCreator.js +23 -1
  410. package/dist/components/questions/types/read-and-color-objects/index.js +2 -1
  411. package/dist/components/questions/types/read-and-color-objects/register.js +3 -1
  412. package/dist/components/questions/types/read-and-color-objects/transform.js +36 -1
  413. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +125 -1
  414. package/dist/components/questions/types/read-displayed-content/register.js +64 -1
  415. package/dist/components/questions/types/read-displayed-content/transform.js +12 -1
  416. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +65 -1
  417. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +511 -1
  418. package/dist/components/questions/types/read-passage-and-answer-questions/index.js +2 -1
  419. package/dist/components/questions/types/read-passage-and-answer-questions/register.js +58 -1
  420. package/dist/components/questions/types/read-passage-and-answer-questions/transform.js +28 -1
  421. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +373 -1
  422. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +41 -1
  423. package/dist/components/questions/types/speaking-conversation/register.js +110 -1
  424. package/dist/components/questions/types/speaking-conversation/transform.js +34 -1
  425. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +74 -1
  426. package/dist/components/questions/types/speaking-cue-card/register.js +52 -1
  427. package/dist/components/questions/types/speaking-cue-card/transform.js +12 -1
  428. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +10 -1
  429. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +161 -1
  430. package/dist/components/questions/types/speaking-describe-image/index.js +2 -1
  431. package/dist/components/questions/types/speaking-describe-image/register.js +69 -1
  432. package/dist/components/questions/types/speaking-describe-image/transform.js +16 -1
  433. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +234 -1
  434. package/dist/components/questions/types/spontaneous-qa/register.js +93 -1
  435. package/dist/components/questions/types/spontaneous-qa/transform.js +34 -1
  436. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.d.ts +2 -0
  437. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.js +9 -0
  438. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.d.ts +2 -0
  439. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.js +179 -0
  440. package/dist/components/questions/types/spontaneous-qa-group/creator-helpers.d.ts +5 -0
  441. package/dist/components/questions/types/spontaneous-qa-group/creator-helpers.js +43 -0
  442. package/dist/components/questions/types/spontaneous-qa-group/flatten.d.ts +14 -0
  443. package/dist/components/questions/types/spontaneous-qa-group/flatten.js +69 -0
  444. package/dist/components/questions/types/spontaneous-qa-group/index.d.ts +2 -0
  445. package/dist/components/questions/types/spontaneous-qa-group/index.js +2 -0
  446. package/dist/components/questions/types/spontaneous-qa-group/register.d.ts +2 -0
  447. package/dist/components/questions/types/spontaneous-qa-group/register.js +139 -0
  448. package/dist/components/questions/types/spontaneous-qa-group/transform.d.ts +2 -0
  449. package/dist/components/questions/types/spontaneous-qa-group/transform.js +52 -0
  450. package/dist/components/questions/types/true-false/TrueFalseClient.js +75 -1
  451. package/dist/components/questions/types/true-false/TrueFalseCreator.js +244 -1
  452. package/dist/components/questions/types/true-false/index.js +2 -1
  453. package/dist/components/questions/types/true-false/register.js +77 -1
  454. package/dist/components/questions/types/true-false/transform.js +32 -1
  455. package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +25 -1
  456. package/dist/components/questions/types/true-false-group/TrueFalseGroupCreator.js +129 -1
  457. package/dist/components/questions/types/true-false-group/index.js +5 -1
  458. package/dist/components/questions/types/true-false-group/map-true-false-group-data.js +69 -1
  459. package/dist/components/questions/types/true-false-group/register.js +36 -1
  460. package/dist/components/questions/types/true-false-group/transform.js +43 -1
  461. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +233 -1
  462. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +631 -1
  463. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphWrapper.js +28 -1
  464. package/dist/components/questions/types/word-fill-paragraph/index.js +2 -1
  465. package/dist/components/questions/types/word-fill-paragraph/register.js +136 -1
  466. package/dist/components/questions/types/word-fill-paragraph/transform.js +56 -1
  467. package/dist/components/questions/types/word-fill-structured-form/WfsfWordBankEditor.js +86 -1
  468. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +246 -1
  469. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.d.ts +2 -0
  470. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +484 -1
  471. package/dist/components/questions/types/word-fill-structured-form/index.js +3 -1
  472. package/dist/components/questions/types/word-fill-structured-form/map-wfsf-question-data.js +113 -1
  473. package/dist/components/questions/types/word-fill-structured-form/register.js +36 -1
  474. package/dist/components/questions/types/word-fill-structured-form/transform.js +77 -1
  475. package/dist/components/questions/types/word-fill-structured-form/wfsf-word-bank.utils.js +110 -1
  476. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.d.ts +3 -0
  477. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.js +190 -0
  478. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupCreator.d.ts +3 -0
  479. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupCreator.js +182 -0
  480. package/dist/components/questions/types/word-order-and-match-group/index.d.ts +4 -0
  481. package/dist/components/questions/types/word-order-and-match-group/index.js +4 -0
  482. package/dist/components/questions/types/word-order-and-match-group/register.d.ts +2 -0
  483. package/dist/components/questions/types/word-order-and-match-group/register.js +121 -0
  484. package/dist/components/questions/types/word-order-and-match-group/transform.d.ts +2 -0
  485. package/dist/components/questions/types/word-order-and-match-group/transform.js +70 -0
  486. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +84 -1
  487. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +192 -1
  488. package/dist/components/questions/types/word-ordering/index.js +2 -1
  489. package/dist/components/questions/types/word-ordering/register.js +46 -1
  490. package/dist/components/questions/types/word-ordering/transform.js +24 -1
  491. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +99 -1
  492. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +320 -1
  493. package/dist/components/questions/types/write-a-short-letter/index.js +2 -1
  494. package/dist/components/questions/types/write-a-short-letter/register.js +68 -1
  495. package/dist/components/questions/types/write-a-short-letter/transform.js +25 -1
  496. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +61 -1
  497. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +196 -1
  498. package/dist/components/questions/types/write-correct-verb-form/index.js +2 -1
  499. package/dist/components/questions/types/write-correct-verb-form/register.js +62 -1
  500. package/dist/components/questions/types/write-correct-verb-form/transform.js +17 -1
  501. package/dist/components/questions/types/write-sentences/WriteSentencesClient.js +22 -1
  502. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +174 -1
  503. package/dist/components/questions/types/write-sentences/index.js +2 -1
  504. package/dist/components/questions/types/write-sentences/register.js +58 -1
  505. package/dist/components/questions/types/write-sentences/transform.js +28 -1
  506. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +96 -1
  507. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +161 -1
  508. package/dist/components/questions/types/write-short-paragraph/index.js +2 -1
  509. package/dist/components/questions/types/write-short-paragraph/register.js +4 -1
  510. package/dist/components/questions/types/write-short-paragraph/transform.js +21 -1
  511. package/dist/components/questions/viewer/ChooseTheCorrectAnswerGroupViewer.js +11 -1
  512. package/dist/components/questions/viewer/CrossOutWordGroupViewer.js +8 -1
  513. package/dist/components/questions/viewer/QuestionGroupViewer.js +36 -1
  514. package/dist/components/questions/viewer/QuestionViewer.js +692 -1
  515. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.d.ts +2 -1
  516. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.js +82 -1
  517. package/dist/components/questions/viewer/TrueFalseGroupViewer.js +11 -1
  518. package/dist/components/questions/viewer/WordFillStructuredFormViewer.js +14 -1
  519. package/dist/components/questions/viewer/WordOrderAndMatchGroupViewer.d.ts +3 -1
  520. package/dist/components/questions/viewer/WordOrderAndMatchGroupViewer.js +95 -1
  521. package/dist/components/questions/viewer/index.js +8 -1
  522. package/dist/components/results/ResultReviewPageContainer.js +220 -1
  523. package/dist/components/results/ReviewQuestionRenderer.js +55 -1
  524. package/dist/components/results/hooks/useResultReviewPolling.js +76 -1
  525. package/dist/components/results/index.js +22 -1
  526. package/dist/components/results/renderers/QuestionExplanations.js +19 -1
  527. package/dist/components/results/renderers/ReviewChooseAdjectiveRenderer.js +94 -1
  528. package/dist/components/results/renderers/ReviewChooseBestAnswerRenderer.js +75 -1
  529. package/dist/components/results/renderers/ReviewColoringRenderer.js +110 -1
  530. package/dist/components/results/renderers/ReviewListenAndWriteRenderer.js +48 -1
  531. package/dist/components/results/renderers/ReviewListenChooseAnswerGroupRenderer.js +87 -1
  532. package/dist/components/results/renderers/ReviewListenChooseObjectsRenderer.js +104 -1
  533. package/dist/components/results/renderers/ReviewListenDragObjectsRenderer.js +123 -1
  534. package/dist/components/results/renderers/ReviewListeningHeader.js +104 -1
  535. package/dist/components/results/renderers/ReviewSpeakingAIPanel.js +116 -1
  536. package/dist/components/results/renderers/ReviewSpeakingMedia.js +48 -1
  537. package/dist/components/results/renderers/ReviewSpeakingQuestionItem.js +42 -1
  538. package/dist/components/results/renderers/ReviewSpeakingRenderer.js +12 -1
  539. package/dist/components/results/renderers/ReviewSpeakingRenderer.types.js +85 -1
  540. package/dist/components/results/renderers/review-question-body-dedicated.js +104 -1
  541. package/dist/components/results/renderers/review-question-body-movers.js +148 -1
  542. package/dist/components/results/renderers/review-speaking-map.js +226 -1
  543. package/dist/components/results/renderers/review-spontaneous-qa-group.d.ts +4 -0
  544. package/dist/components/results/renderers/review-spontaneous-qa-group.js +74 -0
  545. package/dist/components/results/report/BadgeTitle.js +5 -1
  546. package/dist/components/results/report/ExamSkillRow.js +20 -1
  547. package/dist/components/results/report/OceanEduExamReport.js +58 -1
  548. package/dist/components/results/report/OceanEduSpeakingReport.js +62 -1
  549. package/dist/components/results/report/OverallAssessmentSection.js +17 -1
  550. package/dist/components/results/report/ResultErrorState.js +18 -1
  551. package/dist/components/results/report/ResultLoadingState.js +10 -1
  552. package/dist/components/results/report/ResultNotPublicState.js +27 -1
  553. package/dist/components/results/report/SpeakingReportParts.js +14 -1
  554. package/dist/components/results/report/SpeakingReportShared.js +86 -1
  555. package/dist/components/results/report/SpeakingRubricSection.js +171 -1
  556. package/dist/components/results/report/speaking-report.utils.js +198 -1
  557. package/dist/components/results/result-review.utils.js +6 -1
  558. package/dist/components/results/review-data.js +222 -1
  559. package/dist/components/results/review-renderer.utils.js +97 -1
  560. package/dist/components/results/review-status.utils.js +36 -1
  561. package/dist/components/results/review-types.js +1 -1
  562. package/dist/components/results/speaking/SpeakingResultAudioPlayer.js +77 -1
  563. package/dist/components/results/speaking/SpeakingReviewResult.js +48 -1
  564. package/dist/components/results/utils/review-score.utils.js +72 -1
  565. package/dist/components/results/utils/speaking-summary.js +224 -1
  566. package/dist/components/results/utils/transform-speaking-review.js +183 -1
  567. package/dist/components/shared/RichTextEditor.d.ts +9 -0
  568. package/dist/components/shared/RichTextEditor.js +47 -0
  569. package/dist/components/shared/audio/SpeakingAudioPlayer.js +100 -1
  570. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +140 -1
  571. package/dist/components/themes/cambridge-yle/CambridgeYleBottomNav.js +10 -1
  572. package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.d.ts +1 -1
  573. package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.js +18 -1
  574. package/dist/components/themes/cambridge-yle/CambridgeYleExampleText.js +11 -1
  575. package/dist/components/themes/cambridge-yle/CambridgeYleHeader.js +11 -1
  576. package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.d.ts +2 -1
  577. package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.js +19 -1
  578. package/dist/components/themes/cambridge-yle/CambridgeYleNavButton.js +13 -1
  579. package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.d.ts +1 -1
  580. package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.js +3 -1
  581. package/dist/components/themes/cambridge-yle/CambridgeYleProgressBar.js +6 -1
  582. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCard.js +5 -1
  583. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCardFullWidth.js +9 -1
  584. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.d.ts +2 -1
  585. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.js +61 -1
  586. package/dist/components/themes/cambridge-yle/CambridgeYleTimer.d.ts +1 -1
  587. package/dist/components/themes/cambridge-yle/CambridgeYleTimer.js +25 -1
  588. package/dist/components/themes/cambridge-yle/index.js +13 -1
  589. package/dist/components/themes/english-certification/EcAnswerTheQuestion.js +25 -1
  590. package/dist/components/themes/english-certification/EcMatchByWritingAnswer.js +89 -1
  591. package/dist/components/themes/english-certification/EcWordFillParagraph.js +56 -1
  592. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +192 -1
  593. package/dist/components/themes/english-certification/EcWritingTask.js +60 -1
  594. package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +108 -1
  595. package/dist/components/themes/english-certification/EnglishCertificationBrand.js +11 -1
  596. package/dist/components/themes/english-certification/EnglishCertificationExamLayout.js +34 -1
  597. package/dist/components/themes/english-certification/EnglishCertificationFlagButton.js +15 -1
  598. package/dist/components/themes/english-certification/EnglishCertificationFooterBar.js +19 -1
  599. package/dist/components/themes/english-certification/EnglishCertificationGroupedQuestion.js +166 -1
  600. package/dist/components/themes/english-certification/EnglishCertificationHeader.js +31 -1
  601. package/dist/components/themes/english-certification/EnglishCertificationInstructionBanner.js +79 -1
  602. package/dist/components/themes/english-certification/EnglishCertificationNavigationPanel.js +71 -1
  603. package/dist/components/themes/english-certification/EnglishCertificationOptionSelector.js +79 -1
  604. package/dist/components/themes/english-certification/EnglishCertificationPartHeader.js +47 -1
  605. package/dist/components/themes/english-certification/EnglishCertificationPassagePanel.js +77 -1
  606. package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.js +25 -1
  607. package/dist/components/themes/english-certification/EnglishCertificationQuestionItem.js +36 -1
  608. package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +81 -1
  609. package/dist/components/themes/english-certification/EnglishCertificationReadingSection.js +95 -1
  610. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestion.js +18 -1
  611. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js +32 -1
  612. package/dist/components/themes/english-certification/english-cert-scroll.js +163 -1
  613. package/dist/components/themes/english-certification/english-certification-theme.js +58 -1
  614. package/dist/components/themes/english-certification/grouped-question-utils.js +243 -1
  615. package/dist/components/themes/english-certification/index.js +24 -1
  616. package/dist/components/themes/index.js +25 -1
  617. package/dist/components/themes/summer-sky/SummerSkyAudioPlayer.js +170 -1
  618. package/dist/components/themes/summer-sky/SummerSkyExitModal.js +21 -1
  619. package/dist/components/themes/summer-sky/SummerSkyLayout.js +221 -1
  620. package/dist/components/themes/summer-sky/SummerSkySubmitModal.js +18 -1
  621. package/dist/components/themes/summer-sky/SummerSkyWaitingRoom.js +193 -1
  622. package/dist/components/themes/types.js +1 -1
  623. package/dist/components/ui/alert-dialog.js +26 -1
  624. package/dist/components/ui/alert.js +22 -1
  625. package/dist/components/ui/avatar.js +12 -1
  626. package/dist/components/ui/badge.js +38 -1
  627. package/dist/components/ui/button.js +35 -1
  628. package/dist/components/ui/calendar.js +76 -1
  629. package/dist/components/ui/card.js +16 -1
  630. package/dist/components/ui/carousel.js +91 -1
  631. package/dist/components/ui/checkbox.js +9 -1
  632. package/dist/components/ui/collapsible.js +13 -1
  633. package/dist/components/ui/command.js +34 -1
  634. package/dist/components/ui/confirm-dialog.js +49 -1
  635. package/dist/components/ui/dialog.js +23 -1
  636. package/dist/components/ui/drawer.js +35 -1
  637. package/dist/components/ui/dropdown-menu.js +33 -1
  638. package/dist/components/ui/file-upload.js +155 -1
  639. package/dist/components/ui/form.js +62 -1
  640. package/dist/components/ui/hover-card.js +10 -1
  641. package/dist/components/ui/image-preview.js +29 -1
  642. package/dist/components/ui/input.js +8 -1
  643. package/dist/components/ui/label.js +9 -1
  644. package/dist/components/ui/language-switcher.js +15 -1
  645. package/dist/components/ui/multi-select.js +90 -1
  646. package/dist/components/ui/points-input.js +33 -1
  647. package/dist/components/ui/popover.js +26 -1
  648. package/dist/components/ui/progress.js +16 -1
  649. package/dist/components/ui/radio-group.js +12 -1
  650. package/dist/components/ui/scroll-area.js +11 -1
  651. package/dist/components/ui/select.js +38 -1
  652. package/dist/components/ui/separator.js +8 -1
  653. package/dist/components/ui/sheet.js +40 -1
  654. package/dist/components/ui/single-select.js +76 -1
  655. package/dist/components/ui/skeleton.js +6 -1
  656. package/dist/components/ui/switch.js +8 -1
  657. package/dist/components/ui/table.js +20 -1
  658. package/dist/components/ui/tabs.js +17 -1
  659. package/dist/components/ui/textarea.js +8 -1
  660. package/dist/components/ui/tooltip.js +11 -1
  661. package/dist/index.d.ts +3 -0
  662. package/dist/index.js +24 -1
  663. package/dist/shared/constants/ApiConstant.d.ts +1 -0
  664. package/dist/shared/constants/ApiConstant.js +278 -1
  665. package/dist/shared/constants/QueryKeyConstant.js +112 -1
  666. package/dist/shared/constants/exam-level.enum.js +41 -1
  667. package/dist/shared/constants/landings/placement-test.constants.js +431 -1
  668. package/dist/shared/constants/question-skills.js +277 -1
  669. package/dist/shared/constants/routes.js +81 -1
  670. package/dist/shared/index.js +10 -1
  671. package/dist/shared/lib/hooks/index.js +10 -1
  672. package/dist/shared/lib/hooks/useAlertDialog.js +57 -1
  673. package/dist/shared/lib/hooks/useAudioPlayCounter.js +30 -1
  674. package/dist/shared/lib/hooks/useAudioPreview.js +81 -1
  675. package/dist/shared/lib/hooks/useAudioUpload.js +24 -1
  676. package/dist/shared/lib/hooks/useConfirmDialog.js +89 -1
  677. package/dist/shared/lib/hooks/useDebouncedCallback.js +32 -1
  678. package/dist/shared/lib/hooks/useDelayedAutoPlay.js +12 -1
  679. package/dist/shared/lib/hooks/useFileUpload.js +24 -1
  680. package/dist/shared/lib/hooks/useImageUrl.js +5 -1
  681. package/dist/shared/lib/hooks/usePresignedFileUrl.js +108 -1
  682. package/dist/shared/lib/hooks/useReactHookForm.js +31 -1
  683. package/dist/shared/lib/hooks/useToast.js +62 -1
  684. package/dist/shared/lib/html-content.js +22 -1
  685. package/dist/shared/lib/i18n/index.js +54 -1
  686. package/dist/shared/lib/i18n/messages/en/admin.d.ts +11 -4
  687. package/dist/shared/lib/i18n/messages/en/admin.js +2111 -1
  688. package/dist/shared/lib/i18n/messages/en/auth.js +86 -1
  689. package/dist/shared/lib/i18n/messages/en/common.js +140 -1
  690. package/dist/shared/lib/i18n/messages/en/file-upload.js +18 -1
  691. package/dist/shared/lib/i18n/messages/en/student.js +133 -1
  692. package/dist/shared/lib/i18n/messages/en/terms.js +37 -1
  693. package/dist/shared/lib/i18n/messages/en.d.ts +11 -4
  694. package/dist/shared/lib/i18n/messages/en.js +15 -1
  695. package/dist/shared/lib/i18n/messages/vi/admin.d.ts +11 -4
  696. package/dist/shared/lib/i18n/messages/vi/admin.js +2111 -1
  697. package/dist/shared/lib/i18n/messages/vi/auth.js +86 -1
  698. package/dist/shared/lib/i18n/messages/vi/common.js +140 -1
  699. package/dist/shared/lib/i18n/messages/vi/file-upload.js +18 -1
  700. package/dist/shared/lib/i18n/messages/vi/student.js +133 -1
  701. package/dist/shared/lib/i18n/messages/vi/terms.js +37 -1
  702. package/dist/shared/lib/i18n/messages/vi.d.ts +11 -4
  703. package/dist/shared/lib/i18n/messages/vi.js +15 -1
  704. package/dist/shared/lib/i18n/types.js +1 -1
  705. package/dist/shared/lib/stores/examQuestionStore.js +212 -1
  706. package/dist/shared/lib/stores/examTakingStore.js +240 -1
  707. package/dist/shared/lib/stores/gradingTransientStore.js +39 -1
  708. package/dist/shared/lib/stores/localeStore.js +10 -1
  709. package/dist/shared/lib/themes/config/starters.config.js +71 -1
  710. package/dist/shared/lib/themes/index.js +49 -1
  711. package/dist/shared/lib/themes/types.js +2 -1
  712. package/dist/shared/lib/toast.js +43 -1
  713. package/dist/shared/lib/utils/public-page.js +44 -1
  714. package/dist/shared/lib/utils/question-reverse-transform.js +438 -1
  715. package/dist/shared/lib/utils/question-transform-types.js +1 -1
  716. package/dist/shared/lib/utils/question-transform-validation.js +194 -1
  717. package/dist/shared/lib/utils/question-transform.js +199 -1
  718. package/dist/shared/lib/utils.js +168 -1
  719. package/dist/shared/lib/validation/choose-correct-answer.validation.js +111 -1
  720. package/dist/shared/lib/validation/label-the-picture.validation.js +19 -1
  721. package/dist/shared/lib/validation/listen-and-choose-from-answer-group.validation.js +46 -1
  722. package/dist/shared/lib/validation/listen-and-choose-objects-in-scene.validation.js +21 -1
  723. package/dist/shared/lib/validation/listen-and-drag-objects-into-scene.validation.js +34 -1
  724. package/dist/shared/lib/validation/listen-and-speak-answer.validation.js +40 -1
  725. package/dist/shared/lib/validation/listen-and-speak-compare-images.validation.js +45 -1
  726. package/dist/shared/lib/validation/listen-and-speak-image-group.validation.js +19 -1
  727. package/dist/shared/lib/validation/listen-and-speak-info-exchange.validation.js +39 -1
  728. package/dist/shared/lib/validation/listen-and-speak-odd-one-out.validation.js +32 -1
  729. package/dist/shared/lib/validation/listen-and-speak-question-list.validation.js +19 -1
  730. package/dist/shared/lib/validation/listen-and-speak-with-story-images.validation.js +28 -1
  731. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +12 -1
  732. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +47 -1
  733. package/dist/shared/lib/validation/read-and-color-objects.validation.js +29 -1
  734. package/dist/shared/lib/validation/read-displayed-content.validation.js +28 -1
  735. package/dist/shared/lib/validation/speaking-conversation.validation.js +75 -1
  736. package/dist/shared/lib/validation/speaking-describe-image.validation.js +31 -1
  737. package/dist/shared/lib/validation/spontaneous-qa.validation.js +48 -1
  738. package/dist/shared/lib/validation/word-fill-paragraph-default.validation.js +38 -1
  739. package/dist/shared/lib/validation/word-fill-paragraph-with-options.validation.js +39 -1
  740. package/dist/shared/lib/validation/word-fill-paragraph.validation.js +43 -1
  741. package/dist/shared/lib/validation/word-ordering.validation.js +33 -1
  742. package/dist/shared/types/branch.types.js +6 -1
  743. package/dist/shared/types/common.types.js +49 -1
  744. package/dist/shared/types/entities/exam-schedule.types.js +5 -1
  745. package/dist/shared/types/entities/exam.types.js +1 -1
  746. package/dist/shared/types/entities/question.types.js +4 -1
  747. package/dist/shared/types/entities/result.types.js +4 -1
  748. package/dist/shared/types/entities/user.types.js +4 -1
  749. package/dist/shared/types/exam-taking.types.js +2 -1
  750. package/dist/shared/types/history.types.js +34 -1
  751. package/dist/shared/types/index.js +1 -1
  752. package/dist/shared/types/questions/choose-correct-adjective.js +4 -1
  753. package/dist/shared/types/questions/choose-the-correct-answer-group.d.ts +1 -0
  754. package/dist/shared/types/questions/choose-the-correct-answer-group.js +1 -1
  755. package/dist/shared/types/questions/choose-the-correct-answer.js +9 -1
  756. package/dist/shared/types/questions/cross-out-word-group.d.ts +2 -0
  757. package/dist/shared/types/questions/cross-out-word-group.js +1 -1
  758. package/dist/shared/types/questions/fill-missing-words-in-grid.js +11 -1
  759. package/dist/shared/types/questions/index.d.ts +1 -0
  760. package/dist/shared/types/questions/index.js +46 -1
  761. package/dist/shared/types/questions/listen-and-speak-answer.js +1 -1
  762. package/dist/shared/types/questions/listen-and-speak-compare-images.js +1 -1
  763. package/dist/shared/types/questions/listen-and-speak-image-group.js +1 -1
  764. package/dist/shared/types/questions/listen-and-speak-info-exchange.js +3 -1
  765. package/dist/shared/types/questions/listen-and-speak-odd-one-out.js +3 -1
  766. package/dist/shared/types/questions/listen-and-speak-question-list.js +1 -1
  767. package/dist/shared/types/questions/listen-and-speak-with-story-images.js +3 -1
  768. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +5 -1
  769. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +9 -1
  770. package/dist/shared/types/questions/look-picture-fill-word-hint.js +7 -1
  771. package/dist/shared/types/questions/read-displayed-content.js +1 -1
  772. package/dist/shared/types/questions/speaking-conversation.js +7 -1
  773. package/dist/shared/types/questions/speaking-describe-image.js +1 -1
  774. package/dist/shared/types/questions/spontaneous-qa.js +1 -1
  775. package/dist/shared/types/questions/word-order-and-match-group.d.ts +33 -0
  776. package/dist/shared/types/questions/word-order-and-match-group.js +1 -0
  777. package/dist/shared/types/questions/write-correct-verb-form.js +12 -1
  778. package/dist/shared/types/role.types.js +1 -1
  779. package/dist/shared/types/structure-management.js +28 -1
  780. package/dist/shared/types/student.types.js +57 -1
  781. package/dist/shared/types/teacher-dashboard-rooms.js +2 -1
  782. package/package.json +184 -166
  783. package/dist/mcp/config.d.ts +0 -12
  784. package/dist/mcp/config.js +0 -1
  785. package/dist/mcp/http-client.d.ts +0 -6
  786. package/dist/mcp/http-client.js +0 -1
  787. package/dist/mcp/index.d.ts +0 -6
  788. package/dist/mcp/index.js +0 -2
  789. package/dist/mcp/tools/_helpers.d.ts +0 -4
  790. package/dist/mcp/tools/_helpers.js +0 -1
  791. package/dist/mcp/tools/exams.d.ts +0 -2
  792. package/dist/mcp/tools/exams.js +0 -1
  793. package/dist/mcp/tools/index.d.ts +0 -2
  794. package/dist/mcp/tools/index.js +0 -1
  795. package/dist/mcp/tools/results.d.ts +0 -2
  796. package/dist/mcp/tools/results.js +0 -1
@@ -1 +1,104 @@
1
- "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Check as t,X as l}from"lucide-react";import{ResolvedImage as a}from"../../../components/common/ResolvedImage";import i from"../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import{cn as d}from"../../../shared/lib/utils";import{ReviewListeningHeader as s,ListeningLegend as n,getListeningStatus as o}from"./ReviewListeningHeader";function c(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function m(e,r){return c(e)&&"string"==typeof e[r]?e[r]:""}export function ReviewListenChooseObjectsRenderer({partNumber:h,questionCount:p,partName:u,questions:b,studentAnswers:g,rawCorrectAnswerMap:f,isCorrectMap:x,questionScoreMap:w}){return r("div",{className:"flex flex-col gap-6 pt-4",children:[e(i,{partNumber:h,questionCount:p,partName:u,isReviewMode:!0}),b.map(i=>{const h=function(e){let r=e?.payload;if("string"==typeof r)try{r=JSON.parse(r)}catch{return{}}if(!c(r))return{};const t=Array.isArray(r.objectNodes)?r.objectNodes.flatMap(e=>c(e)&&"string"==typeof e.id?[{id:e.id,label:"string"==typeof e.label?e.label:"",imageUrl:"string"==typeof e.imageUrl?e.imageUrl:"",x:"number"==typeof e.x?e.x:0,y:"number"==typeof e.y?e.y:0,width:"number"==typeof e.width?e.width:0,height:"number"==typeof e.height?e.height:0}]:[]):[];return{backgroundImageUrl:"string"==typeof r.backgroundImageUrl?r.backgroundImageUrl:void 0,objectNodes:t}}(i.questionGroup),p=h.objectNodes??[],u=h.backgroundImageUrl??"",b=m(i.content,"audioUrl")||m(i.content,"audio"),N=m(g[i.id],"objectId"),y=m(f[i.id],"objectId"),v=x[i.id]??null,j=o(v,!!N),k=w[i.id]??{score:null,maxScore:Number(i.total_points||0)},U=p.find(e=>e.id===y),q=p.find(e=>e.id===N);return r("div",{className:"rounded-2xl border border-slate-100 bg-slate-50/60 p-4 md:p-5",children:[e(s,{questionNumber:i.question_number,typeLabel:"Listen & choose object",status:j,score:k.score,maxScore:k.maxScore,audioKey:b}),e(n,{showCorrect:!0,showStudent:!!N&&!v}),e("div",{className:"mt-3 overflow-hidden rounded-xl border border-slate-200 bg-white",children:r("div",{className:"relative w-full",style:{aspectRatio:"16 / 9"},children:[u?e(a,{src:u,alt:"Scene",className:"absolute inset-0 h-full w-full object-contain",draggable:!1}):e("div",{className:"absolute inset-0 flex items-center justify-center text-sm text-slate-400",children:"No scene image"}),p.map(i=>{const s=i.id===y,n=i.id===N;let o="",c=null,m=i.label;return s&&n?(o="ring-4 ring-emerald-500 shadow-lg shadow-emerald-500/30",m=`${i.label} – Đáp án đúng (bạn đã chọn)`,c=e("span",{className:"absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500 text-white shadow ring-2 ring-white",children:e(t,{className:"h-3.5 w-3.5",strokeWidth:3})})):s?(o="ring-4 ring-emerald-500 shadow-lg shadow-emerald-500/30",m=`${i.label} – Đáp án đúng`,c=e("span",{className:"absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500 text-white shadow ring-2 ring-white",children:e(t,{className:"h-3.5 w-3.5",strokeWidth:3})})):n&&(o="ring-4 ring-red-500 shadow-lg shadow-red-500/30",m=`${i.label} – Lựa chọn của bạn (sai)`,c=e("span",{className:"absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-red-500 text-white shadow ring-2 ring-white",children:e(l,{className:"h-3.5 w-3.5",strokeWidth:3})})),e("div",{className:"absolute",style:{left:`${i.x}%`,top:`${i.y}%`,width:`${i.width}%`,height:`${i.height}%`},"aria-label":m,children:r("div",{className:d("relative h-full w-full rounded-md transition",o,!o&&"rounded-md border border-dashed border-slate-300/80 bg-white/40"),children:[e("div",{className:d("h-full w-full overflow-hidden rounded-md",!o&&"opacity-60"),children:i.imageUrl?e(a,{src:i.imageUrl,alt:i.label,className:"h-full w-full object-contain",draggable:!1}):e("div",{className:"flex h-full w-full items-center justify-center bg-white/60 text-[10px] text-slate-500",children:i.label})}),c]})},i.id)})]})}),r("div","correct"===j?{className:"mt-3 rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2 text-sm",children:[e("p",{className:"text-[11px] font-semibold uppercase tracking-wide text-emerald-700",children:"Đáp án đúng"}),e("p",{className:"mt-0.5 text-emerald-800",children:U?.label||y||"—"})]}:"unanswered"===j?{className:"mt-3 grid grid-cols-1 gap-2 text-sm sm:grid-cols-2",children:[r("div",{className:"rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2",children:[e("p",{className:"text-[11px] font-semibold uppercase tracking-wide text-emerald-700",children:"Đáp án đúng"}),e("p",{className:"mt-0.5 text-emerald-800",children:U?.label||y||"—"})]}),r("div",{className:"rounded-lg border border-slate-200 bg-white px-3 py-2",children:[e("p",{className:"text-[11px] font-semibold uppercase tracking-wide text-slate-500",children:"Đáp án của bạn"}),e("p",{className:"mt-0.5 italic text-slate-500",children:"Bạn chưa trả lời"})]})]}:{className:"mt-3 grid grid-cols-1 gap-2 text-sm sm:grid-cols-2",children:[r("div",{className:"rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2",children:[e("p",{className:"text-[11px] font-semibold uppercase tracking-wide text-emerald-700",children:"Đáp án đúng"}),e("p",{className:"mt-0.5 text-emerald-800",children:U?.label||y||"—"})]}),r("div",{className:"rounded-lg border border-red-200 bg-red-50 px-3 py-2",children:[e("p",{className:"text-[11px] font-semibold uppercase tracking-wide text-red-700",children:"Đáp án của bạn"}),e("p",{className:"mt-0.5 text-red-800",children:q?.label||N||"—"})]})]})]},i.id)})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * ReviewListenChooseObjectsRenderer
5
+ *
6
+ * Renders LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE questions in review mode.
7
+ * For each question we show:
8
+ * - Header (number / type badge / status / score / audio)
9
+ * - Scene background with object overlays positioned via x/y/width/height (%)
10
+ * - The correct object is ringed in emerald
11
+ * - The student's pick is ringed in red when incorrect (green when correct)
12
+ */
13
+ import { Check, X } from 'lucide-react';
14
+ import { ResolvedImage } from '../../../components/common/ResolvedImage';
15
+ import CambridgeYlePartBanner from '../../../components/themes/cambridge-yle/CambridgeYlePartBanner';
16
+ import { cn } from '../../../shared/lib/utils';
17
+ import { ReviewListeningHeader, ListeningLegend, getListeningStatus } from './ReviewListeningHeader';
18
+ function isRecord(value) {
19
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
20
+ }
21
+ function readString(value, key) {
22
+ if (!isRecord(value))
23
+ return '';
24
+ return typeof value[key] === 'string' ? value[key] : '';
25
+ }
26
+ function parsePayload(group) {
27
+ let payload = group?.payload;
28
+ if (typeof payload === 'string') {
29
+ try {
30
+ payload = JSON.parse(payload);
31
+ }
32
+ catch {
33
+ return {};
34
+ }
35
+ }
36
+ if (!isRecord(payload))
37
+ return {};
38
+ const objectNodes = Array.isArray(payload.objectNodes)
39
+ ? payload.objectNodes.flatMap((value) => {
40
+ if (!isRecord(value) || typeof value.id !== 'string')
41
+ return [];
42
+ return [{
43
+ id: value.id,
44
+ label: typeof value.label === 'string' ? value.label : '',
45
+ imageUrl: typeof value.imageUrl === 'string' ? value.imageUrl : '',
46
+ x: typeof value.x === 'number' ? value.x : 0,
47
+ y: typeof value.y === 'number' ? value.y : 0,
48
+ width: typeof value.width === 'number' ? value.width : 0,
49
+ height: typeof value.height === 'number' ? value.height : 0,
50
+ }];
51
+ })
52
+ : [];
53
+ return {
54
+ backgroundImageUrl: typeof payload.backgroundImageUrl === 'string' ? payload.backgroundImageUrl : undefined,
55
+ objectNodes,
56
+ };
57
+ }
58
+ export function ReviewListenChooseObjectsRenderer({ partNumber, questionCount, partName, questions, studentAnswers, rawCorrectAnswerMap, isCorrectMap, questionScoreMap, }) {
59
+ return (_jsxs("div", { className: "flex flex-col gap-6 pt-4", children: [_jsx(CambridgeYlePartBanner, { partNumber: partNumber, questionCount: questionCount, partName: partName, isReviewMode: true }), questions.map((question) => {
60
+ const payload = parsePayload(question.questionGroup);
61
+ const objectNodes = payload.objectNodes ?? [];
62
+ const backgroundUrl = payload.backgroundImageUrl ?? '';
63
+ const audioKey = readString(question.content, 'audioUrl') ||
64
+ readString(question.content, 'audio');
65
+ const studentObjectId = readString(studentAnswers[question.id], 'objectId');
66
+ const correctObjectId = readString(rawCorrectAnswerMap[question.id], 'objectId');
67
+ const isCorrect = isCorrectMap[question.id] ?? null;
68
+ const status = getListeningStatus(isCorrect, !!studentObjectId);
69
+ const scoreInfo = questionScoreMap[question.id] ?? {
70
+ score: null,
71
+ maxScore: Number(question.total_points || 0),
72
+ };
73
+ const correctNode = objectNodes.find((n) => n.id === correctObjectId);
74
+ const studentNode = objectNodes.find((n) => n.id === studentObjectId);
75
+ return (_jsxs("div", { className: "rounded-2xl border border-slate-100 bg-slate-50/60 p-4 md:p-5", children: [_jsx(ReviewListeningHeader, { questionNumber: question.question_number, typeLabel: "Listen & choose object", status: status, score: scoreInfo.score, maxScore: scoreInfo.maxScore, audioKey: audioKey }), _jsx(ListeningLegend, { showCorrect: true, showStudent: !!studentObjectId && !isCorrect }), _jsx("div", { className: "mt-3 overflow-hidden rounded-xl border border-slate-200 bg-white", children: _jsxs("div", { className: "relative w-full", style: { aspectRatio: '16 / 9' }, children: [backgroundUrl ? (_jsx(ResolvedImage, { src: backgroundUrl, alt: "Scene", className: "absolute inset-0 h-full w-full object-contain", draggable: false })) : (_jsx("div", { className: "absolute inset-0 flex items-center justify-center text-sm text-slate-400", children: "No scene image" })), objectNodes.map((node) => {
76
+ const isCorrectNode = node.id === correctObjectId;
77
+ const isStudentNode = node.id === studentObjectId;
78
+ let ring = '';
79
+ let badge = null;
80
+ let ariaLabel = node.label;
81
+ if (isCorrectNode && isStudentNode) {
82
+ ring = 'ring-4 ring-emerald-500 shadow-lg shadow-emerald-500/30';
83
+ ariaLabel = `${node.label} – Đáp án đúng (bạn đã chọn)`;
84
+ badge = (_jsx("span", { className: "absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500 text-white shadow ring-2 ring-white", children: _jsx(Check, { className: "h-3.5 w-3.5", strokeWidth: 3 }) }));
85
+ }
86
+ else if (isCorrectNode) {
87
+ ring = 'ring-4 ring-emerald-500 shadow-lg shadow-emerald-500/30';
88
+ ariaLabel = `${node.label} – Đáp án đúng`;
89
+ badge = (_jsx("span", { className: "absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500 text-white shadow ring-2 ring-white", children: _jsx(Check, { className: "h-3.5 w-3.5", strokeWidth: 3 }) }));
90
+ }
91
+ else if (isStudentNode) {
92
+ ring = 'ring-4 ring-red-500 shadow-lg shadow-red-500/30';
93
+ ariaLabel = `${node.label} – Lựa chọn của bạn (sai)`;
94
+ badge = (_jsx("span", { className: "absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-red-500 text-white shadow ring-2 ring-white", children: _jsx(X, { className: "h-3.5 w-3.5", strokeWidth: 3 }) }));
95
+ }
96
+ return (_jsx("div", { className: "absolute", style: {
97
+ left: `${node.x}%`,
98
+ top: `${node.y}%`,
99
+ width: `${node.width}%`,
100
+ height: `${node.height}%`,
101
+ }, "aria-label": ariaLabel, children: _jsxs("div", { className: cn('relative h-full w-full rounded-md transition', ring, !ring && 'rounded-md border border-dashed border-slate-300/80 bg-white/40'), children: [_jsx("div", { className: cn('h-full w-full overflow-hidden rounded-md', !ring && 'opacity-60'), children: node.imageUrl ? (_jsx(ResolvedImage, { src: node.imageUrl, alt: node.label, className: "h-full w-full object-contain", draggable: false })) : (_jsx("div", { className: "flex h-full w-full items-center justify-center bg-white/60 text-[10px] text-slate-500", children: node.label })) }), badge] }) }, node.id));
102
+ })] }) }), status === 'correct' ? (_jsxs("div", { className: "mt-3 rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2 text-sm", children: [_jsx("p", { className: "text-[11px] font-semibold uppercase tracking-wide text-emerald-700", children: "\u0110\u00E1p \u00E1n \u0111\u00FAng" }), _jsx("p", { className: "mt-0.5 text-emerald-800", children: correctNode?.label || correctObjectId || '—' })] })) : status === 'unanswered' ? (_jsxs("div", { className: "mt-3 grid grid-cols-1 gap-2 text-sm sm:grid-cols-2", children: [_jsxs("div", { className: "rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2", children: [_jsx("p", { className: "text-[11px] font-semibold uppercase tracking-wide text-emerald-700", children: "\u0110\u00E1p \u00E1n \u0111\u00FAng" }), _jsx("p", { className: "mt-0.5 text-emerald-800", children: correctNode?.label || correctObjectId || '—' })] }), _jsxs("div", { className: "rounded-lg border border-slate-200 bg-white px-3 py-2", children: [_jsx("p", { className: "text-[11px] font-semibold uppercase tracking-wide text-slate-500", children: "\u0110\u00E1p \u00E1n c\u1EE7a b\u1EA1n" }), _jsx("p", { className: "mt-0.5 italic text-slate-500", children: "B\u1EA1n ch\u01B0a tr\u1EA3 l\u1EDDi" })] })] })) : (_jsxs("div", { className: "mt-3 grid grid-cols-1 gap-2 text-sm sm:grid-cols-2", children: [_jsxs("div", { className: "rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2", children: [_jsx("p", { className: "text-[11px] font-semibold uppercase tracking-wide text-emerald-700", children: "\u0110\u00E1p \u00E1n \u0111\u00FAng" }), _jsx("p", { className: "mt-0.5 text-emerald-800", children: correctNode?.label || correctObjectId || '—' })] }), _jsxs("div", { className: "rounded-lg border border-red-200 bg-red-50 px-3 py-2", children: [_jsx("p", { className: "text-[11px] font-semibold uppercase tracking-wide text-red-700", children: "\u0110\u00E1p \u00E1n c\u1EE7a b\u1EA1n" }), _jsx("p", { className: "mt-0.5 text-red-800", children: studentNode?.label || studentObjectId || '—' })] })] }))] }, question.id));
103
+ })] }));
104
+ }
@@ -1 +1,123 @@
1
- "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Check as t,X as a}from"lucide-react";import{ResolvedImage as i}from"../../../components/common/ResolvedImage";import l from"../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import{cn as d}from"../../../shared/lib/utils";import{ReviewListeningHeader as s,ListeningLegend as n,getListeningStatus as o}from"./ReviewListeningHeader";function c(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function m(e,r){return c(e)&&"string"==typeof e[r]?e[r]:""}function u({zone:l,studentObject:s,correctObject:n,isStudentZone:o,isCorrectZone:c,isStudentCorrect:m}){let u,b="border-2 border-dashed border-slate-300/80 bg-white/30",p=null;return c&&o?(b=m?"border-2 border-emerald-500 bg-emerald-50/70 ring-2 ring-emerald-400/40":"border-2 border-red-500 bg-red-50/70 ring-2 ring-red-400/40",p=e("span",m?{className:"absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500 text-white shadow ring-2 ring-white",children:e(t,{className:"h-3.5 w-3.5",strokeWidth:3})}:{className:"absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-red-500 text-white shadow ring-2 ring-white",children:e(a,{className:"h-3.5 w-3.5",strokeWidth:3})}),u=s):o?(b="border-2 border-red-500 bg-red-50/70 ring-2 ring-red-400/40",p=e("span",{className:"absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-red-500 text-white shadow ring-2 ring-white",children:e(a,{className:"h-3.5 w-3.5",strokeWidth:3})}),u=s):c&&(b="border-2 border-emerald-500 bg-emerald-50/70 ring-2 ring-emerald-400/40",p=e("span",{className:"absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500 text-white shadow ring-2 ring-white",children:e(t,{className:"h-3.5 w-3.5",strokeWidth:3})}),u=n),e("div",{className:"absolute",style:{left:`${l.x}%`,top:`${l.y}%`,width:`${l.width}%`,height:`${l.height}%`},children:r("div",{className:d("relative flex h-full w-full items-center justify-center rounded-md",b),children:[u?.imageUrl?e(i,{src:u.imageUrl,alt:u.label,className:"h-full w-full object-contain p-1",draggable:!1}):e("span",{className:"px-1 text-center text-[10px] font-medium text-slate-500",children:l.label}),p]})})}export function ReviewListenDragObjectsRenderer({partNumber:t,questionCount:a,partName:b,questions:p,studentAnswers:h,rawCorrectAnswerMap:g,isCorrectMap:f,questionScoreMap:x}){return r("div",{className:"flex flex-col gap-6 pt-4",children:[e(l,{partNumber:t,questionCount:a,partName:b,isReviewMode:!0}),p.map(t=>{const a=function(e){let r=e?.payload;if("string"==typeof r)try{r=JSON.parse(r)}catch{return{}}if(!c(r))return{};const t=Array.isArray(r.objectNodes)?r.objectNodes.flatMap(e=>c(e)&&"string"==typeof e.id?[{id:e.id,label:"string"==typeof e.label?e.label:"",imageUrl:"string"==typeof e.imageUrl?e.imageUrl:""}]:[]):[],a=Array.isArray(r.dropZones)?r.dropZones.flatMap(e=>c(e)&&"string"==typeof e.id?[{id:e.id,label:"string"==typeof e.label?e.label:"",x:"number"==typeof e.x?e.x:0,y:"number"==typeof e.y?e.y:0,width:"number"==typeof e.width?e.width:0,height:"number"==typeof e.height?e.height:0}]:[]):[];return{backgroundImageUrl:"string"==typeof r.backgroundImageUrl?r.backgroundImageUrl:void 0,objectNodes:t,dropZones:a}}(t.questionGroup),l=a.objectNodes??[],b=a.dropZones??[],p=a.backgroundImageUrl??"",N=m(t.content,"audioUrl")||m(t.content,"audio"),w=m(h[t.id],"objectId"),y=m(h[t.id],"dropZoneId"),j=m(g[t.id],"objectId"),v=m(g[t.id],"dropZoneId"),k=!!w||!!y,S=f[t.id]??null,C=o(S,k),U=x[t.id]??{score:null,maxScore:Number(t.total_points||0)},Z=l.find(e=>e.id===w),I=l.find(e=>e.id===j),A=b.find(e=>e.id===y),q=b.find(e=>e.id===v);return r("div",{className:"rounded-2xl border border-slate-100 bg-slate-50/60 p-4 md:p-5",children:[e(s,{questionNumber:t.question_number,typeLabel:"Listen & drag object",status:C,score:U.score,maxScore:U.maxScore,audioKey:N}),e(n,{showCorrect:!0,showStudent:k&&!S}),e("div",{className:"mt-3 overflow-hidden rounded-xl border border-slate-200 bg-white",children:r("div",{className:"relative w-full",style:{aspectRatio:"16 / 9"},children:[p?e(i,{src:p,alt:"Scene",className:"absolute inset-0 h-full w-full object-contain",draggable:!1}):e("div",{className:"absolute inset-0 flex items-center justify-center text-sm text-slate-400",children:"No scene image"}),b.map(r=>e(u,{zone:r,studentObject:Z,correctObject:I,isStudentZone:r.id===y,isCorrectZone:r.id===v,isStudentCorrect:!!S},r.id))]})}),r("div","correct"===C?{className:"mt-3 rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2 text-sm",children:[e("p",{className:"text-[11px] font-semibold uppercase tracking-wide text-emerald-700",children:"Vị trí đúng"}),r("p",{className:"mt-0.5 text-emerald-800",children:[e("span",{className:"font-medium",children:I?.label||j||"—"}),e("span",{className:"mx-1.5 text-emerald-500",children:"→"}),e("span",{children:q?.label||v||"—"})]})]}:{className:"mt-3 grid grid-cols-1 gap-2 text-sm sm:grid-cols-2",children:[r("div",{className:"rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2",children:[e("p",{className:"text-[11px] font-semibold uppercase tracking-wide text-emerald-700",children:"Vị trí đúng"}),r("p",{className:"mt-0.5 text-emerald-800",children:[e("span",{className:"font-medium",children:I?.label||j||"—"}),e("span",{className:"mx-1.5 text-emerald-500",children:"→"}),e("span",{children:q?.label||v||"—"})]})]}),r("div",{className:d("rounded-lg border px-3 py-2","incorrect"===C?"border-red-200 bg-red-50":"border-slate-200 bg-white"),children:[e("p",{className:d("text-[11px] font-semibold uppercase tracking-wide","incorrect"===C?"text-red-700":"text-slate-500"),children:"Vị trí của bạn"}),k?r("p",{className:"mt-0.5 text-red-800",children:[e("span",{className:"font-medium",children:Z?.label||w||"—"}),e("span",{className:"mx-1.5 text-red-500",children:"→"}),e("span",{children:A?.label||y||"—"})]}):e("p",{className:"mt-0.5 italic text-slate-500",children:"Bạn chưa trả lời"})]})]})]},t.id)})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * ReviewListenDragObjectsRenderer
5
+ *
6
+ * Renders LISTEN_AND_DRAG_OBJECTS_INTO_SCENE questions in review mode.
7
+ * Shows the scene background with drop zones outlined, and visualises:
8
+ * - The correct object placed in the correct drop zone (emerald)
9
+ * - The student's placement (red if incorrect, green if correct)
10
+ * - A summary table showing object → drop zone for both student and correct.
11
+ */
12
+ import { Check, X } from 'lucide-react';
13
+ import { ResolvedImage } from '../../../components/common/ResolvedImage';
14
+ import CambridgeYlePartBanner from '../../../components/themes/cambridge-yle/CambridgeYlePartBanner';
15
+ import { cn } from '../../../shared/lib/utils';
16
+ import { ReviewListeningHeader, ListeningLegend, getListeningStatus } from './ReviewListeningHeader';
17
+ function isRecord(value) {
18
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
19
+ }
20
+ function readString(value, key) {
21
+ if (!isRecord(value))
22
+ return '';
23
+ return typeof value[key] === 'string' ? value[key] : '';
24
+ }
25
+ function parsePayload(group) {
26
+ let payload = group?.payload;
27
+ if (typeof payload === 'string') {
28
+ try {
29
+ payload = JSON.parse(payload);
30
+ }
31
+ catch {
32
+ return {};
33
+ }
34
+ }
35
+ if (!isRecord(payload))
36
+ return {};
37
+ const objectNodes = Array.isArray(payload.objectNodes)
38
+ ? payload.objectNodes.flatMap((value) => {
39
+ if (!isRecord(value) || typeof value.id !== 'string')
40
+ return [];
41
+ return [{
42
+ id: value.id,
43
+ label: typeof value.label === 'string' ? value.label : '',
44
+ imageUrl: typeof value.imageUrl === 'string' ? value.imageUrl : '',
45
+ }];
46
+ })
47
+ : [];
48
+ const dropZones = Array.isArray(payload.dropZones)
49
+ ? payload.dropZones.flatMap((value) => {
50
+ if (!isRecord(value) || typeof value.id !== 'string')
51
+ return [];
52
+ return [{
53
+ id: value.id,
54
+ label: typeof value.label === 'string' ? value.label : '',
55
+ x: typeof value.x === 'number' ? value.x : 0,
56
+ y: typeof value.y === 'number' ? value.y : 0,
57
+ width: typeof value.width === 'number' ? value.width : 0,
58
+ height: typeof value.height === 'number' ? value.height : 0,
59
+ }];
60
+ })
61
+ : [];
62
+ return {
63
+ backgroundImageUrl: typeof payload.backgroundImageUrl === 'string' ? payload.backgroundImageUrl : undefined,
64
+ objectNodes,
65
+ dropZones,
66
+ };
67
+ }
68
+ function DropZoneOverlay({ zone, studentObject, correctObject, isStudentZone, isCorrectZone, isStudentCorrect, }) {
69
+ let frame = 'border-2 border-dashed border-slate-300/80 bg-white/30';
70
+ let badge = null;
71
+ let displayObject;
72
+ if (isCorrectZone && isStudentZone) {
73
+ frame = isStudentCorrect
74
+ ? 'border-2 border-emerald-500 bg-emerald-50/70 ring-2 ring-emerald-400/40'
75
+ : 'border-2 border-red-500 bg-red-50/70 ring-2 ring-red-400/40';
76
+ badge = isStudentCorrect ? (_jsx("span", { className: "absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500 text-white shadow ring-2 ring-white", children: _jsx(Check, { className: "h-3.5 w-3.5", strokeWidth: 3 }) })) : (_jsx("span", { className: "absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-red-500 text-white shadow ring-2 ring-white", children: _jsx(X, { className: "h-3.5 w-3.5", strokeWidth: 3 }) }));
77
+ displayObject = studentObject;
78
+ }
79
+ else if (isStudentZone) {
80
+ frame = 'border-2 border-red-500 bg-red-50/70 ring-2 ring-red-400/40';
81
+ badge = (_jsx("span", { className: "absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-red-500 text-white shadow ring-2 ring-white", children: _jsx(X, { className: "h-3.5 w-3.5", strokeWidth: 3 }) }));
82
+ displayObject = studentObject;
83
+ }
84
+ else if (isCorrectZone) {
85
+ frame = 'border-2 border-emerald-500 bg-emerald-50/70 ring-2 ring-emerald-400/40';
86
+ badge = (_jsx("span", { className: "absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500 text-white shadow ring-2 ring-white", children: _jsx(Check, { className: "h-3.5 w-3.5", strokeWidth: 3 }) }));
87
+ displayObject = correctObject;
88
+ }
89
+ return (_jsx("div", { className: "absolute", style: {
90
+ left: `${zone.x}%`,
91
+ top: `${zone.y}%`,
92
+ width: `${zone.width}%`,
93
+ height: `${zone.height}%`,
94
+ }, children: _jsxs("div", { className: cn('relative flex h-full w-full items-center justify-center rounded-md', frame), children: [displayObject?.imageUrl ? (_jsx(ResolvedImage, { src: displayObject.imageUrl, alt: displayObject.label, className: "h-full w-full object-contain p-1", draggable: false })) : (_jsx("span", { className: "px-1 text-center text-[10px] font-medium text-slate-500", children: zone.label })), badge] }) }));
95
+ }
96
+ export function ReviewListenDragObjectsRenderer({ partNumber, questionCount, partName, questions, studentAnswers, rawCorrectAnswerMap, isCorrectMap, questionScoreMap, }) {
97
+ return (_jsxs("div", { className: "flex flex-col gap-6 pt-4", children: [_jsx(CambridgeYlePartBanner, { partNumber: partNumber, questionCount: questionCount, partName: partName, isReviewMode: true }), questions.map((question) => {
98
+ const payload = parsePayload(question.questionGroup);
99
+ const objectNodes = payload.objectNodes ?? [];
100
+ const dropZones = payload.dropZones ?? [];
101
+ const backgroundUrl = payload.backgroundImageUrl ?? '';
102
+ const audioKey = readString(question.content, 'audioUrl') ||
103
+ readString(question.content, 'audio');
104
+ const studentObjectId = readString(studentAnswers[question.id], 'objectId');
105
+ const studentZoneId = readString(studentAnswers[question.id], 'dropZoneId');
106
+ const correctObjectId = readString(rawCorrectAnswerMap[question.id], 'objectId');
107
+ const correctZoneId = readString(rawCorrectAnswerMap[question.id], 'dropZoneId');
108
+ const hasAnswered = !!studentObjectId || !!studentZoneId;
109
+ const isCorrect = isCorrectMap[question.id] ?? null;
110
+ const status = getListeningStatus(isCorrect, hasAnswered);
111
+ const scoreInfo = questionScoreMap[question.id] ?? {
112
+ score: null,
113
+ maxScore: Number(question.total_points || 0),
114
+ };
115
+ const studentObject = objectNodes.find((o) => o.id === studentObjectId);
116
+ const correctObject = objectNodes.find((o) => o.id === correctObjectId);
117
+ const studentZone = dropZones.find((z) => z.id === studentZoneId);
118
+ const correctZone = dropZones.find((z) => z.id === correctZoneId);
119
+ return (_jsxs("div", { className: "rounded-2xl border border-slate-100 bg-slate-50/60 p-4 md:p-5", children: [_jsx(ReviewListeningHeader, { questionNumber: question.question_number, typeLabel: "Listen & drag object", status: status, score: scoreInfo.score, maxScore: scoreInfo.maxScore, audioKey: audioKey }), _jsx(ListeningLegend, { showCorrect: true, showStudent: hasAnswered && !isCorrect }), _jsx("div", { className: "mt-3 overflow-hidden rounded-xl border border-slate-200 bg-white", children: _jsxs("div", { className: "relative w-full", style: { aspectRatio: '16 / 9' }, children: [backgroundUrl ? (_jsx(ResolvedImage, { src: backgroundUrl, alt: "Scene", className: "absolute inset-0 h-full w-full object-contain", draggable: false })) : (_jsx("div", { className: "absolute inset-0 flex items-center justify-center text-sm text-slate-400", children: "No scene image" })), dropZones.map((zone) => (_jsx(DropZoneOverlay, { zone: zone, studentObject: studentObject, correctObject: correctObject, isStudentZone: zone.id === studentZoneId, isCorrectZone: zone.id === correctZoneId, isStudentCorrect: !!isCorrect }, zone.id)))] }) }), status === 'correct' ? (_jsxs("div", { className: "mt-3 rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2 text-sm", children: [_jsx("p", { className: "text-[11px] font-semibold uppercase tracking-wide text-emerald-700", children: "V\u1ECB tr\u00ED \u0111\u00FAng" }), _jsxs("p", { className: "mt-0.5 text-emerald-800", children: [_jsx("span", { className: "font-medium", children: correctObject?.label || correctObjectId || '—' }), _jsx("span", { className: "mx-1.5 text-emerald-500", children: "\u2192" }), _jsx("span", { children: correctZone?.label || correctZoneId || '—' })] })] })) : (_jsxs("div", { className: "mt-3 grid grid-cols-1 gap-2 text-sm sm:grid-cols-2", children: [_jsxs("div", { className: "rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2", children: [_jsx("p", { className: "text-[11px] font-semibold uppercase tracking-wide text-emerald-700", children: "V\u1ECB tr\u00ED \u0111\u00FAng" }), _jsxs("p", { className: "mt-0.5 text-emerald-800", children: [_jsx("span", { className: "font-medium", children: correctObject?.label || correctObjectId || '—' }), _jsx("span", { className: "mx-1.5 text-emerald-500", children: "\u2192" }), _jsx("span", { children: correctZone?.label || correctZoneId || '—' })] })] }), _jsxs("div", { className: cn('rounded-lg border px-3 py-2', status === 'incorrect'
120
+ ? 'border-red-200 bg-red-50'
121
+ : 'border-slate-200 bg-white'), children: [_jsx("p", { className: cn('text-[11px] font-semibold uppercase tracking-wide', status === 'incorrect' ? 'text-red-700' : 'text-slate-500'), children: "V\u1ECB tr\u00ED c\u1EE7a b\u1EA1n" }), hasAnswered ? (_jsxs("p", { className: "mt-0.5 text-red-800", children: [_jsx("span", { className: "font-medium", children: studentObject?.label || studentObjectId || '—' }), _jsx("span", { className: "mx-1.5 text-red-500", children: "\u2192" }), _jsx("span", { children: studentZone?.label || studentZoneId || '—' })] })) : (_jsx("p", { className: "mt-0.5 italic text-slate-500", children: "B\u1EA1n ch\u01B0a tr\u1EA3 l\u1EDDi" }))] })] }))] }, question.id));
122
+ })] }));
123
+ }
@@ -1 +1,104 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{Check as r,Volume2 as n,X as a,Pause as s,Play as l}from"lucide-react";import{useCallback as i,useEffect as d,useRef as c,useState as o}from"react";import{Badge as u}from"../../../components/ui/badge";import{cn as m}from"../../../shared/lib/utils";import{usePresignedFileUrl as x}from"../../../shared/lib/hooks/usePresignedFileUrl";export function getListeningStatus(e,t){return t?!0===e?"correct":!1===e?"incorrect":"unanswered":"unanswered"}export function ListeningStatusBadge({status:n}){return t("span","correct"===n?{className:"inline-flex items-center gap-1 rounded-full border border-emerald-200 bg-emerald-50 px-2 py-0.5 text-xs font-semibold text-emerald-700",children:[e("span",{className:"flex h-4 w-4 items-center justify-center rounded-full bg-emerald-500",children:e(r,{className:"h-2.5 w-2.5 text-white",strokeWidth:3})}),"Đúng"]}:"incorrect"===n?{className:"inline-flex items-center gap-1 rounded-full border border-red-200 bg-red-50 px-2 py-0.5 text-xs font-semibold text-red-700",children:[e("span",{className:"flex h-4 w-4 items-center justify-center rounded-full bg-red-500",children:e(a,{className:"h-2.5 w-2.5 text-white",strokeWidth:3})}),"Sai"]}:{className:"inline-flex items-center gap-1 rounded-full border border-slate-200 bg-slate-50 px-2 py-0.5 text-xs font-semibold text-slate-600",children:[e("span",{className:"flex h-4 w-4 items-center justify-center rounded-full bg-slate-400",children:e(a,{className:"h-2.5 w-2.5 text-white",strokeWidth:3})}),"Chưa trả lời"]})}export function ListeningScorePill({score:r,maxScore:n,status:a}){const s=null==r?"—":r;return t("div",{className:m("flex flex-shrink-0 items-baseline gap-1 rounded-full border px-3 py-1","correct"===a?"border-emerald-200 bg-emerald-50 text-emerald-700":"incorrect"===a?"border-red-200 bg-red-50 text-red-700":"border-slate-200 bg-slate-50 text-slate-600"),children:[e("span",{className:"text-sm font-bold leading-none",children:s}),t("span",{className:"text-xs font-medium opacity-80",children:["/ ",n," điểm"]})]})}function h(e){if(!isFinite(e))return"0:00";return`${Math.floor(e/60)}:${Math.floor(e%60).toString().padStart(2,"0")}`}export function ListeningAudioBar({audioKey:r}){const{previewUrl:a}=x(r),u=c(null),[m,p]=o(!1),[f,b]=o(0),[g,w]=o(0),N=i(()=>{const e=u.current;e&&(m?e.pause():e.play().catch(e=>{console.warn("Audio play request was interrupted:",e)}))},[m]),y=i(e=>{const t=u.current;if(!t)return;const r=Number(e.target.value);t.currentTime=r,b(r)},[]);return d(()=>{p(!1),b(0)},[a]),t("div",{className:"flex items-center gap-3 rounded-lg border border-slate-200 bg-white px-3 py-2",children:[a&&e("audio",{ref:u,src:a,onPlay:()=>p(!0),onPause:()=>p(!1),onEnded:()=>p(!1),onTimeUpdate:e=>b(e.currentTarget.currentTime),onLoadedMetadata:e=>w(e.currentTarget.duration),preload:"metadata"}),e("div",{className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-50 text-blue-600",children:e(n,{className:"h-4 w-4"})}),e("button",{type:"button",onClick:N,disabled:!a,className:"flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full bg-blue-600 text-white shadow-sm transition hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-40","aria-label":m?"Tạm dừng":"Phát âm thanh câu hỏi",children:m?e(s,{className:"h-4 w-4"}):e(l,{className:"ml-0.5 h-4 w-4"})}),e("span",{className:"w-10 text-xs tabular-nums text-slate-500",children:h(f)}),e("input",{type:"range",min:0,max:g||0,step:.01,value:f,onChange:y,className:"h-1 flex-1 cursor-pointer appearance-none rounded-full bg-slate-200 accent-blue-600","aria-label":"Tua âm thanh"}),e("span",{className:"w-10 text-right text-xs tabular-nums text-slate-500",children:h(g)})]})}export function ReviewListeningHeader({questionNumber:r,typeLabel:n,status:a,score:s,maxScore:l,audioKey:i,title:d,rightExtra:c}){return t("div",{className:"mb-3 space-y-3",children:[t("div",{className:"flex items-start gap-3",children:[e("span",{className:"mt-0.5 flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-sm font-bold text-white",children:r}),t("div",{className:"min-w-0 flex-1",children:[t("div",{className:"flex flex-wrap items-center gap-2",children:[e(u,{variant:"secondary",className:"bg-slate-100 text-slate-700 hover:bg-slate-100",children:n}),e(ListeningStatusBadge,{status:a}),c]}),d?e("p",{className:"mt-1.5 text-sm font-medium text-slate-700",children:d}):null]}),e(ListeningScorePill,{score:s,maxScore:l,status:a})]}),i?e(ListeningAudioBar,{audioKey:i}):null]})}export function ListeningLegend({showCorrect:r=!0,showStudent:n=!0}){return r||n?t("div",{className:"flex flex-wrap items-center gap-3 text-xs text-slate-600",children:[r?t("span",{className:"inline-flex items-center gap-1.5",children:[e("span",{className:"inline-block h-3 w-3 rounded-full border-2 border-emerald-500 bg-emerald-50"}),"Đáp án đúng"]}):null,n?t("span",{className:"inline-flex items-center gap-1.5",children:[e("span",{className:"inline-block h-3 w-3 rounded-full border-2 border-red-500 bg-red-50"}),"Đáp án của bạn"]}):null]}):null}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /**
4
+ * ReviewListeningHeader
5
+ *
6
+ * Shared per-question header for the three listening question reviewers:
7
+ * - LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE
8
+ * - LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP
9
+ * - LISTEN_AND_DRAG_OBJECTS_INTO_SCENE
10
+ *
11
+ * Renders question number, type badge, textual+icon correctness label, score
12
+ * pill, a compact audio prompt bar (speaker icon, no recording chrome) and a
13
+ * legend for the visualization (Correct / Your answer).
14
+ */
15
+ import { Check, Volume2, X, Pause, Play } from 'lucide-react';
16
+ import { useCallback, useEffect, useRef, useState } from 'react';
17
+ import { Badge } from '../../../components/ui/badge';
18
+ import { cn } from '../../../shared/lib/utils';
19
+ import { usePresignedFileUrl } from '../../../shared/lib/hooks/usePresignedFileUrl';
20
+ export function getListeningStatus(isCorrect, hasStudentAnswer) {
21
+ if (!hasStudentAnswer)
22
+ return 'unanswered';
23
+ if (isCorrect === true)
24
+ return 'correct';
25
+ if (isCorrect === false)
26
+ return 'incorrect';
27
+ return 'unanswered';
28
+ }
29
+ /**
30
+ * Combined icon + textual correctness badge. Provides redundancy beyond colour
31
+ * for accessibility / quick scanning.
32
+ */
33
+ export function ListeningStatusBadge({ status }) {
34
+ if (status === 'correct') {
35
+ return (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full border border-emerald-200 bg-emerald-50 px-2 py-0.5 text-xs font-semibold text-emerald-700", children: [_jsx("span", { className: "flex h-4 w-4 items-center justify-center rounded-full bg-emerald-500", children: _jsx(Check, { className: "h-2.5 w-2.5 text-white", strokeWidth: 3 }) }), "\u0110\u00FAng"] }));
36
+ }
37
+ if (status === 'incorrect') {
38
+ return (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full border border-red-200 bg-red-50 px-2 py-0.5 text-xs font-semibold text-red-700", children: [_jsx("span", { className: "flex h-4 w-4 items-center justify-center rounded-full bg-red-500", children: _jsx(X, { className: "h-2.5 w-2.5 text-white", strokeWidth: 3 }) }), "Sai"] }));
39
+ }
40
+ return (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full border border-slate-200 bg-slate-50 px-2 py-0.5 text-xs font-semibold text-slate-600", children: [_jsx("span", { className: "flex h-4 w-4 items-center justify-center rounded-full bg-slate-400", children: _jsx(X, { className: "h-2.5 w-2.5 text-white", strokeWidth: 3 }) }), "Ch\u01B0a tr\u1EA3 l\u1EDDi"] }));
41
+ }
42
+ export function ListeningScorePill({ score, maxScore, status }) {
43
+ const tone = status === 'correct'
44
+ ? 'border-emerald-200 bg-emerald-50 text-emerald-700'
45
+ : status === 'incorrect'
46
+ ? 'border-red-200 bg-red-50 text-red-700'
47
+ : 'border-slate-200 bg-slate-50 text-slate-600';
48
+ const display = score === null || score === undefined ? '—' : score;
49
+ return (_jsxs("div", { className: cn('flex flex-shrink-0 items-baseline gap-1 rounded-full border px-3 py-1', tone), children: [_jsx("span", { className: "text-sm font-bold leading-none", children: display }), _jsxs("span", { className: "text-xs font-medium opacity-80", children: ["/ ", maxScore, " \u0111i\u1EC3m"] })] }));
50
+ }
51
+ function formatTime(s) {
52
+ if (!isFinite(s))
53
+ return '0:00';
54
+ const m = Math.floor(s / 60);
55
+ const sec = Math.floor(s % 60).toString().padStart(2, '0');
56
+ return `${m}:${sec}`;
57
+ }
58
+ /**
59
+ * Minimal audio bar tailored for listening prompts in review mode.
60
+ * Uses a speaker icon (not a microphone) so it reads as "audio to listen to",
61
+ * not "your recording".
62
+ */
63
+ export function ListeningAudioBar({ audioKey }) {
64
+ const { previewUrl } = usePresignedFileUrl(audioKey);
65
+ const audioRef = useRef(null);
66
+ const [isPlaying, setPlaying] = useState(false);
67
+ const [time, setTime] = useState(0);
68
+ const [duration, setDuration] = useState(0);
69
+ const toggle = useCallback(() => {
70
+ const el = audioRef.current;
71
+ if (!el)
72
+ return;
73
+ if (isPlaying) {
74
+ el.pause();
75
+ }
76
+ else {
77
+ el.play().catch((err) => {
78
+ console.warn('Audio play request was interrupted:', err);
79
+ });
80
+ }
81
+ }, [isPlaying]);
82
+ const seek = useCallback((e) => {
83
+ const el = audioRef.current;
84
+ if (!el)
85
+ return;
86
+ const v = Number(e.target.value);
87
+ el.currentTime = v;
88
+ setTime(v);
89
+ }, []);
90
+ useEffect(() => {
91
+ setPlaying(false);
92
+ setTime(0);
93
+ }, [previewUrl]);
94
+ return (_jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-slate-200 bg-white px-3 py-2", children: [previewUrl && (_jsx("audio", { ref: audioRef, src: previewUrl, onPlay: () => setPlaying(true), onPause: () => setPlaying(false), onEnded: () => setPlaying(false), onTimeUpdate: (e) => setTime(e.currentTarget.currentTime), onLoadedMetadata: (e) => setDuration(e.currentTarget.duration), preload: "metadata" })), _jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-50 text-blue-600", children: _jsx(Volume2, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", onClick: toggle, disabled: !previewUrl, className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full bg-blue-600 text-white shadow-sm transition hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-40", "aria-label": isPlaying ? 'Tạm dừng' : 'Phát âm thanh câu hỏi', children: isPlaying ? _jsx(Pause, { className: "h-4 w-4" }) : _jsx(Play, { className: "ml-0.5 h-4 w-4" }) }), _jsx("span", { className: "w-10 text-xs tabular-nums text-slate-500", children: formatTime(time) }), _jsx("input", { type: "range", min: 0, max: duration || 0, step: 0.01, value: time, onChange: seek, className: "h-1 flex-1 cursor-pointer appearance-none rounded-full bg-slate-200 accent-blue-600", "aria-label": "Tua \u00E2m thanh" }), _jsx("span", { className: "w-10 text-right text-xs tabular-nums text-slate-500", children: formatTime(duration) })] }));
95
+ }
96
+ export function ReviewListeningHeader({ questionNumber, typeLabel, status, score, maxScore, audioKey, title, rightExtra, }) {
97
+ return (_jsxs("div", { className: "mb-3 space-y-3", children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx("span", { className: "mt-0.5 flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-sm font-bold text-white", children: questionNumber }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Badge, { variant: "secondary", className: "bg-slate-100 text-slate-700 hover:bg-slate-100", children: typeLabel }), _jsx(ListeningStatusBadge, { status: status }), rightExtra] }), title ? (_jsx("p", { className: "mt-1.5 text-sm font-medium text-slate-700", children: title })) : null] }), _jsx(ListeningScorePill, { score: score, maxScore: maxScore, status: status })] }), audioKey ? _jsx(ListeningAudioBar, { audioKey: audioKey }) : null] }));
98
+ }
99
+ /** Small legend chip rendered above the visualization. Shown only when relevant. */
100
+ export function ListeningLegend({ showCorrect = true, showStudent = true, }) {
101
+ if (!showCorrect && !showStudent)
102
+ return null;
103
+ return (_jsxs("div", { className: "flex flex-wrap items-center gap-3 text-xs text-slate-600", children: [showCorrect ? (_jsxs("span", { className: "inline-flex items-center gap-1.5", children: [_jsx("span", { className: "inline-block h-3 w-3 rounded-full border-2 border-emerald-500 bg-emerald-50" }), "\u0110\u00E1p \u00E1n \u0111\u00FAng"] })) : null, showStudent ? (_jsxs("span", { className: "inline-flex items-center gap-1.5", children: [_jsx("span", { className: "inline-block h-3 w-3 rounded-full border-2 border-red-500 bg-red-50" }), "\u0110\u00E1p \u00E1n c\u1EE7a b\u1EA1n"] })) : null] }));
104
+ }
@@ -1 +1,116 @@
1
- "use client";import{jsx as e,jsxs as l}from"react/jsx-runtime";import{Activity as t,Award as a,Bot as r,Brain as s,MessageSquare as n,TrendingUp as c}from"lucide-react";import{cn as d}from"../../../shared/lib/utils";export function ReviewSpeakingAIPanel({question:t}){if(!t.studentAudioKey)return null;return null!=t.pronunciationScore||null!=t.accuracyScore||null!=t.fluencyScore||null!=t.prosodyScore||null!=t.completenessScore||!!t.recognizedText||null!=t.contentScore||t.feedbackEn?l("div",{className:"space-y-4",children:[t.classificationTitle?e(i,{title:t.classificationTitle,tag:t.classificationTag}):null,l("div",{className:"grid grid-cols-1 gap-4 lg:grid-cols-2",children:[e(o,{question:t}),null!=t.contentScore||t.feedbackEn?e(x,{contentScore:t.contentScore,feedback:t.feedbackEn}):e(m,{})]})]}):null}function i({title:t,tag:r}){const s={Platinum:{bg:"bg-slate-50 border-slate-200",text:"text-slate-800",iconBg:"bg-slate-200/60"},Gold:{bg:"bg-amber-50/60 border-amber-200/60",text:"text-amber-800",iconBg:"bg-amber-100"},Silver:{bg:"bg-gray-50 border-gray-200",text:"text-gray-700",iconBg:"bg-gray-200"},Bronze:{bg:"bg-orange-50/60 border-orange-200/60",text:"text-orange-800",iconBg:"bg-orange-100"}}[t]??{bg:"bg-blue-50/60 border-blue-100",text:"text-blue-800",iconBg:"bg-blue-100/60"};return l("div",{className:d("flex items-center gap-3 rounded-xl border px-4 py-2.5",s.bg),children:[e("div",{className:d("flex h-8 w-8 items-center justify-center rounded-full",s.iconBg),children:e(a,{className:d("h-4 w-4",s.text)})}),e("div",{className:"flex-1",children:l("div",{className:"flex items-center gap-2",children:[e("span",{className:d("text-sm font-black",s.text),children:t}),r?e("span",{className:"rounded-[4px] border border-gray-100/60 bg-white/70 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider text-gray-600 shadow-sm",children:r}):null]})}),e("span",{className:"text-[10px] font-semibold uppercase tracking-wider text-gray-400",children:"Phân loại năng lực"})]})}function o({question:a}){const r=[{label:"Phát âm",sublabel:"Pronunciation",score:a.pronunciationScore},{label:"Lưu loát",sublabel:"Fluency",score:a.fluencyScore},{label:"Ngữ điệu",sublabel:"Prosody",score:a.prosodyScore},{label:"Trọn vẹn",sublabel:"Completeness",score:a.completenessScore},{label:"Chính xác",sublabel:"Accuracy",score:a.accuracyScore}];return l("div",{className:"flex flex-col rounded-2xl border border-slate-200 bg-slate-50/60 shadow-[0_2px_8px_rgba(15,23,42,0.03)]",children:[l("div",{className:"flex items-center gap-3 px-4 py-3",children:[e(t,{className:"h-5 w-5 text-blue-600"}),e("h4",{className:"text-sm font-black tracking-wide text-slate-800",children:"Phân tích Khẩu hình"})]}),l("div",{className:"mx-4 mb-4 rounded-xl border border-slate-200 bg-white p-3.5 shadow-sm",children:[e("p",{className:"mb-1 text-[9px] font-bold uppercase tracking-wider text-blue-500",children:"AI Transcribed"}),l("p",{className:"text-sm font-medium italic leading-relaxed text-slate-800",children:["“",a.recognizedText||"Không rõ chữ / Không thể phiên âm","”"]})]}),e("div",{className:"grid grid-cols-2 gap-3 px-4 pb-4",children:r.map(l=>e(b,{...l},l.sublabel))})]})}function b({label:t,score:a}){const r=null!=a?Math.round(a):null,s=null==r?{text:"text-slate-400",bar:"bg-slate-200",bg:"bg-white border-slate-100"}:r>=80?{text:"text-emerald-600",bar:"bg-emerald-500",bg:"bg-emerald-50/50 border-emerald-100"}:r>=50?{text:"text-amber-600",bar:"bg-amber-400",bg:"bg-amber-50/50 border-amber-100"}:{text:"text-rose-600",bar:"bg-rose-400",bg:"bg-rose-50/50 border-rose-100"};return l("div",{className:d("flex flex-col gap-2 rounded-[14px] border p-3 shadow-[0_2px_10px_rgba(15,23,42,0.03)]",s.bg),children:[l("div",{className:"flex items-center justify-between",children:[e("span",{className:"text-xs font-black text-slate-700",children:t}),e("span",{className:d("text-lg font-black tracking-tight",s.text),children:null!=r?r:"N/A"})]}),e("div",{className:"h-1.5 w-full overflow-hidden rounded-full bg-slate-100",children:e("div",{className:d("h-full rounded-full transition-all duration-700 ease-out",s.bar),style:{width:`${Math.max(0,Math.min(100,r??0))}%`}})})]})}function x({contentScore:t,feedback:a}){return l("div",{className:"flex h-full flex-col rounded-2xl border border-slate-200 bg-slate-50/60 shadow-[0_2px_8px_rgba(15,23,42,0.03)]",children:[l("div",{className:"flex shrink-0 items-center gap-3 px-4 py-3",children:[e(s,{className:"h-5 w-5 text-blue-600"}),e("h4",{className:"text-sm font-black tracking-wide text-slate-800",children:"Phân tích Nội dung"})]}),null!=t?e("div",{className:"flex flex-col gap-3 px-4 pb-3",children:e(u,{label:"Phân tích Nội dung (LLM)",score:t,icon:e(c,{className:"h-3.5 w-3.5 text-blue-500"})})}):null,a?l("div",{className:"mx-4 mb-4 flex flex-1 flex-col rounded-xl border border-slate-200 border-l-[6px] border-l-blue-500 bg-white p-4 shadow-sm",children:[l("div",{className:"mb-2 flex shrink-0 items-center gap-2",children:[e("div",{className:"flex h-7 w-7 items-center justify-center rounded-full bg-blue-50 text-blue-600",children:e(r,{className:"h-4 w-4"})}),e("span",{className:"text-xs font-black uppercase tracking-wider text-blue-600",children:"Nhận xét AI"})]}),e("div",{className:"flex-1 overflow-y-auto pr-1",children:e("p",{className:"text-[15px] font-medium leading-relaxed text-slate-700",children:a})})]}):null]})}function u({label:t,score:a,icon:r}){const s=Math.round(a),n=s>=80?{bar:"bg-emerald-500",text:"text-emerald-600"}:s>=50?{bar:"bg-amber-400",text:"text-amber-600"}:{bar:"bg-rose-400",text:"text-rose-600"};return l("div",{className:"flex flex-col gap-1.5 rounded-xl border border-slate-200 bg-white p-2.5 shadow-sm",children:[l("div",{className:"flex items-center justify-between",children:[l("div",{className:"flex items-center gap-1.5",children:[e("span",{className:d("opacity-70",n.text),children:r}),e("span",{className:"text-xs font-bold text-slate-700",children:t})]}),e("span",{className:d("text-xs font-black",n.text),children:s})]}),e("div",{className:"h-1 w-full overflow-hidden rounded-full bg-slate-100",children:e("div",{className:d("h-full rounded-full transition-all duration-1000 ease-out",n.bar),style:{width:`${Math.max(0,Math.min(100,a))}%`}})})]})}function m(){return l("div",{className:"flex flex-col items-center justify-center gap-2 rounded-2xl border border-dashed border-slate-200 bg-slate-50/60 p-6 text-center",children:[e(n,{className:"h-5 w-5 text-slate-300"}),l("div",{children:[e("p",{className:"text-sm font-semibold text-slate-500",children:"Không chấm nội dung"}),e("p",{className:"mt-0.5 text-xs text-slate-400",children:"Dạng câu hỏi có kịch bản mẫu"})]})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Activity, Award, Bot, Brain, MessageSquare, TrendingUp } from 'lucide-react';
4
+ import { cn } from '../../../shared/lib/utils';
5
+ /**
6
+ * AI review panel for the /student/results/:attemptId/review page.
7
+ *
8
+ * Layout mirrors SpeakingAIAssessmentPanel (speaking-review page) but uses the
9
+ * slate / blue-500 / emerald / amber / rose palette of the existing review page
10
+ * (instead of the #2874A6 tone used on speaking-review).
11
+ *
12
+ * Only renders for questions with a student audio recording. If there's no
13
+ * content score / feedback the right-hand card falls back to a neutral
14
+ * placeholder.
15
+ */
16
+ export function ReviewSpeakingAIPanel({ question }) {
17
+ // No recording → nothing to analyse, skip entire panel.
18
+ if (!question.studentAudioKey)
19
+ return null;
20
+ const hasAnyPronunciationScore = question.pronunciationScore != null ||
21
+ question.accuracyScore != null ||
22
+ question.fluencyScore != null ||
23
+ question.prosodyScore != null ||
24
+ question.completenessScore != null ||
25
+ !!question.recognizedText;
26
+ if (!hasAnyPronunciationScore && question.contentScore == null && !question.feedbackEn) {
27
+ return null;
28
+ }
29
+ return (_jsxs("div", { className: "space-y-4", children: [question.classificationTitle ? (_jsx(ClassificationBanner, { title: question.classificationTitle, tag: question.classificationTag })) : null, _jsxs("div", { className: "grid grid-cols-1 gap-4 lg:grid-cols-2", children: [_jsx(PronunciationCard, { question: question }), question.contentScore != null || question.feedbackEn ? (_jsx(GrammarContentCard, { contentScore: question.contentScore, feedback: question.feedbackEn })) : (_jsx(NoGrammarPlaceholder, {}))] })] }));
30
+ }
31
+ /* ─── Classification banner ──────────────────────────────────────────────── */
32
+ function ClassificationBanner({ title, tag }) {
33
+ const tierStyles = {
34
+ Platinum: {
35
+ bg: 'bg-slate-50 border-slate-200',
36
+ text: 'text-slate-800',
37
+ iconBg: 'bg-slate-200/60',
38
+ },
39
+ Gold: {
40
+ bg: 'bg-amber-50/60 border-amber-200/60',
41
+ text: 'text-amber-800',
42
+ iconBg: 'bg-amber-100',
43
+ },
44
+ Silver: {
45
+ bg: 'bg-gray-50 border-gray-200',
46
+ text: 'text-gray-700',
47
+ iconBg: 'bg-gray-200',
48
+ },
49
+ Bronze: {
50
+ bg: 'bg-orange-50/60 border-orange-200/60',
51
+ text: 'text-orange-800',
52
+ iconBg: 'bg-orange-100',
53
+ },
54
+ };
55
+ const style = tierStyles[title] ?? {
56
+ bg: 'bg-blue-50/60 border-blue-100',
57
+ text: 'text-blue-800',
58
+ iconBg: 'bg-blue-100/60',
59
+ };
60
+ return (_jsxs("div", { className: cn('flex items-center gap-3 rounded-xl border px-4 py-2.5', style.bg), children: [_jsx("div", { className: cn('flex h-8 w-8 items-center justify-center rounded-full', style.iconBg), children: _jsx(Award, { className: cn('h-4 w-4', style.text) }) }), _jsx("div", { className: "flex-1", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: cn('text-sm font-black', style.text), children: title }), tag ? (_jsx("span", { className: "rounded-[4px] border border-gray-100/60 bg-white/70 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider text-gray-600 shadow-sm", children: tag })) : null] }) }), _jsx("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-gray-400", children: "Ph\u00E2n lo\u1EA1i n\u0103ng l\u1EF1c" })] }));
61
+ }
62
+ /* ─── Pronunciation card (left) ──────────────────────────────────────────── */
63
+ function PronunciationCard({ question }) {
64
+ const metrics = [
65
+ { label: 'Phát âm', sublabel: 'Pronunciation', score: question.pronunciationScore },
66
+ { label: 'Lưu loát', sublabel: 'Fluency', score: question.fluencyScore },
67
+ { label: 'Ngữ điệu', sublabel: 'Prosody', score: question.prosodyScore },
68
+ { label: 'Trọn vẹn', sublabel: 'Completeness', score: question.completenessScore },
69
+ { label: 'Chính xác', sublabel: 'Accuracy', score: question.accuracyScore },
70
+ ];
71
+ return (_jsxs("div", { className: "flex flex-col rounded-2xl border border-slate-200 bg-slate-50/60 shadow-[0_2px_8px_rgba(15,23,42,0.03)]", children: [_jsxs("div", { className: "flex items-center gap-3 px-4 py-3", children: [_jsx(Activity, { className: "h-5 w-5 text-blue-600" }), _jsx("h4", { className: "text-sm font-black tracking-wide text-slate-800", children: "Ph\u00E2n t\u00EDch Kh\u1EA9u h\u00ECnh" })] }), _jsxs("div", { className: "mx-4 mb-4 rounded-xl border border-slate-200 bg-white p-3.5 shadow-sm", children: [_jsx("p", { className: "mb-1 text-[9px] font-bold uppercase tracking-wider text-blue-500", children: "AI Transcribed" }), _jsxs("p", { className: "text-sm font-medium italic leading-relaxed text-slate-800", children: ["\u201C", question.recognizedText || 'Không rõ chữ / Không thể phiên âm', "\u201D"] })] }), _jsx("div", { className: "grid grid-cols-2 gap-3 px-4 pb-4", children: metrics.map((metric) => (_jsx(ScoreMetricCard, { ...metric }, metric.sublabel))) })] }));
72
+ }
73
+ function ScoreMetricCard({ label, score, }) {
74
+ const safeScore = score != null ? Math.round(score) : null;
75
+ const tone = safeScore == null
76
+ ? {
77
+ text: 'text-slate-400',
78
+ bar: 'bg-slate-200',
79
+ bg: 'bg-white border-slate-100',
80
+ }
81
+ : safeScore >= 80
82
+ ? {
83
+ text: 'text-emerald-600',
84
+ bar: 'bg-emerald-500',
85
+ bg: 'bg-emerald-50/50 border-emerald-100',
86
+ }
87
+ : safeScore >= 50
88
+ ? {
89
+ text: 'text-amber-600',
90
+ bar: 'bg-amber-400',
91
+ bg: 'bg-amber-50/50 border-amber-100',
92
+ }
93
+ : {
94
+ text: 'text-rose-600',
95
+ bar: 'bg-rose-400',
96
+ bg: 'bg-rose-50/50 border-rose-100',
97
+ };
98
+ return (_jsxs("div", { className: cn('flex flex-col gap-2 rounded-[14px] border p-3 shadow-[0_2px_10px_rgba(15,23,42,0.03)]', tone.bg), children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-xs font-black text-slate-700", children: label }), _jsx("span", { className: cn('text-lg font-black tracking-tight', tone.text), children: safeScore != null ? safeScore : 'N/A' })] }), _jsx("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-slate-100", children: _jsx("div", { className: cn('h-full rounded-full transition-all duration-700 ease-out', tone.bar), style: { width: `${Math.max(0, Math.min(100, safeScore ?? 0))}%` } }) })] }));
99
+ }
100
+ /* ─── Grammar & content card (right) ─────────────────────────────────────── */
101
+ function GrammarContentCard({ contentScore, feedback, }) {
102
+ return (_jsxs("div", { className: "flex h-full flex-col rounded-2xl border border-slate-200 bg-slate-50/60 shadow-[0_2px_8px_rgba(15,23,42,0.03)]", children: [_jsxs("div", { className: "flex shrink-0 items-center gap-3 px-4 py-3", children: [_jsx(Brain, { className: "h-5 w-5 text-blue-600" }), _jsx("h4", { className: "text-sm font-black tracking-wide text-slate-800", children: "Ph\u00E2n t\u00EDch N\u1ED9i dung" })] }), contentScore != null ? (_jsx("div", { className: "flex flex-col gap-3 px-4 pb-3", children: _jsx(ScoreProgressBar, { label: "Ph\u00E2n t\u00EDch N\u1ED9i dung (LLM)", score: contentScore, icon: _jsx(TrendingUp, { className: "h-3.5 w-3.5 text-blue-500" }) }) })) : null, feedback ? (_jsxs("div", { className: "mx-4 mb-4 flex flex-1 flex-col rounded-xl border border-slate-200 border-l-[6px] border-l-blue-500 bg-white p-4 shadow-sm", children: [_jsxs("div", { className: "mb-2 flex shrink-0 items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-full bg-blue-50 text-blue-600", children: _jsx(Bot, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-xs font-black uppercase tracking-wider text-blue-600", children: "Nh\u1EADn x\u00E9t AI" })] }), _jsx("div", { className: "flex-1 overflow-y-auto pr-1", children: _jsx("p", { className: "text-[15px] font-medium leading-relaxed text-slate-700", children: feedback }) })] })) : null] }));
103
+ }
104
+ function ScoreProgressBar({ label, score, icon, }) {
105
+ const rounded = Math.round(score);
106
+ const tone = rounded >= 80
107
+ ? { bar: 'bg-emerald-500', text: 'text-emerald-600' }
108
+ : rounded >= 50
109
+ ? { bar: 'bg-amber-400', text: 'text-amber-600' }
110
+ : { bar: 'bg-rose-400', text: 'text-rose-600' };
111
+ return (_jsxs("div", { className: "flex flex-col gap-1.5 rounded-xl border border-slate-200 bg-white p-2.5 shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: cn('opacity-70', tone.text), children: icon }), _jsx("span", { className: "text-xs font-bold text-slate-700", children: label })] }), _jsx("span", { className: cn('text-xs font-black', tone.text), children: rounded })] }), _jsx("div", { className: "h-1 w-full overflow-hidden rounded-full bg-slate-100", children: _jsx("div", { className: cn('h-full rounded-full transition-all duration-1000 ease-out', tone.bar), style: { width: `${Math.max(0, Math.min(100, score))}%` } }) })] }));
112
+ }
113
+ /* ─── Empty-state placeholder (no content grading) ───────────────────────── */
114
+ function NoGrammarPlaceholder() {
115
+ return (_jsxs("div", { className: "flex flex-col items-center justify-center gap-2 rounded-2xl border border-dashed border-slate-200 bg-slate-50/60 p-6 text-center", children: [_jsx(MessageSquare, { className: "h-5 w-5 text-slate-300" }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-semibold text-slate-500", children: "Kh\u00F4ng ch\u1EA5m n\u1ED9i dung" }), _jsx("p", { className: "mt-0.5 text-xs text-slate-400", children: "D\u1EA1ng c\u00E2u h\u1ECFi c\u00F3 k\u1ECBch b\u1EA3n m\u1EABu" })] })] }));
116
+ }
@@ -1 +1,48 @@
1
- "use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import{ImageIcon as t,ImageOff as r}from"lucide-react";import{useState as l}from"react";import{ResolvedImage as s}from"../../../components/common/ResolvedImage";import{usePresignedFileUrl as i}from"../../../shared/lib/hooks/usePresignedFileUrl";import{cn as n}from"../../../shared/lib/utils";import{SpeakingResultAudioPlayer as d}from"../speaking/SpeakingResultAudioPlayer";import{ListeningAudioBar as o}from"./ReviewListeningHeader";export function PromptImage({imageKey:s,alt:n}){const{previewUrl:d,isLoading:o}=i(s||void 0),[c,m]=l(!1);return s?a("div",{className:"rounded-lg border border-gray-200 bg-white p-4 shadow-sm",children:[a("div",{className:"mb-3 flex items-center gap-2",children:[e(t,{className:"h-4 w-4 text-violet-500"}),e("p",{className:"text-sm font-semibold text-gray-700",children:"Question image"})]}),e("div",{className:"flex min-h-[200px] items-center justify-center rounded-lg bg-gray-50 p-4",children:o?e("span",{className:"text-sm text-gray-400",children:"Đang tải hình ảnh..."}):d&&!c?e("img",{src:d,alt:n,className:"max-h-[320px] w-auto rounded-lg object-contain",onError:()=>m(!0)}):a("div",{className:"flex flex-col items-center justify-center text-gray-400",children:[e(r,{className:"mb-2 h-8 w-8"}),e("span",{className:"text-sm",children:"Không thể tải hình ảnh"})]})})]}):null}export function ResolvedImageItem({imageKey:t,alt:s,label:d,highlight:o,aspectRatio:c="3.3"}){const{previewUrl:m,isLoading:g}=i(t||void 0),[h,x]=l(!1);return a("div",{className:n("relative w-full overflow-hidden rounded-xl border-2 bg-slate-50",o?"border-amber-400 ring-2 ring-amber-300":"border-slate-200"),style:{aspectRatio:c},children:[d&&e("span",{className:"absolute left-1/2 top-2 z-10 -translate-x-1/2 rounded-full bg-slate-800/80 px-3 py-0.5 text-xs font-bold uppercase tracking-wide text-white shadow backdrop-blur-sm",children:d}),g?e("div",{className:"flex h-full w-full items-center justify-center",children:e("span",{className:"text-xs text-gray-400",children:"Đang tải..."})}):m&&!h?e("img",{src:m,alt:s,className:"h-full w-full object-cover",onError:()=>x(!0)}):a("div",{className:"flex h-full w-full flex-col items-center justify-center text-gray-400",children:[e(r,{className:"mb-2 h-6 w-6"}),e("span",{className:"text-xs",children:"Không có ảnh"})]})]})}export function CompareImagesSection({candidateImageUrl:r,examinerImageUrl:l}){return r||l?a("div",{className:"space-y-3 rounded-lg border border-gray-200 bg-white p-4 shadow-sm",children:[a("div",{className:"mb-1 flex items-center gap-2",children:[e(t,{className:"h-4 w-4 text-violet-500"}),e("p",{className:"text-sm font-semibold text-gray-700",children:"Hình ảnh câu hỏi"})]}),e(ResolvedImageItem,{imageKey:r,alt:"Candidate's copy",label:"Candidate's copy"}),e(ResolvedImageItem,{imageKey:l,alt:"Examiner's copy",label:"Examiner's copy"})]}):null}export function StoryImagesSection({images:r}){return r&&0!==r.length?a("div",{className:"rounded-lg border border-gray-200 bg-white p-4 shadow-sm",children:[a("div",{className:"mb-3 flex items-center gap-2",children:[e(t,{className:"h-4 w-4 text-violet-500"}),a("p",{className:"text-sm font-semibold text-gray-700",children:["Hình ảnh câu chuyện (",r.length," ảnh)"]})]}),e("div",{className:"grid grid-cols-2 gap-3",children:r.map((a,t)=>e(ResolvedImageItem,{imageKey:a,alt:`Story image ${t+1}`,label:`Ảnh ${t+1}`,aspectRatio:"1/1"},a+t))})]}):null}export function InfoExchangeSection({data:r}){const{title:l,imageUrl:d,candidateFields:o,examinerFields:c}=r,m=o.length>0?o:c,{previewUrl:g,isLoading:h}=i(d||void 0);return a("div",{className:"space-y-3",children:[l?e("h3",{className:"text-sm font-bold text-gray-800",children:l}):null,d?e("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:h?e("div",{className:"flex h-[220px] items-center justify-center text-sm text-gray-400",children:"Loading image..."}):g?e(s,{src:d,alt:l||"Info exchange",className:"mx-auto h-auto max-h-[260px] w-auto max-w-full object-contain p-2"}):a("div",{className:"flex h-[220px] items-center justify-center text-sm text-gray-400",children:[e(t,{className:"mr-2 h-4 w-4"}),"Image unavailable"]})}):null,m.length>0?e("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:e("table",{className:"w-full text-sm",children:e("tbody",{children:m.map((t,r)=>a("tr",{className:n("border-b border-gray-100 transition-colors last:border-0 hover:bg-blue-50/50",r%2==0?"bg-white":"bg-gray-50/50"),children:[e("td",{className:"w-1/3 px-4 py-2.5 font-bold text-gray-700",children:t.key}),e("td",{className:"px-4 py-2.5",children:t.value||t.answer?e("span",{className:"font-medium text-gray-800",children:t.value||t.answer}):e("span",{className:"italic text-gray-300",children:"—"})})]},`${t.key}-${r}`))})})}):null]})}export function OddOneOutImagesSection({groupImages:r,contentImages:l,activeId:s}){const i=r.length>0?r:l.map((e,a)=>({id:String(a),url:e,label:String(a+1)}));return 0===i.length?null:a("div",{className:"rounded-lg border border-gray-200 bg-white p-4 shadow-sm",children:[a("div",{className:"mb-3 flex items-center gap-2",children:[e(t,{className:"h-4 w-4 text-violet-500"}),e("p",{className:"text-sm font-semibold text-gray-700",children:"Hình ảnh Odd One Out"})]}),e("div",{className:"grid grid-cols-2 gap-3",children:i.map(a=>e(ResolvedImageItem,{imageKey:a.url,alt:`Image ${a.label}`,label:a.id===s?`Ảnh ${a.label} ★`:`Ảnh ${a.label}`,highlight:a.id===s},a.id))})]})}export function AudioSection({title:t,subtitle:r,icon:l,audioKey:s,emptyMessage:i,useListeningBar:n=!1}){return a("div",{className:"audio-player-wrapper rounded-lg border border-gray-200 bg-white p-4 shadow-sm",children:[a("div",{className:"mb-3 flex items-center gap-2",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-full bg-gray-100 text-gray-700",children:l}),a("div",{children:[e("p",{className:"text-sm font-semibold text-gray-800",children:t}),e("p",{className:"text-xs text-gray-500",children:r})]})]}),s?n?e(o,{audioKey:s}):e(d,{audioKey:s,variant:"admin"}):e("div",{className:"rounded-lg border border-dashed border-gray-300 bg-gray-50 px-4 py-3 text-sm text-gray-500",children:i})]})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { ImageIcon, ImageOff } from 'lucide-react';
4
+ import { useState } from 'react';
5
+ import { ResolvedImage } from '../../../components/common/ResolvedImage';
6
+ import { usePresignedFileUrl } from '../../../shared/lib/hooks/usePresignedFileUrl';
7
+ import { cn } from '../../../shared/lib/utils';
8
+ import { SpeakingResultAudioPlayer } from '../speaking/SpeakingResultAudioPlayer';
9
+ import { ListeningAudioBar } from './ReviewListeningHeader';
10
+ export function PromptImage({ imageKey, alt }) {
11
+ const { previewUrl, isLoading } = usePresignedFileUrl(imageKey || undefined);
12
+ const [hasError, setHasError] = useState(false);
13
+ if (!imageKey)
14
+ return null;
15
+ return (_jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4 shadow-sm", children: [_jsxs("div", { className: "mb-3 flex items-center gap-2", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-violet-500" }), _jsx("p", { className: "text-sm font-semibold text-gray-700", children: "Question image" })] }), _jsx("div", { className: "flex min-h-[200px] items-center justify-center rounded-lg bg-gray-50 p-4", children: isLoading ? (_jsx("span", { className: "text-sm text-gray-400", children: "\u0110ang t\u1EA3i h\u00ECnh \u1EA3nh..." })) : previewUrl && !hasError ? (_jsx("img", { src: previewUrl, alt: alt, className: "max-h-[320px] w-auto rounded-lg object-contain", onError: () => setHasError(true) })) : (_jsxs("div", { className: "flex flex-col items-center justify-center text-gray-400", children: [_jsx(ImageOff, { className: "mb-2 h-8 w-8" }), _jsx("span", { className: "text-sm", children: "Kh\u00F4ng th\u1EC3 t\u1EA3i h\u00ECnh \u1EA3nh" })] })) })] }));
16
+ }
17
+ export function ResolvedImageItem({ imageKey, alt, label, highlight, aspectRatio = '3.3', }) {
18
+ const { previewUrl, isLoading } = usePresignedFileUrl(imageKey || undefined);
19
+ const [hasError, setHasError] = useState(false);
20
+ return (_jsxs("div", { className: cn('relative w-full overflow-hidden rounded-xl border-2 bg-slate-50', highlight ? 'border-amber-400 ring-2 ring-amber-300' : 'border-slate-200'), style: { aspectRatio }, children: [label && (_jsx("span", { className: "absolute left-1/2 top-2 z-10 -translate-x-1/2 rounded-full bg-slate-800/80 px-3 py-0.5 text-xs font-bold uppercase tracking-wide text-white shadow backdrop-blur-sm", children: label })), isLoading ? (_jsx("div", { className: "flex h-full w-full items-center justify-center", children: _jsx("span", { className: "text-xs text-gray-400", children: "\u0110ang t\u1EA3i..." }) })) : previewUrl && !hasError ? (_jsx("img", { src: previewUrl, alt: alt, className: "h-full w-full object-cover", onError: () => setHasError(true) })) : (_jsxs("div", { className: "flex h-full w-full flex-col items-center justify-center text-gray-400", children: [_jsx(ImageOff, { className: "mb-2 h-6 w-6" }), _jsx("span", { className: "text-xs", children: "Kh\u00F4ng c\u00F3 \u1EA3nh" })] }))] }));
21
+ }
22
+ export function CompareImagesSection({ candidateImageUrl, examinerImageUrl, }) {
23
+ if (!candidateImageUrl && !examinerImageUrl)
24
+ return null;
25
+ return (_jsxs("div", { className: "space-y-3 rounded-lg border border-gray-200 bg-white p-4 shadow-sm", children: [_jsxs("div", { className: "mb-1 flex items-center gap-2", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-violet-500" }), _jsx("p", { className: "text-sm font-semibold text-gray-700", children: "H\u00ECnh \u1EA3nh c\u00E2u h\u1ECFi" })] }), _jsx(ResolvedImageItem, { imageKey: candidateImageUrl, alt: "Candidate's copy", label: "Candidate's copy" }), _jsx(ResolvedImageItem, { imageKey: examinerImageUrl, alt: "Examiner's copy", label: "Examiner's copy" })] }));
26
+ }
27
+ export function StoryImagesSection({ images }) {
28
+ if (!images || images.length === 0)
29
+ return null;
30
+ return (_jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4 shadow-sm", children: [_jsxs("div", { className: "mb-3 flex items-center gap-2", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-violet-500" }), _jsxs("p", { className: "text-sm font-semibold text-gray-700", children: ["H\u00ECnh \u1EA3nh c\u00E2u chuy\u1EC7n (", images.length, " \u1EA3nh)"] })] }), _jsx("div", { className: "grid grid-cols-2 gap-3", children: images.map((url, i) => (_jsx(ResolvedImageItem, { imageKey: url, alt: `Story image ${i + 1}`, label: `Ảnh ${i + 1}`, aspectRatio: "1/1" }, url + i))) })] }));
31
+ }
32
+ export function InfoExchangeSection({ data, }) {
33
+ const { title, imageUrl, candidateFields, examinerFields } = data;
34
+ const fields = candidateFields.length > 0 ? candidateFields : examinerFields;
35
+ const { previewUrl, isLoading } = usePresignedFileUrl(imageUrl || undefined);
36
+ return (_jsxs("div", { className: "space-y-3", children: [title ? _jsx("h3", { className: "text-sm font-bold text-gray-800", children: title }) : null, imageUrl ? (_jsx("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: isLoading ? (_jsx("div", { className: "flex h-[220px] items-center justify-center text-sm text-gray-400", children: "Loading image..." })) : previewUrl ? (_jsx(ResolvedImage, { src: imageUrl, alt: title || 'Info exchange', className: "mx-auto h-auto max-h-[260px] w-auto max-w-full object-contain p-2" })) : (_jsxs("div", { className: "flex h-[220px] items-center justify-center text-sm text-gray-400", children: [_jsx(ImageIcon, { className: "mr-2 h-4 w-4" }), "Image unavailable"] })) })) : null, fields.length > 0 ? (_jsx("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: _jsx("table", { className: "w-full text-sm", children: _jsx("tbody", { children: fields.map((field, idx) => (_jsxs("tr", { className: cn('border-b border-gray-100 transition-colors last:border-0 hover:bg-blue-50/50', idx % 2 === 0 ? 'bg-white' : 'bg-gray-50/50'), children: [_jsx("td", { className: "w-1/3 px-4 py-2.5 font-bold text-gray-700", children: field.key }), _jsx("td", { className: "px-4 py-2.5", children: field.value || field.answer ? (_jsx("span", { className: "font-medium text-gray-800", children: field.value || field.answer })) : (_jsx("span", { className: "italic text-gray-300", children: "\u2014" })) })] }, `${field.key}-${idx}`))) }) }) })) : null] }));
37
+ }
38
+ export function OddOneOutImagesSection({ groupImages, contentImages, activeId, }) {
39
+ const images = groupImages.length > 0
40
+ ? groupImages
41
+ : contentImages.map((url, i) => ({ id: String(i), url, label: String(i + 1) }));
42
+ if (images.length === 0)
43
+ return null;
44
+ return (_jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-4 shadow-sm", children: [_jsxs("div", { className: "mb-3 flex items-center gap-2", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-violet-500" }), _jsx("p", { className: "text-sm font-semibold text-gray-700", children: "H\u00ECnh \u1EA3nh Odd One Out" })] }), _jsx("div", { className: "grid grid-cols-2 gap-3", children: images.map((img) => (_jsx(ResolvedImageItem, { imageKey: img.url, alt: `Image ${img.label}`, label: img.id === activeId ? `Ảnh ${img.label} ★` : `Ảnh ${img.label}`, highlight: img.id === activeId }, img.id))) })] }));
45
+ }
46
+ export function AudioSection({ title, subtitle, icon, audioKey, emptyMessage, useListeningBar = false, }) {
47
+ return (_jsxs("div", { className: "audio-player-wrapper rounded-lg border border-gray-200 bg-white p-4 shadow-sm", children: [_jsxs("div", { className: "mb-3 flex items-center gap-2", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-gray-100 text-gray-700", children: icon }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-semibold text-gray-800", children: title }), _jsx("p", { className: "text-xs text-gray-500", children: subtitle })] })] }), audioKey ? (useListeningBar ? (_jsx(ListeningAudioBar, { audioKey: audioKey })) : (_jsx(SpeakingResultAudioPlayer, { audioKey: audioKey, variant: "admin" }))) : (_jsx("div", { className: "rounded-lg border border-dashed border-gray-300 bg-gray-50 px-4 py-3 text-sm text-gray-500", children: emptyMessage }))] }));
48
+ }