@tinyweb_dev/oe-exam-sdk 0.1.28 → 0.1.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (796) hide show
  1. package/README.md +4 -4
  2. package/dist/api/contest-rooms.js +54 -1
  3. package/dist/api/contest-rooms.types.js +1 -1
  4. package/dist/api/exam-create.js +179 -1
  5. package/dist/api/exam-create.types.js +1 -1
  6. package/dist/api/exam-entry.js +68 -1
  7. package/dist/api/exam-entry.types.js +1 -1
  8. package/dist/api/exam-questions.js +149 -1
  9. package/dist/api/exam-questions.types.js +1 -1
  10. package/dist/api/exam-taking.js +84 -1
  11. package/dist/api/exam-taking.types.d.ts +7 -0
  12. package/dist/api/exam-taking.types.js +1 -1
  13. package/dist/api/index.js +8 -1
  14. package/dist/api/result-review.js +62 -1
  15. package/dist/api/result-review.types.js +16 -1
  16. package/dist/components/CueCardEditor.js +58 -1
  17. package/dist/components/common/ResolvedImage.js +15 -1
  18. package/dist/components/exams/CompletionDialog.js +8 -1
  19. package/dist/components/exams/CreateExamInfoForm.js +84 -1
  20. package/dist/components/exams/CreateExamPageContainer.js +110 -1
  21. package/dist/components/exams/ExamCreator.js +287 -1
  22. package/dist/components/exams/ExamPartTabs.js +138 -1
  23. package/dist/components/exams/ExamPreviewDialog.d.ts +6 -7
  24. package/dist/components/exams/ExamPreviewDialog.js +121 -1
  25. package/dist/components/exams/ExamPreviewPartContent.d.ts +18 -0
  26. package/dist/components/exams/ExamPreviewPartContent.js +27 -0
  27. package/dist/components/exams/ExamPreviewSidebar.d.ts +17 -0
  28. package/dist/components/exams/ExamPreviewSidebar.js +37 -0
  29. package/dist/components/exams/ExamQuestionGrid.js +37 -1
  30. package/dist/components/exams/ExamQuestionsPageContainer.js +112 -1
  31. package/dist/components/exams/ExamViewDialog.js +61 -1
  32. package/dist/components/exams/PartAudioUploader.js +9 -1
  33. package/dist/components/exams/TemplateSelector.js +76 -1
  34. package/dist/components/exams/UnsavedChangesDialog.js +7 -1
  35. package/dist/components/exams/api.js +3 -1
  36. package/dist/components/exams/create-exam.texts.js +1 -1
  37. package/dist/components/exams/create-exam.validation.js +18 -1
  38. package/dist/components/exams/entry/EntryRestartDialog.js +11 -1
  39. package/dist/components/exams/entry/StudentExamEntryPageContainer.js +358 -1
  40. package/dist/components/exams/entry/exam-entry.states.js +26 -1
  41. package/dist/components/exams/entry/exam-entry.utils.js +55 -1
  42. package/dist/components/exams/entry/fullscreen-loading.js +18 -1
  43. package/dist/components/exams/entry/index.js +4 -1
  44. package/dist/components/exams/exam-preview.types.d.ts +30 -0
  45. package/dist/components/exams/exam-preview.types.js +62 -0
  46. package/dist/components/exams/exam-question-status.js +45 -1
  47. package/dist/components/exams/exam-template.utils.js +18 -1
  48. package/dist/components/exams/index.d.ts +1 -0
  49. package/dist/components/exams/index.js +19 -1
  50. package/dist/components/exams/rooms/StudentContestRoomsPageContainer.js +183 -1
  51. package/dist/components/exams/rooms/contest-rooms.utils.js +93 -1
  52. package/dist/components/exams/rooms/index.js +2 -1
  53. package/dist/components/exams/take/ExamTakingApiContext.js +25 -1
  54. package/dist/components/exams/take/ExamTakingPageContainer.js +955 -1
  55. package/dist/components/exams/take/GenericQuestionRenderer.js +14 -1
  56. package/dist/components/exams/take/components/QuestionRenderer.js +207 -1
  57. package/dist/components/exams/take/components/index.js +11 -1
  58. package/dist/components/exams/take/components/question-renderers/ArticlesViewer.js +8 -1
  59. package/dist/components/exams/take/components/question-renderers/MoversAnswerTheQuestionRenderer.js +30 -1
  60. package/dist/components/exams/take/components/question-renderers/MoversChooseAdjectiveRenderer.js +63 -1
  61. package/dist/components/exams/take/components/question-renderers/MoversChooseBestAnswerRenderer.js +136 -1
  62. package/dist/components/exams/take/components/question-renderers/MoversColoringRenderer.js +379 -1
  63. package/dist/components/exams/take/components/question-renderers/MoversCrossOutWordGroupRenderer.d.ts +20 -0
  64. package/dist/components/exams/take/components/question-renderers/MoversCrossOutWordGroupRenderer.js +141 -0
  65. package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.js +131 -1
  66. package/dist/components/exams/take/components/question-renderers/MoversGridFillRenderer.js +76 -1
  67. package/dist/components/exams/take/components/question-renderers/MoversLabelThePictureRenderer.js +68 -1
  68. package/dist/components/exams/take/components/question-renderers/MoversListenAndWriteRenderer.js +42 -1
  69. package/dist/components/exams/take/components/question-renderers/MoversMatchByWritingAnswerRenderer.js +131 -1
  70. package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.js +76 -1
  71. package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.js +68 -1
  72. package/dist/components/exams/take/components/question-renderers/MoversReadingPassageRenderer.js +62 -1
  73. package/dist/components/exams/take/components/question-renderers/MoversTrueFalseRenderer.d.ts +1 -1
  74. package/dist/components/exams/take/components/question-renderers/MoversTrueFalseRenderer.js +51 -1
  75. package/dist/components/exams/take/components/question-renderers/MoversWordFillParagraphRenderer.js +171 -1
  76. package/dist/components/exams/take/components/question-renderers/MoversWordFillStructuredFormRenderer.js +198 -1
  77. package/dist/components/exams/take/components/question-renderers/MoversWordOrderAndMatchGroupRenderer.d.ts +15 -0
  78. package/dist/components/exams/take/components/question-renderers/MoversWordOrderAndMatchGroupRenderer.js +36 -0
  79. package/dist/components/exams/take/components/question-renderers/MoversWordOrderingRenderer.js +92 -1
  80. package/dist/components/exams/take/components/question-renderers/MoversWriteSentencesRenderer.js +11 -1
  81. package/dist/components/exams/take/components/question-renderers/MoversWritingRenderer.js +56 -1
  82. package/dist/components/exams/take/components/question-renderers/index.d.ts +2 -0
  83. package/dist/components/exams/take/components/question-renderers/index.js +25 -1
  84. package/dist/components/exams/take/components/speaking/SpeakingAudioPlayer.js +85 -1
  85. package/dist/components/exams/take/components/speaking/SpeakingContentFrame.js +10 -1
  86. package/dist/components/exams/take/components/speaking/SpeakingExamLayout.js +15 -1
  87. package/dist/components/exams/take/components/speaking/SpeakingHeader.js +6 -1
  88. package/dist/components/exams/take/components/speaking/SpeakingInstructionBar.js +8 -1
  89. package/dist/components/exams/take/components/speaking/SpeakingMicButton.js +59 -1
  90. package/dist/components/exams/take/components/speaking/SpeakingNavButton.js +7 -1
  91. package/dist/components/exams/take/components/speaking/SpeakingPartBadge.js +5 -1
  92. package/dist/components/exams/take/components/speaking/SpeakingReadDisplayedContent.module.css +120 -0
  93. package/dist/components/exams/take/components/speaking/SpeakingRecordingPreview.js +73 -1
  94. package/dist/components/exams/take/components/speaking/SpeakingReportErrorButton.js +8 -1
  95. package/dist/components/exams/take/components/speaking/SpeakingSpontaneousQA.module.css +344 -0
  96. package/dist/components/exams/take/components/speaking/SpeakingVideoIntro.js +32 -1
  97. package/dist/components/exams/take/components/speaking/index.js +1 -1
  98. package/dist/components/exams/take/components/speaking/renderers/SpeakingChooseAnswerGroupRenderer.js +63 -1
  99. package/dist/components/exams/take/components/speaking/renderers/SpeakingChooseObjectsRenderer.js +95 -1
  100. package/dist/components/exams/take/components/speaking/renderers/SpeakingCompareImagesRenderer.js +35 -1
  101. package/dist/components/exams/take/components/speaking/renderers/SpeakingConversationRenderer.js +147 -1
  102. package/dist/components/exams/take/components/speaking/renderers/SpeakingCueCardRenderer.js +232 -1
  103. package/dist/components/exams/take/components/speaking/renderers/SpeakingDescribeImageRenderer.js +193 -1
  104. package/dist/components/exams/take/components/speaking/renderers/SpeakingDragObjectsRenderer.js +155 -1
  105. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.js +212 -1
  106. package/dist/components/exams/take/components/speaking/renderers/SpeakingInfoExchangeRenderer.js +77 -1
  107. package/dist/components/exams/take/components/speaking/renderers/SpeakingListenAndSpeakAnswerRenderer.js +64 -1
  108. package/dist/components/exams/take/components/speaking/renderers/SpeakingOddOneOutRenderer.js +69 -1
  109. package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionListRenderer.js +64 -1
  110. package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionRenderer.js +92 -1
  111. package/dist/components/exams/take/components/speaking/renderers/SpeakingReadDisplayedContentRenderer.js +202 -1
  112. package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQARenderer.js +339 -1
  113. package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQaGroupRenderer.d.ts +19 -0
  114. package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQaGroupRenderer.js +76 -0
  115. package/dist/components/exams/take/components/speaking/renderers/SpeakingStoryImagesRenderer.js +56 -1
  116. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQACambridgeLayout.d.ts +24 -0
  117. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQACambridgeLayout.js +120 -0
  118. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQAInlineAudioPlayer.d.ts +8 -0
  119. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQAInlineAudioPlayer.js +94 -0
  120. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQASummerSkyAudioPlayer.d.ts +8 -0
  121. package/dist/components/exams/take/components/speaking/renderers/SpontaneousQASummerSkyAudioPlayer.js +101 -0
  122. package/dist/components/exams/take/components/speaking/renderers/index.d.ts +1 -0
  123. package/dist/components/exams/take/components/speaking/renderers/index.js +14 -1
  124. package/dist/components/exams/take/exam-taking.utils.d.ts +13 -0
  125. package/dist/components/exams/take/exam-taking.utils.js +203 -1
  126. package/dist/components/exams/take/hooks/index.js +2 -1
  127. package/dist/components/exams/take/hooks/useAnswerAutosave.js +71 -1
  128. package/dist/components/exams/take/hooks/useAttemptSubmit.js +38 -1
  129. package/dist/components/exams/take/hooks/useAutoSave.js +83 -1
  130. package/dist/components/exams/take/hooks/useExamCountdown.js +35 -1
  131. package/dist/components/exams/take/hooks/useExamTimer.js +97 -1
  132. package/dist/components/exams/take/hooks/useProctoringCamera.js +118 -1
  133. package/dist/components/exams/take/hooks/useProctoringSnapshot.js +76 -1
  134. package/dist/components/exams/take/hooks/useSpeakingConversationAudio.js +178 -1
  135. package/dist/components/exams/take/hooks/useSpeakingNavigation.js +51 -1
  136. package/dist/components/exams/take/hooks/useSpeakingRecorder.js +406 -1
  137. package/dist/components/exams/take/hooks/useSpeakingRendererSetup.js +136 -1
  138. package/dist/components/exams/take/hooks/useSpontaneousQaGroupRecorder.d.ts +11 -0
  139. package/dist/components/exams/take/hooks/useSpontaneousQaGroupRecorder.js +352 -0
  140. package/dist/components/exams/take/hooks/useSpontaneousQaGroupRendererSetup.d.ts +42 -0
  141. package/dist/components/exams/take/hooks/useSpontaneousQaGroupRendererSetup.js +166 -0
  142. package/dist/components/exams/take/hooks/useToeicAudio.js +35 -1
  143. package/dist/components/exams/take/index.js +6 -1
  144. package/dist/components/exams/take/types/speaking-cue-card.types.js +1 -1
  145. package/dist/components/exams/take/types/speaking.types.js +7 -1
  146. package/dist/components/exams/take/types.js +4 -1
  147. package/dist/components/exams/take/utils/answer-transformers.js +452 -1
  148. package/dist/components/exams/take/utils/cambridge-yle-inline-speaking.d.ts +21 -0
  149. package/dist/components/exams/take/utils/cambridge-yle-inline-speaking.js +44 -0
  150. package/dist/components/exams/take/utils/coloring-assignments.js +40 -1
  151. package/dist/components/exams/take/utils/cross-out-word-group.transformer.d.ts +21 -0
  152. package/dist/components/exams/take/utils/cross-out-word-group.transformer.js +83 -0
  153. package/dist/components/exams/take/utils/question-transformers.d.ts +43 -2
  154. package/dist/components/exams/take/utils/question-transformers.js +1137 -1
  155. package/dist/components/features/exam-entry/entry-state-cards.js +19 -1
  156. package/dist/components/features/exam-entry/index.js +2 -1
  157. package/dist/components/features/exam-entry/themed/CambridgeYleWaitingRoom.js +121 -1
  158. package/dist/components/features/exam-entry/themed/ThemedWaitingRoom.js +37 -1
  159. package/dist/components/features/exam-entry/themed/ToeicWaitingRoom.js +15 -1
  160. package/dist/components/features/exam-entry/themed/cambridge-yle-waiting-parts.js +80 -1
  161. package/dist/components/features/exam-entry/themed/components/CloseButton.js +6 -1
  162. package/dist/components/features/exam-entry/themed/components/ExamInfoPanel.js +17 -1
  163. package/dist/components/features/exam-entry/themed/components/ModeSelector.js +6 -1
  164. package/dist/components/features/exam-entry/themed/components/StartButton.js +12 -1
  165. package/dist/components/features/exam-entry/themed/components/index.js +4 -1
  166. package/dist/components/features/exam-entry/themed/index.js +3 -1
  167. package/dist/components/index.d.ts +1 -0
  168. package/dist/components/index.js +43 -1
  169. package/dist/components/questions/_shared/config/question-types.config.js +362 -1
  170. package/dist/components/questions/_shared/constants/color-palette.js +21 -1
  171. package/dist/components/questions/_shared/hooks/useAnswerGroupQuestionGroup.js +105 -1
  172. package/dist/components/questions/_shared/hooks/useBasicQuestionGroup.js +222 -1
  173. package/dist/components/questions/_shared/hooks/useCompareImagesGroup.js +140 -1
  174. package/dist/components/questions/_shared/hooks/useImageQuestionGroup.js +123 -1
  175. package/dist/components/questions/_shared/hooks/useInfoExchangeGroup.js +50 -1
  176. package/dist/components/questions/_shared/hooks/useQuestionFormState.js +54 -1
  177. package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +123 -1
  178. package/dist/components/questions/_shared/hooks/useQuestionListGroup.js +121 -1
  179. package/dist/components/questions/_shared/hooks/useQuestionOnChange.js +70 -1
  180. package/dist/components/questions/_shared/hooks/useSceneQuestionGroup.js +194 -1
  181. package/dist/components/questions/_shared/index.js +7 -1
  182. package/dist/components/questions/_shared/types/answer-the-question.type.js +1 -1
  183. package/dist/components/questions/_shared/types/arrange-letters-into-words.type.js +1 -1
  184. package/dist/components/questions/_shared/types/choose-correct-adjective.type.js +3 -1
  185. package/dist/components/questions/_shared/types/choose-the-correct-answer-group.type.d.ts +1 -0
  186. package/dist/components/questions/_shared/types/choose-the-correct-answer-group.type.js +1 -1
  187. package/dist/components/questions/_shared/types/color-region.config.js +1 -1
  188. package/dist/components/questions/_shared/types/cross-out-word-group.type.d.ts +2 -0
  189. package/dist/components/questions/_shared/types/cross-out-word-group.type.js +1 -1
  190. package/dist/components/questions/_shared/types/fill-in-blank.type.js +1 -1
  191. package/dist/components/questions/_shared/types/fill-missing-words-in-grid.type.js +1 -1
  192. package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +1 -1
  193. package/dist/components/questions/_shared/types/index.d.ts +1 -0
  194. package/dist/components/questions/_shared/types/index.js +14 -1
  195. package/dist/components/questions/_shared/types/label-the-picture.type.js +1 -1
  196. package/dist/components/questions/_shared/types/listen-and-choose-from-answer-group.type.js +1 -1
  197. package/dist/components/questions/_shared/types/listen-and-choose-objects-in-scene.type.js +1 -1
  198. package/dist/components/questions/_shared/types/listen-and-drag-objects-into-scene.type.js +1 -1
  199. package/dist/components/questions/_shared/types/listen-and-speak-answer.type.js +1 -1
  200. package/dist/components/questions/_shared/types/listen-and-speak-compare-images.type.js +1 -1
  201. package/dist/components/questions/_shared/types/listen-and-speak-image-group.type.js +1 -1
  202. package/dist/components/questions/_shared/types/listen-and-speak-info-exchange.type.js +1 -1
  203. package/dist/components/questions/_shared/types/listen-and-speak-odd-one-out.type.js +3 -1
  204. package/dist/components/questions/_shared/types/listen-and-speak-question-list.type.js +1 -1
  205. package/dist/components/questions/_shared/types/listen-and-speak-with-story-images.type.js +3 -1
  206. package/dist/components/questions/_shared/types/listen-and-tick-answer.type.js +1 -1
  207. package/dist/components/questions/_shared/types/listening-drawline.type.js +1 -1
  208. package/dist/components/questions/_shared/types/look-and-choose-answer.type.js +1 -1
  209. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +1 -1
  210. package/dist/components/questions/_shared/types/match-by-writing-answer.type.js +1 -1
  211. package/dist/components/questions/_shared/types/multiple-choice.type.d.ts +5 -0
  212. package/dist/components/questions/_shared/types/multiple-choice.type.js +1 -1
  213. package/dist/components/questions/_shared/types/question-group.type.js +11 -1
  214. package/dist/components/questions/_shared/types/read-and-color-objects.type.js +1 -1
  215. package/dist/components/questions/_shared/types/read-displayed-content.type.js +1 -1
  216. package/dist/components/questions/_shared/types/read-passage-and-answer-questions.type.js +1 -1
  217. package/dist/components/questions/_shared/types/speaking-describe-image.type.js +1 -1
  218. package/dist/components/questions/_shared/types/spontaneous-qa-group.type.d.ts +37 -0
  219. package/dist/components/questions/_shared/types/spontaneous-qa-group.type.js +1 -0
  220. package/dist/components/questions/_shared/types/spontaneous-qa.type.js +1 -1
  221. package/dist/components/questions/_shared/types/true-false-group.type.js +1 -1
  222. package/dist/components/questions/_shared/types/word-fill-paragraph-with-options.type.js +1 -1
  223. package/dist/components/questions/_shared/types/word-fill-paragraph.type.js +1 -1
  224. package/dist/components/questions/_shared/types/word-fill-structured-form.type.d.ts +20 -11
  225. package/dist/components/questions/_shared/types/word-fill-structured-form.type.js +1 -1
  226. package/dist/components/questions/_shared/types/word-order-and-match-group.type.d.ts +38 -0
  227. package/dist/components/questions/_shared/types/word-order-and-match-group.type.js +1 -0
  228. package/dist/components/questions/_shared/types/word-ordering.type.js +1 -1
  229. package/dist/components/questions/_shared/types/write-a-short-letter.type.js +1 -1
  230. package/dist/components/questions/_shared/types/write-correct-verb-form.type.js +3 -1
  231. package/dist/components/questions/_shared/types/write-sentences.type.js +1 -1
  232. package/dist/components/questions/_shared/types/write-short-paragraph.type.js +1 -1
  233. package/dist/components/questions/_shared/utils/answer-builder.js +79 -1
  234. package/dist/components/questions/_shared/utils/createColorRegionRegistration.js +39 -1
  235. package/dist/components/questions/_shared/utils/id-generator.js +3 -1
  236. package/dist/components/questions/_shared/utils/parseFillBlankStem.d.ts +30 -0
  237. package/dist/components/questions/_shared/utils/parseFillBlankStem.js +69 -0
  238. package/dist/components/questions/_shared/utils/question-validation.js +21 -1
  239. package/dist/components/questions/components/ColorNode.js +85 -1
  240. package/dist/components/questions/components/ColorRegionClient.js +545 -1
  241. package/dist/components/questions/components/ColorRegionCreator.js +420 -1
  242. package/dist/components/questions/components/ColorRegionNode.js +59 -1
  243. package/dist/components/questions/components/CreatorGuide.js +103 -1
  244. package/dist/components/questions/components/NameNode.js +26 -1
  245. package/dist/components/questions/components/QuestionBankOption.js +46 -1
  246. package/dist/components/questions/components/QuestionBasicInfoForm.js +9 -1
  247. package/dist/components/questions/components/QuestionCommonFields.js +33 -1
  248. package/dist/components/questions/components/QuestionSearchDropdown.js +258 -1
  249. package/dist/components/questions/components/QuestionTypeSelector.js +32 -1
  250. package/dist/components/questions/components/QuestionTypeSpecificForms.js +6 -1
  251. package/dist/components/questions/components/RegionNode.js +94 -1
  252. package/dist/components/questions/components/index.js +14 -1
  253. package/dist/components/questions/creator/QuestionCreator.js +90 -1
  254. package/dist/components/questions/creator/QuestionGroupCreator.js +119 -1
  255. package/dist/components/questions/creator/dedicated-component-router.js +79 -1
  256. package/dist/components/questions/creator/index.js +3 -1
  257. package/dist/components/questions/creator/question-type-registry.js +90 -1
  258. package/dist/components/questions/groups/ArticlesGroupCard.js +121 -1
  259. package/dist/components/questions/groups/BasicQuestionGroupCard.js +50 -1
  260. package/dist/components/questions/groups/DocumentGroupCard.js +114 -1
  261. package/dist/components/questions/groups/MatchAnswerGroupCard.js +25 -1
  262. package/dist/components/questions/index.d.ts +1 -0
  263. package/dist/components/questions/index.js +11 -1
  264. package/dist/components/questions/question-bank/EditQuestionBankDialog.d.ts +2 -0
  265. package/dist/components/questions/question-bank/EditQuestionBankDialog.js +114 -0
  266. package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +23 -0
  267. package/dist/components/questions/question-bank/QuestionEditorRenderer.js +75 -0
  268. package/dist/components/questions/question-bank/difficulty.d.ts +15 -0
  269. package/dist/components/questions/question-bank/difficulty.js +33 -0
  270. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.d.ts +5 -0
  271. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.js +101 -0
  272. package/dist/components/questions/question-bank/editor-adapters/index.d.ts +5 -0
  273. package/dist/components/questions/question-bank/editor-adapters/index.js +4 -0
  274. package/dist/components/questions/question-bank/editor-adapters/media-adapters.d.ts +6 -0
  275. package/dist/components/questions/question-bank/editor-adapters/media-adapters.js +140 -0
  276. package/dist/components/questions/question-bank/editor-adapters/types.d.ts +1 -0
  277. package/dist/components/questions/question-bank/editor-adapters/types.js +1 -0
  278. package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.d.ts +8 -0
  279. package/dist/components/questions/question-bank/editor-adapters/unsupported-fallback.js +6 -0
  280. package/dist/components/questions/question-bank/editor-adapters/writing-adapters.d.ts +5 -0
  281. package/dist/components/questions/question-bank/editor-adapters/writing-adapters.js +114 -0
  282. package/dist/components/questions/question-bank/index.d.ts +4 -0
  283. package/dist/components/questions/question-bank/index.js +3 -0
  284. package/dist/components/questions/question-bank/types.d.ts +59 -0
  285. package/dist/components/questions/question-bank/types.js +1 -0
  286. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +37 -1
  287. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +351 -1
  288. package/dist/components/questions/types/answer-the-question/register.js +67 -1
  289. package/dist/components/questions/types/answer-the-question/transform.js +36 -1
  290. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +141 -1
  291. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +118 -1
  292. package/dist/components/questions/types/arrange-letters-into-words/index.js +2 -1
  293. package/dist/components/questions/types/arrange-letters-into-words/register.js +4 -1
  294. package/dist/components/questions/types/arrange-letters-into-words/transform.js +24 -1
  295. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +74 -1
  296. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +169 -1
  297. package/dist/components/questions/types/choose-correct-adjective/index.js +2 -1
  298. package/dist/components/questions/types/choose-correct-adjective/register.js +55 -1
  299. package/dist/components/questions/types/choose-correct-adjective/transform.js +16 -1
  300. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +149 -1
  301. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +568 -1
  302. package/dist/components/questions/types/choose-the-correct-answer/index.js +3 -1
  303. package/dist/components/questions/types/choose-the-correct-answer/normalize.d.ts +9 -0
  304. package/dist/components/questions/types/choose-the-correct-answer/normalize.js +88 -1
  305. package/dist/components/questions/types/choose-the-correct-answer/register.js +99 -1
  306. package/dist/components/questions/types/choose-the-correct-answer/transform.js +70 -1
  307. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +22 -1
  308. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupCreator.js +163 -1
  309. package/dist/components/questions/types/choose-the-correct-answer-group/index.js +3 -1
  310. package/dist/components/questions/types/choose-the-correct-answer-group/map-choose-the-correct-answer-group-data.js +94 -1
  311. package/dist/components/questions/types/choose-the-correct-answer-group/register.js +36 -1
  312. package/dist/components/questions/types/choose-the-correct-answer-group/transform.js +55 -1
  313. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +27 -1
  314. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +231 -1
  315. package/dist/components/questions/types/cross-out-word-group/index.js +5 -1
  316. package/dist/components/questions/types/cross-out-word-group/map-cross-out-word-group-data.d.ts +1 -1
  317. package/dist/components/questions/types/cross-out-word-group/map-cross-out-word-group-data.js +110 -1
  318. package/dist/components/questions/types/cross-out-word-group/register.js +36 -1
  319. package/dist/components/questions/types/cross-out-word-group/transform.js +67 -1
  320. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +275 -1
  321. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +464 -1
  322. package/dist/components/questions/types/fill-in-blank/index.js +2 -1
  323. package/dist/components/questions/types/fill-in-blank/register.js +148 -1
  324. package/dist/components/questions/types/fill-in-blank/transform.js +88 -1
  325. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +117 -1
  326. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +671 -1
  327. package/dist/components/questions/types/fill-missing-words-in-grid/index.js +2 -1
  328. package/dist/components/questions/types/fill-missing-words-in-grid/register.js +37 -1
  329. package/dist/components/questions/types/fill-missing-words-in-grid/transform.js +15 -1
  330. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +262 -1
  331. package/dist/components/questions/types/gn-speaking-interview/register.js +121 -1
  332. package/dist/components/questions/types/gn-speaking-interview/transform.js +48 -1
  333. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingClient.js +16 -1
  334. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingCreator.js +27 -1
  335. package/dist/components/questions/types/image-object-matching/index.js +2 -1
  336. package/dist/components/questions/types/image-object-matching/register.js +3 -1
  337. package/dist/components/questions/types/image-object-matching/transform.js +2 -1
  338. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +102 -1
  339. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +344 -1
  340. package/dist/components/questions/types/label-the-picture/index.js +2 -1
  341. package/dist/components/questions/types/label-the-picture/register.js +54 -1
  342. package/dist/components/questions/types/label-the-picture/transform.js +28 -1
  343. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +78 -1
  344. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +232 -1
  345. package/dist/components/questions/types/listen-and-choose-from-answer-group/index.js +2 -1
  346. package/dist/components/questions/types/listen-and-choose-from-answer-group/register.js +82 -1
  347. package/dist/components/questions/types/listen-and-choose-from-answer-group/transform.js +27 -1
  348. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +70 -1
  349. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +386 -1
  350. package/dist/components/questions/types/listen-and-choose-objects-in-scene/index.js +2 -1
  351. package/dist/components/questions/types/listen-and-choose-objects-in-scene/register.js +100 -1
  352. package/dist/components/questions/types/listen-and-choose-objects-in-scene/transform.js +33 -1
  353. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +106 -1
  354. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +457 -1
  355. package/dist/components/questions/types/listen-and-drag-objects-into-scene/index.js +2 -1
  356. package/dist/components/questions/types/listen-and-drag-objects-into-scene/register.js +110 -1
  357. package/dist/components/questions/types/listen-and-drag-objects-into-scene/transform.js +35 -1
  358. package/dist/components/questions/types/listen-and-speak-answer/ArrowIndicator.js +8 -1
  359. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +22 -1
  360. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +291 -1
  361. package/dist/components/questions/types/listen-and-speak-answer/index.js +2 -1
  362. package/dist/components/questions/types/listen-and-speak-answer/register.js +84 -1
  363. package/dist/components/questions/types/listen-and-speak-answer/transform.js +27 -1
  364. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +32 -1
  365. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +295 -1
  366. package/dist/components/questions/types/listen-and-speak-compare-images/register.js +79 -1
  367. package/dist/components/questions/types/listen-and-speak-compare-images/transform.js +27 -1
  368. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +34 -1
  369. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +285 -1
  370. package/dist/components/questions/types/listen-and-speak-image-group/register.js +81 -1
  371. package/dist/components/questions/types/listen-and-speak-image-group/transform.js +26 -1
  372. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +38 -1
  373. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +385 -1
  374. package/dist/components/questions/types/listen-and-speak-info-exchange/register.js +87 -1
  375. package/dist/components/questions/types/listen-and-speak-info-exchange/transform.js +32 -1
  376. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +26 -1
  377. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +247 -1
  378. package/dist/components/questions/types/listen-and-speak-odd-one-out/register.js +59 -1
  379. package/dist/components/questions/types/listen-and-speak-odd-one-out/transform.js +18 -1
  380. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +26 -1
  381. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +256 -1
  382. package/dist/components/questions/types/listen-and-speak-question-list/register.js +81 -1
  383. package/dist/components/questions/types/listen-and-speak-question-list/transform.js +26 -1
  384. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +26 -1
  385. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +230 -1
  386. package/dist/components/questions/types/listen-and-speak-with-story-images/register.js +56 -1
  387. package/dist/components/questions/types/listen-and-speak-with-story-images/transform.js +16 -1
  388. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +72 -1
  389. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +230 -1
  390. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +1 -1
  391. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +66 -1
  392. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +42 -1
  393. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +96 -1
  394. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +238 -1
  395. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +2 -1
  396. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +76 -1
  397. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +19 -1
  398. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +134 -1
  399. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +617 -1
  400. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +2 -1
  401. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +82 -1
  402. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +29 -1
  403. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +38 -1
  404. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +359 -1
  405. package/dist/components/questions/types/match-by-writing-answer/index.js +3 -1
  406. package/dist/components/questions/types/match-by-writing-answer/register.js +58 -1
  407. package/dist/components/questions/types/match-by-writing-answer/transform.js +61 -1
  408. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsClient.js +16 -1
  409. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsCreator.js +23 -1
  410. package/dist/components/questions/types/read-and-color-objects/index.js +2 -1
  411. package/dist/components/questions/types/read-and-color-objects/register.js +3 -1
  412. package/dist/components/questions/types/read-and-color-objects/transform.js +36 -1
  413. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +125 -1
  414. package/dist/components/questions/types/read-displayed-content/register.js +64 -1
  415. package/dist/components/questions/types/read-displayed-content/transform.js +12 -1
  416. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +65 -1
  417. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +511 -1
  418. package/dist/components/questions/types/read-passage-and-answer-questions/index.js +2 -1
  419. package/dist/components/questions/types/read-passage-and-answer-questions/register.js +58 -1
  420. package/dist/components/questions/types/read-passage-and-answer-questions/transform.js +28 -1
  421. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +373 -1
  422. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +41 -1
  423. package/dist/components/questions/types/speaking-conversation/register.js +110 -1
  424. package/dist/components/questions/types/speaking-conversation/transform.js +34 -1
  425. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +74 -1
  426. package/dist/components/questions/types/speaking-cue-card/register.js +52 -1
  427. package/dist/components/questions/types/speaking-cue-card/transform.js +12 -1
  428. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +10 -1
  429. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +161 -1
  430. package/dist/components/questions/types/speaking-describe-image/index.js +2 -1
  431. package/dist/components/questions/types/speaking-describe-image/register.js +69 -1
  432. package/dist/components/questions/types/speaking-describe-image/transform.js +16 -1
  433. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +234 -1
  434. package/dist/components/questions/types/spontaneous-qa/register.js +93 -1
  435. package/dist/components/questions/types/spontaneous-qa/transform.js +34 -1
  436. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.d.ts +2 -0
  437. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.js +9 -0
  438. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.d.ts +2 -0
  439. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.js +179 -0
  440. package/dist/components/questions/types/spontaneous-qa-group/creator-helpers.d.ts +5 -0
  441. package/dist/components/questions/types/spontaneous-qa-group/creator-helpers.js +43 -0
  442. package/dist/components/questions/types/spontaneous-qa-group/flatten.d.ts +14 -0
  443. package/dist/components/questions/types/spontaneous-qa-group/flatten.js +69 -0
  444. package/dist/components/questions/types/spontaneous-qa-group/index.d.ts +2 -0
  445. package/dist/components/questions/types/spontaneous-qa-group/index.js +2 -0
  446. package/dist/components/questions/types/spontaneous-qa-group/register.d.ts +2 -0
  447. package/dist/components/questions/types/spontaneous-qa-group/register.js +139 -0
  448. package/dist/components/questions/types/spontaneous-qa-group/transform.d.ts +2 -0
  449. package/dist/components/questions/types/spontaneous-qa-group/transform.js +52 -0
  450. package/dist/components/questions/types/true-false/TrueFalseClient.js +75 -1
  451. package/dist/components/questions/types/true-false/TrueFalseCreator.js +244 -1
  452. package/dist/components/questions/types/true-false/index.js +2 -1
  453. package/dist/components/questions/types/true-false/register.js +77 -1
  454. package/dist/components/questions/types/true-false/transform.js +32 -1
  455. package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +25 -1
  456. package/dist/components/questions/types/true-false-group/TrueFalseGroupCreator.js +129 -1
  457. package/dist/components/questions/types/true-false-group/index.js +5 -1
  458. package/dist/components/questions/types/true-false-group/map-true-false-group-data.js +69 -1
  459. package/dist/components/questions/types/true-false-group/register.js +36 -1
  460. package/dist/components/questions/types/true-false-group/transform.js +43 -1
  461. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +233 -1
  462. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +631 -1
  463. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphWrapper.js +28 -1
  464. package/dist/components/questions/types/word-fill-paragraph/index.js +2 -1
  465. package/dist/components/questions/types/word-fill-paragraph/register.js +136 -1
  466. package/dist/components/questions/types/word-fill-paragraph/transform.js +56 -1
  467. package/dist/components/questions/types/word-fill-structured-form/WfsfWordBankEditor.js +86 -1
  468. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +246 -1
  469. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.d.ts +2 -0
  470. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +484 -1
  471. package/dist/components/questions/types/word-fill-structured-form/index.js +3 -1
  472. package/dist/components/questions/types/word-fill-structured-form/map-wfsf-question-data.js +113 -1
  473. package/dist/components/questions/types/word-fill-structured-form/register.js +36 -1
  474. package/dist/components/questions/types/word-fill-structured-form/transform.js +77 -1
  475. package/dist/components/questions/types/word-fill-structured-form/wfsf-word-bank.utils.js +110 -1
  476. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.d.ts +3 -0
  477. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.js +190 -0
  478. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupCreator.d.ts +3 -0
  479. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupCreator.js +182 -0
  480. package/dist/components/questions/types/word-order-and-match-group/index.d.ts +4 -0
  481. package/dist/components/questions/types/word-order-and-match-group/index.js +4 -0
  482. package/dist/components/questions/types/word-order-and-match-group/register.d.ts +2 -0
  483. package/dist/components/questions/types/word-order-and-match-group/register.js +121 -0
  484. package/dist/components/questions/types/word-order-and-match-group/transform.d.ts +2 -0
  485. package/dist/components/questions/types/word-order-and-match-group/transform.js +70 -0
  486. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +84 -1
  487. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +192 -1
  488. package/dist/components/questions/types/word-ordering/index.js +2 -1
  489. package/dist/components/questions/types/word-ordering/register.js +46 -1
  490. package/dist/components/questions/types/word-ordering/transform.js +24 -1
  491. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +99 -1
  492. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +320 -1
  493. package/dist/components/questions/types/write-a-short-letter/index.js +2 -1
  494. package/dist/components/questions/types/write-a-short-letter/register.js +68 -1
  495. package/dist/components/questions/types/write-a-short-letter/transform.js +25 -1
  496. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +61 -1
  497. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +196 -1
  498. package/dist/components/questions/types/write-correct-verb-form/index.js +2 -1
  499. package/dist/components/questions/types/write-correct-verb-form/register.js +62 -1
  500. package/dist/components/questions/types/write-correct-verb-form/transform.js +17 -1
  501. package/dist/components/questions/types/write-sentences/WriteSentencesClient.js +22 -1
  502. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +174 -1
  503. package/dist/components/questions/types/write-sentences/index.js +2 -1
  504. package/dist/components/questions/types/write-sentences/register.js +58 -1
  505. package/dist/components/questions/types/write-sentences/transform.js +28 -1
  506. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +96 -1
  507. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +161 -1
  508. package/dist/components/questions/types/write-short-paragraph/index.js +2 -1
  509. package/dist/components/questions/types/write-short-paragraph/register.js +4 -1
  510. package/dist/components/questions/types/write-short-paragraph/transform.js +21 -1
  511. package/dist/components/questions/viewer/ChooseTheCorrectAnswerGroupViewer.js +11 -1
  512. package/dist/components/questions/viewer/CrossOutWordGroupViewer.js +8 -1
  513. package/dist/components/questions/viewer/QuestionGroupViewer.js +36 -1
  514. package/dist/components/questions/viewer/QuestionViewer.js +692 -1
  515. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.d.ts +2 -1
  516. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.js +82 -1
  517. package/dist/components/questions/viewer/TrueFalseGroupViewer.js +11 -1
  518. package/dist/components/questions/viewer/WordFillStructuredFormViewer.js +14 -1
  519. package/dist/components/questions/viewer/WordOrderAndMatchGroupViewer.d.ts +3 -1
  520. package/dist/components/questions/viewer/WordOrderAndMatchGroupViewer.js +95 -1
  521. package/dist/components/questions/viewer/index.js +8 -1
  522. package/dist/components/results/ResultReviewPageContainer.js +220 -1
  523. package/dist/components/results/ReviewQuestionRenderer.js +55 -1
  524. package/dist/components/results/hooks/useResultReviewPolling.js +76 -1
  525. package/dist/components/results/index.js +22 -1
  526. package/dist/components/results/renderers/QuestionExplanations.js +19 -1
  527. package/dist/components/results/renderers/ReviewChooseAdjectiveRenderer.js +94 -1
  528. package/dist/components/results/renderers/ReviewChooseBestAnswerRenderer.js +75 -1
  529. package/dist/components/results/renderers/ReviewColoringRenderer.js +110 -1
  530. package/dist/components/results/renderers/ReviewListenAndWriteRenderer.js +48 -1
  531. package/dist/components/results/renderers/ReviewListenChooseAnswerGroupRenderer.js +87 -1
  532. package/dist/components/results/renderers/ReviewListenChooseObjectsRenderer.js +104 -1
  533. package/dist/components/results/renderers/ReviewListenDragObjectsRenderer.js +123 -1
  534. package/dist/components/results/renderers/ReviewListeningHeader.js +104 -1
  535. package/dist/components/results/renderers/ReviewSpeakingAIPanel.js +116 -1
  536. package/dist/components/results/renderers/ReviewSpeakingMedia.js +48 -1
  537. package/dist/components/results/renderers/ReviewSpeakingQuestionItem.js +42 -1
  538. package/dist/components/results/renderers/ReviewSpeakingRenderer.js +12 -1
  539. package/dist/components/results/renderers/ReviewSpeakingRenderer.types.js +85 -1
  540. package/dist/components/results/renderers/review-question-body-dedicated.js +104 -1
  541. package/dist/components/results/renderers/review-question-body-movers.js +148 -1
  542. package/dist/components/results/renderers/review-speaking-map.js +226 -1
  543. package/dist/components/results/renderers/review-spontaneous-qa-group.d.ts +4 -0
  544. package/dist/components/results/renderers/review-spontaneous-qa-group.js +74 -0
  545. package/dist/components/results/report/BadgeTitle.js +5 -1
  546. package/dist/components/results/report/ExamSkillRow.js +20 -1
  547. package/dist/components/results/report/OceanEduExamReport.js +58 -1
  548. package/dist/components/results/report/OceanEduSpeakingReport.js +62 -1
  549. package/dist/components/results/report/OverallAssessmentSection.js +17 -1
  550. package/dist/components/results/report/ResultErrorState.js +18 -1
  551. package/dist/components/results/report/ResultLoadingState.js +10 -1
  552. package/dist/components/results/report/ResultNotPublicState.js +27 -1
  553. package/dist/components/results/report/SpeakingReportParts.js +14 -1
  554. package/dist/components/results/report/SpeakingReportShared.js +86 -1
  555. package/dist/components/results/report/SpeakingRubricSection.js +171 -1
  556. package/dist/components/results/report/speaking-report.utils.js +198 -1
  557. package/dist/components/results/result-review.utils.js +6 -1
  558. package/dist/components/results/review-data.js +222 -1
  559. package/dist/components/results/review-renderer.utils.js +97 -1
  560. package/dist/components/results/review-status.utils.js +36 -1
  561. package/dist/components/results/review-types.js +1 -1
  562. package/dist/components/results/speaking/SpeakingResultAudioPlayer.js +77 -1
  563. package/dist/components/results/speaking/SpeakingReviewResult.js +48 -1
  564. package/dist/components/results/utils/review-score.utils.js +72 -1
  565. package/dist/components/results/utils/speaking-summary.js +224 -1
  566. package/dist/components/results/utils/transform-speaking-review.js +183 -1
  567. package/dist/components/shared/RichTextEditor.d.ts +9 -0
  568. package/dist/components/shared/RichTextEditor.js +47 -0
  569. package/dist/components/shared/audio/SpeakingAudioPlayer.js +100 -1
  570. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +140 -1
  571. package/dist/components/themes/cambridge-yle/CambridgeYleBottomNav.js +10 -1
  572. package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.d.ts +1 -1
  573. package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.js +18 -1
  574. package/dist/components/themes/cambridge-yle/CambridgeYleExampleText.js +11 -1
  575. package/dist/components/themes/cambridge-yle/CambridgeYleHeader.js +11 -1
  576. package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.d.ts +2 -1
  577. package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.js +19 -1
  578. package/dist/components/themes/cambridge-yle/CambridgeYleNavButton.js +13 -1
  579. package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.d.ts +1 -1
  580. package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.js +3 -1
  581. package/dist/components/themes/cambridge-yle/CambridgeYleProgressBar.js +6 -1
  582. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCard.js +5 -1
  583. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCardFullWidth.js +9 -1
  584. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.d.ts +2 -1
  585. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.js +61 -1
  586. package/dist/components/themes/cambridge-yle/CambridgeYleTimer.d.ts +1 -1
  587. package/dist/components/themes/cambridge-yle/CambridgeYleTimer.js +25 -1
  588. package/dist/components/themes/cambridge-yle/index.js +13 -1
  589. package/dist/components/themes/english-certification/EcAnswerTheQuestion.js +25 -1
  590. package/dist/components/themes/english-certification/EcMatchByWritingAnswer.js +89 -1
  591. package/dist/components/themes/english-certification/EcWordFillParagraph.js +56 -1
  592. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +192 -1
  593. package/dist/components/themes/english-certification/EcWritingTask.js +60 -1
  594. package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +108 -1
  595. package/dist/components/themes/english-certification/EnglishCertificationBrand.js +11 -1
  596. package/dist/components/themes/english-certification/EnglishCertificationExamLayout.js +34 -1
  597. package/dist/components/themes/english-certification/EnglishCertificationFlagButton.js +15 -1
  598. package/dist/components/themes/english-certification/EnglishCertificationFooterBar.js +19 -1
  599. package/dist/components/themes/english-certification/EnglishCertificationGroupedQuestion.js +166 -1
  600. package/dist/components/themes/english-certification/EnglishCertificationHeader.js +31 -1
  601. package/dist/components/themes/english-certification/EnglishCertificationInstructionBanner.js +79 -1
  602. package/dist/components/themes/english-certification/EnglishCertificationNavigationPanel.js +71 -1
  603. package/dist/components/themes/english-certification/EnglishCertificationOptionSelector.js +79 -1
  604. package/dist/components/themes/english-certification/EnglishCertificationPartHeader.js +47 -1
  605. package/dist/components/themes/english-certification/EnglishCertificationPassagePanel.js +77 -1
  606. package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.js +25 -1
  607. package/dist/components/themes/english-certification/EnglishCertificationQuestionItem.js +36 -1
  608. package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +81 -1
  609. package/dist/components/themes/english-certification/EnglishCertificationReadingSection.js +95 -1
  610. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestion.js +18 -1
  611. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js +32 -1
  612. package/dist/components/themes/english-certification/english-cert-scroll.js +163 -1
  613. package/dist/components/themes/english-certification/english-certification-theme.js +58 -1
  614. package/dist/components/themes/english-certification/grouped-question-utils.js +243 -1
  615. package/dist/components/themes/english-certification/index.js +24 -1
  616. package/dist/components/themes/index.js +25 -1
  617. package/dist/components/themes/summer-sky/SummerSkyAudioPlayer.js +170 -1
  618. package/dist/components/themes/summer-sky/SummerSkyExitModal.js +21 -1
  619. package/dist/components/themes/summer-sky/SummerSkyLayout.js +221 -1
  620. package/dist/components/themes/summer-sky/SummerSkySubmitModal.js +18 -1
  621. package/dist/components/themes/summer-sky/SummerSkyWaitingRoom.js +193 -1
  622. package/dist/components/themes/types.js +1 -1
  623. package/dist/components/ui/alert-dialog.js +26 -1
  624. package/dist/components/ui/alert.js +22 -1
  625. package/dist/components/ui/avatar.js +12 -1
  626. package/dist/components/ui/badge.js +38 -1
  627. package/dist/components/ui/button.js +35 -1
  628. package/dist/components/ui/calendar.js +76 -1
  629. package/dist/components/ui/card.js +16 -1
  630. package/dist/components/ui/carousel.js +91 -1
  631. package/dist/components/ui/checkbox.js +9 -1
  632. package/dist/components/ui/collapsible.js +13 -1
  633. package/dist/components/ui/command.js +34 -1
  634. package/dist/components/ui/confirm-dialog.js +49 -1
  635. package/dist/components/ui/dialog.js +23 -1
  636. package/dist/components/ui/drawer.js +35 -1
  637. package/dist/components/ui/dropdown-menu.js +33 -1
  638. package/dist/components/ui/file-upload.js +155 -1
  639. package/dist/components/ui/form.js +62 -1
  640. package/dist/components/ui/hover-card.js +10 -1
  641. package/dist/components/ui/image-preview.js +29 -1
  642. package/dist/components/ui/input.js +8 -1
  643. package/dist/components/ui/label.js +9 -1
  644. package/dist/components/ui/language-switcher.js +15 -1
  645. package/dist/components/ui/multi-select.js +90 -1
  646. package/dist/components/ui/points-input.js +33 -1
  647. package/dist/components/ui/popover.js +26 -1
  648. package/dist/components/ui/progress.js +16 -1
  649. package/dist/components/ui/radio-group.js +12 -1
  650. package/dist/components/ui/scroll-area.js +11 -1
  651. package/dist/components/ui/select.js +38 -1
  652. package/dist/components/ui/separator.js +8 -1
  653. package/dist/components/ui/sheet.js +40 -1
  654. package/dist/components/ui/single-select.js +76 -1
  655. package/dist/components/ui/skeleton.js +6 -1
  656. package/dist/components/ui/switch.js +8 -1
  657. package/dist/components/ui/table.js +20 -1
  658. package/dist/components/ui/tabs.js +17 -1
  659. package/dist/components/ui/textarea.js +8 -1
  660. package/dist/components/ui/tooltip.js +11 -1
  661. package/dist/index.d.ts +3 -0
  662. package/dist/index.js +24 -1
  663. package/dist/shared/constants/ApiConstant.d.ts +1 -0
  664. package/dist/shared/constants/ApiConstant.js +278 -1
  665. package/dist/shared/constants/QueryKeyConstant.js +112 -1
  666. package/dist/shared/constants/exam-level.enum.js +41 -1
  667. package/dist/shared/constants/landings/placement-test.constants.js +431 -1
  668. package/dist/shared/constants/question-skills.js +277 -1
  669. package/dist/shared/constants/routes.js +81 -1
  670. package/dist/shared/index.js +10 -1
  671. package/dist/shared/lib/hooks/index.js +10 -1
  672. package/dist/shared/lib/hooks/useAlertDialog.js +57 -1
  673. package/dist/shared/lib/hooks/useAudioPlayCounter.js +30 -1
  674. package/dist/shared/lib/hooks/useAudioPreview.js +81 -1
  675. package/dist/shared/lib/hooks/useAudioUpload.js +24 -1
  676. package/dist/shared/lib/hooks/useConfirmDialog.js +89 -1
  677. package/dist/shared/lib/hooks/useDebouncedCallback.js +32 -1
  678. package/dist/shared/lib/hooks/useDelayedAutoPlay.js +12 -1
  679. package/dist/shared/lib/hooks/useFileUpload.js +24 -1
  680. package/dist/shared/lib/hooks/useImageUrl.js +5 -1
  681. package/dist/shared/lib/hooks/usePresignedFileUrl.js +108 -1
  682. package/dist/shared/lib/hooks/useReactHookForm.js +31 -1
  683. package/dist/shared/lib/hooks/useToast.js +62 -1
  684. package/dist/shared/lib/html-content.js +22 -1
  685. package/dist/shared/lib/i18n/index.js +54 -1
  686. package/dist/shared/lib/i18n/messages/en/admin.d.ts +11 -4
  687. package/dist/shared/lib/i18n/messages/en/admin.js +2111 -1
  688. package/dist/shared/lib/i18n/messages/en/auth.js +86 -1
  689. package/dist/shared/lib/i18n/messages/en/common.js +140 -1
  690. package/dist/shared/lib/i18n/messages/en/file-upload.js +18 -1
  691. package/dist/shared/lib/i18n/messages/en/student.js +133 -1
  692. package/dist/shared/lib/i18n/messages/en/terms.js +37 -1
  693. package/dist/shared/lib/i18n/messages/en.d.ts +11 -4
  694. package/dist/shared/lib/i18n/messages/en.js +15 -1
  695. package/dist/shared/lib/i18n/messages/vi/admin.d.ts +11 -4
  696. package/dist/shared/lib/i18n/messages/vi/admin.js +2111 -1
  697. package/dist/shared/lib/i18n/messages/vi/auth.js +86 -1
  698. package/dist/shared/lib/i18n/messages/vi/common.js +140 -1
  699. package/dist/shared/lib/i18n/messages/vi/file-upload.js +18 -1
  700. package/dist/shared/lib/i18n/messages/vi/student.js +133 -1
  701. package/dist/shared/lib/i18n/messages/vi/terms.js +37 -1
  702. package/dist/shared/lib/i18n/messages/vi.d.ts +11 -4
  703. package/dist/shared/lib/i18n/messages/vi.js +15 -1
  704. package/dist/shared/lib/i18n/types.js +1 -1
  705. package/dist/shared/lib/stores/examQuestionStore.js +212 -1
  706. package/dist/shared/lib/stores/examTakingStore.js +240 -1
  707. package/dist/shared/lib/stores/gradingTransientStore.js +39 -1
  708. package/dist/shared/lib/stores/localeStore.js +10 -1
  709. package/dist/shared/lib/themes/config/starters.config.js +71 -1
  710. package/dist/shared/lib/themes/index.js +49 -1
  711. package/dist/shared/lib/themes/types.js +2 -1
  712. package/dist/shared/lib/toast.js +43 -1
  713. package/dist/shared/lib/utils/public-page.js +44 -1
  714. package/dist/shared/lib/utils/question-reverse-transform.js +438 -1
  715. package/dist/shared/lib/utils/question-transform-types.js +1 -1
  716. package/dist/shared/lib/utils/question-transform-validation.js +194 -1
  717. package/dist/shared/lib/utils/question-transform.js +199 -1
  718. package/dist/shared/lib/utils.js +168 -1
  719. package/dist/shared/lib/validation/choose-correct-answer.validation.js +111 -1
  720. package/dist/shared/lib/validation/label-the-picture.validation.js +19 -1
  721. package/dist/shared/lib/validation/listen-and-choose-from-answer-group.validation.js +46 -1
  722. package/dist/shared/lib/validation/listen-and-choose-objects-in-scene.validation.js +21 -1
  723. package/dist/shared/lib/validation/listen-and-drag-objects-into-scene.validation.js +34 -1
  724. package/dist/shared/lib/validation/listen-and-speak-answer.validation.js +40 -1
  725. package/dist/shared/lib/validation/listen-and-speak-compare-images.validation.js +45 -1
  726. package/dist/shared/lib/validation/listen-and-speak-image-group.validation.js +19 -1
  727. package/dist/shared/lib/validation/listen-and-speak-info-exchange.validation.js +39 -1
  728. package/dist/shared/lib/validation/listen-and-speak-odd-one-out.validation.js +32 -1
  729. package/dist/shared/lib/validation/listen-and-speak-question-list.validation.js +19 -1
  730. package/dist/shared/lib/validation/listen-and-speak-with-story-images.validation.js +28 -1
  731. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +12 -1
  732. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +47 -1
  733. package/dist/shared/lib/validation/read-and-color-objects.validation.js +29 -1
  734. package/dist/shared/lib/validation/read-displayed-content.validation.js +28 -1
  735. package/dist/shared/lib/validation/speaking-conversation.validation.js +75 -1
  736. package/dist/shared/lib/validation/speaking-describe-image.validation.js +31 -1
  737. package/dist/shared/lib/validation/spontaneous-qa.validation.js +48 -1
  738. package/dist/shared/lib/validation/word-fill-paragraph-default.validation.js +38 -1
  739. package/dist/shared/lib/validation/word-fill-paragraph-with-options.validation.js +39 -1
  740. package/dist/shared/lib/validation/word-fill-paragraph.validation.js +43 -1
  741. package/dist/shared/lib/validation/word-ordering.validation.js +33 -1
  742. package/dist/shared/types/branch.types.js +6 -1
  743. package/dist/shared/types/common.types.js +49 -1
  744. package/dist/shared/types/entities/exam-schedule.types.js +5 -1
  745. package/dist/shared/types/entities/exam.types.js +1 -1
  746. package/dist/shared/types/entities/question.types.js +4 -1
  747. package/dist/shared/types/entities/result.types.js +4 -1
  748. package/dist/shared/types/entities/user.types.js +4 -1
  749. package/dist/shared/types/exam-taking.types.js +2 -1
  750. package/dist/shared/types/history.types.js +34 -1
  751. package/dist/shared/types/index.js +1 -1
  752. package/dist/shared/types/questions/choose-correct-adjective.js +4 -1
  753. package/dist/shared/types/questions/choose-the-correct-answer-group.d.ts +1 -0
  754. package/dist/shared/types/questions/choose-the-correct-answer-group.js +1 -1
  755. package/dist/shared/types/questions/choose-the-correct-answer.js +9 -1
  756. package/dist/shared/types/questions/cross-out-word-group.d.ts +2 -0
  757. package/dist/shared/types/questions/cross-out-word-group.js +1 -1
  758. package/dist/shared/types/questions/fill-missing-words-in-grid.js +11 -1
  759. package/dist/shared/types/questions/index.d.ts +1 -0
  760. package/dist/shared/types/questions/index.js +46 -1
  761. package/dist/shared/types/questions/listen-and-speak-answer.js +1 -1
  762. package/dist/shared/types/questions/listen-and-speak-compare-images.js +1 -1
  763. package/dist/shared/types/questions/listen-and-speak-image-group.js +1 -1
  764. package/dist/shared/types/questions/listen-and-speak-info-exchange.js +3 -1
  765. package/dist/shared/types/questions/listen-and-speak-odd-one-out.js +3 -1
  766. package/dist/shared/types/questions/listen-and-speak-question-list.js +1 -1
  767. package/dist/shared/types/questions/listen-and-speak-with-story-images.js +3 -1
  768. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +5 -1
  769. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +9 -1
  770. package/dist/shared/types/questions/look-picture-fill-word-hint.js +7 -1
  771. package/dist/shared/types/questions/read-displayed-content.js +1 -1
  772. package/dist/shared/types/questions/speaking-conversation.js +7 -1
  773. package/dist/shared/types/questions/speaking-describe-image.js +1 -1
  774. package/dist/shared/types/questions/spontaneous-qa.js +1 -1
  775. package/dist/shared/types/questions/word-order-and-match-group.d.ts +33 -0
  776. package/dist/shared/types/questions/word-order-and-match-group.js +1 -0
  777. package/dist/shared/types/questions/write-correct-verb-form.js +12 -1
  778. package/dist/shared/types/role.types.js +1 -1
  779. package/dist/shared/types/structure-management.js +28 -1
  780. package/dist/shared/types/student.types.js +57 -1
  781. package/dist/shared/types/teacher-dashboard-rooms.js +2 -1
  782. package/package.json +184 -166
  783. package/dist/mcp/config.d.ts +0 -12
  784. package/dist/mcp/config.js +0 -1
  785. package/dist/mcp/http-client.d.ts +0 -6
  786. package/dist/mcp/http-client.js +0 -1
  787. package/dist/mcp/index.d.ts +0 -6
  788. package/dist/mcp/index.js +0 -2
  789. package/dist/mcp/tools/_helpers.d.ts +0 -4
  790. package/dist/mcp/tools/_helpers.js +0 -1
  791. package/dist/mcp/tools/exams.d.ts +0 -2
  792. package/dist/mcp/tools/exams.js +0 -1
  793. package/dist/mcp/tools/index.d.ts +0 -2
  794. package/dist/mcp/tools/index.js +0 -1
  795. package/dist/mcp/tools/results.d.ts +0 -2
  796. package/dist/mcp/tools/results.js +0 -1
@@ -1 +1,568 @@
1
- "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{ImagePreview as t}from"../../../../components/ui/image-preview";import{useState as s,useRef as n,useEffect as a,useMemo as o}from"react";import{Button as i}from"../../../../components/ui/button";import{Label as l}from"../../../../components/ui/label";import{Input as c}from"../../../../components/ui/input";import{Textarea as d}from"../../../../components/ui/textarea";import{PointsInput as m}from"../../../../components/ui/points-input";import{Card as g,CardContent as p,CardHeader as u,CardTitle as h}from"../../../../components/ui/card";import{RadioGroup as x}from"../../../../components/ui/radio-group";import{Switch as f}from"../../../../components/ui/switch";import{usePresignedFileUrl as b,useDebouncedCallback as w}from"../../../../shared/lib/hooks";import{useReactHookForm as N}from"../../../../shared/lib/hooks/useReactHookForm";import{Pencil as y,Eye as v,Plus as A,Trash2 as C,HelpCircle as V,CheckCircle2 as U,Lightbulb as k,MessageSquare as T,ImageIcon as S,Type as E,Users as q,BookOpen as I,Star as G,ListChecks as O}from"lucide-react";import{FileUpload as $}from"../../../../components/ui/file-upload";import{ChooseTheCorrectAnswerClient as j}from"./ChooseTheCorrectAnswerClient";import{normalizeChooseAnswerCorrectAnswer as R,normalizeChooseAnswerOptions as z,pickRawChooseAnswer as B}from"./normalize";import{chooseCorrectAnswerFormSchema as F}from"../../../../shared/lib/validation/choose-correct-answer.validation";import{BasicQuestionGroupCard as J}from"../../groups/BasicQuestionGroupCard";import{ArticlesGroupCard as P}from"../../groups/ArticlesGroupCard";import{DocumentGroupCard as D}from"../../groups/DocumentGroupCard";import{useBasicQuestionGroup as L}from"../../_shared/hooks/useBasicQuestionGroup";import{z as M}from"zod";function Q({src:r,alt:t}){const{previewUrl:s,isLoading:n}=b(r);return e("div",n?{className:"flex h-24 w-full max-w-[200px] items-center justify-center rounded-lg border border-gray-200 bg-gray-50",children:e("div",{className:"h-5 w-5 animate-spin rounded-full border-2 border-gray-200 border-t-blue-500"})}:{className:"relative h-24 w-full max-w-[200px] overflow-hidden rounded-lg border border-gray-200 bg-gray-50",children:e("img",{src:s||r,alt:t,className:"h-full w-full object-contain",onError:e=>{const r=e.target;r.onerror=null,r.src="/images/placeholder-image.svg"}})})}function H(e,r){const{optionCount:t,defaultValue:s,defaultQuestion:n,multipleAnswers:a}=r,o=e?.content?.options||e?.options,i=o?.length>0?z(o):s&&s.length>0?s:Array.from({length:t},()=>""),l=R(B(e?.correctAnswer),o),c=Boolean(e?.multipleAnswers??e?.content?.multipleAnswers??e?.questionGroup?.payload?.multipleAnswers??Array.isArray(l))||Boolean(a);return{question:e?.content?.question||e?.question||n||"",optionType:e?.content?.optionType||e?.optionType||"text",options:i,correctAnswer:c?Array.isArray(l)?l:-1===l?[]:[l]:Array.isArray(l)?l[0]??-1:l,multipleAnswers:c,imageUrl:e?.content?.imageUrl||e?.imageUrl||"",explanation:e?.explanation||"",askerName:e?.content?.askerName||e?.askerName||"",answererName:e?.content?.answererName||e?.answererName||"",isExample:e?.isExample||!1,points:e?.points??1}}function Z({initialData:R,onSave:z,onCancel:B,onChange:Z,externalErrors:K,optionCount:X=4,optionMode:_,defaultValue:W,defaultQuestion:Y,viewMode:ee,onUnsavedChangesChange:re,validationRef:te,groups:se,questionIndexInPart:ne=0,partId:ae="",questionConfig:oe,groupType:ie,groupConfig:le,articleCount:ce=0,multipleAnswers:de,headerExtra:me}){const ge=R,pe=o(()=>F.superRefine((e,r)=>{"CONVERSATION"===ee&&(e.askerName?.trim()||r.addIssue({code:M.ZodIssueCode.custom,message:"Vui lòng nhập tên người hỏi!",path:["askerName"]}),e.answererName?.trim()||r.addIssue({code:M.ZodIssueCode.custom,message:"Vui lòng nhập tên người trả lời!",path:["answererName"]}))}),[ee]),ue=o(()=>({optionCount:X,defaultValue:W,defaultQuestion:Y,multipleAnswers:de}),[X,W,Y,de]),he=N({defaultValues:H(ge,ue),zodSchema:pe,mode:"onBlur"}),[xe,fe]=s(!1),[be,we]=s(""),[Ne,ye]=s({}),{isGrouped:ve,currentGroupNumber:Ae,isFirstInGroup:Ce,groupTitle:Ve,groupSubtitle:Ue,groupContent:ke,imageUrl:Te,articles:Se,setGroupTitle:Ee,setGroupSubtitle:qe,setGroupContent:Ie,setImageUrl:Ge,setArticles:Oe}=L({partId:ae,groups:se,questionIndexInPart:ne,initialData:{title:R?.groupTitle||R?.questionGroup?.payload?.title||R?.title||"",subtitle:R?.groupSubtitle||R?.questionGroup?.payload?.subtitle||"",content:R?.groupContent||R?.questionGroup?.payload?.content||"",imageUrl:R?.groupImageUrl||R?.questionGroup?.payload?.imageUrl||"",articles:R?.articles||R?.questionGroup?.payload?.articles||void 0},articleCount:ce}),$e=n(null),je=n(null),Re=n(R),ze=n(!1),Be=n(Z),Fe=n("");a(()=>{Be.current=Z},[Z]);const Je=w(e=>{Be.current?.(e)},300),Pe=he.watch("question"),De=he.watch("optionType"),Le=he.watch("options"),Me=he.watch("correctAnswer"),Qe=he.watch("multipleAnswers"),He=he.watch("imageUrl"),Ze=he.watch("explanation"),Ke=he.watch("askerName"),Xe=he.watch("answererName"),_e=he.watch("isExample"),We=he.watch("points"),Ye="image"===_?.toLowerCase()?"image":"text",er=(ie||"BASIC").toUpperCase(),rr=!oe||0===oe.length||oe.includes("imageUrl"),tr=!le||0===le.length||le.includes("imageUrl");a(()=>{Ye&&De!==Ye&&he.setValue("optionType",Ye)},[Ye,De,he]);const{previewUrl:sr}=b(He),nr=be||sr||"";a(()=>{te&&(te.current={triggerValidation:async()=>await he.trigger(),getFormData:()=>({question:Pe,optionType:De,options:Le,correctAnswer:Me,multipleAnswers:Qe,imageUrl:rr?He:"",explanation:Ze,askerName:Ke,answererName:Xe,isExample:_e,points:We,groupTitle:Ve,groupSubtitle:Ue,groupContent:ke,groupImageUrl:tr?Te:"",groupNumber:Ae,..."ARTICLES"===er?{articles:Se}:{}})})},[te,he,Me,Qe,Pe,De,Le,He,Ze,Ke,Xe,_e,We,Ve,Ue,ke,Te,Ae,ie,Se]),a(()=>{const e=R,r={...H(e,ue),groupTitle:e?.groupTitle||e?.questionGroup?.payload?.title||"",groupContent:e?.groupContent||e?.questionGroup?.payload?.content||"",groupImageUrl:e?.groupImageUrl||e?.questionGroup?.payload?.imageUrl||""};if(!(JSON.stringify(Re.current)!==JSON.stringify(R)))return;Re.current=R;const t={question:Pe,optionType:De,options:Le,correctAnswer:Me,multipleAnswers:Qe,imageUrl:He,explanation:Ze,askerName:Ke,answererName:Xe,isExample:_e,points:We},s=t.question===r.question&&t.optionType===r.optionType&&JSON.stringify(t.correctAnswer)===JSON.stringify(r.correctAnswer)&&t.multipleAnswers===r.multipleAnswers&&t.points===r.points&&t.explanation===r.explanation&&t.imageUrl===r.imageUrl&&JSON.stringify(t.options)===JSON.stringify(r.options);if(s&&!0===R?.isCompleted)return;const n=!s&&(JSON.stringify(r.correctAnswer)!==JSON.stringify(t.correctAnswer)||r.multipleAnswers!==t.multipleAnswers||r.question!==t.question||r.imageUrl!==t.imageUrl),a=Array.isArray(r.correctAnswer)?r.correctAnswer.length>0:-1!==r.correctAnswer;n&&a&&(ze.current=!0,he.reset(r),setTimeout(()=>{ze.current=!1},150))},[R,X,de,he,Pe,De,Le,Me,Qe,He,Ze,Ke,Xe,_e,We,ue]);const ar=JSON.stringify(Le);a(()=>{if(ze.current){const e=JSON.stringify({question:Pe,optionType:De,options:Le,correctAnswer:Me,multipleAnswers:Qe,imageUrl:He,explanation:Ze,askerName:Ke,answererName:Xe,isExample:_e,points:We,groupTitle:Ve,groupSubtitle:Ue,groupContent:ke,groupImageUrl:Te,groupNumber:Ae,..."ARTICLES"===er?{articles:Se}:{}});return void(Fe.current=e)}const e={question:Pe,optionType:De,options:Le,correctAnswer:Me,multipleAnswers:Qe,imageUrl:rr?He:"",explanation:Ze,askerName:Ke,answererName:Xe,isExample:_e,points:We,groupTitle:Ve,groupSubtitle:Ue,groupContent:ke,groupImageUrl:tr?Te:"",groupNumber:Ae,..."ARTICLES"===er?{articles:Se}:{}},r=JSON.stringify(e);Fe.current!==r&&(Fe.current=r,Je(e))},[Pe,De,ar,Me,Qe,He,Ze,Ke,Xe,_e,We,Ve,Ue,ke,Te,Ae,ie,Se]);he.handleSubmit(e=>{if(e.multipleAnswers){if((Array.isArray(e.correctAnswer)?e.correctAnswer:-1===e.correctAnswer?[]:[e.correctAnswer]).some(r=>r<0||r>=e.options.length))return void he.setError("correctAnswer",{message:"Đáp án đúng không hợp lệ!"})}else{const r="number"==typeof e.correctAnswer?e.correctAnswer:-1;if(-1!==r&&r>=e.options.length)return void he.setError("correctAnswer",{message:"Đáp án đúng không hợp lệ!"})}$e.current=null,je.current=null,z?.(e)});if(xe){const t={question:Pe||"",optionType:De,options:Le,correctAnswer:Me,multipleAnswers:Qe,imageUrl:nr||He,explanation:Ze,askerName:Ke,answererName:Xe,isExample:_e,isGroup:ve,groupTitle:Ve,groupSubtitle:Ue,groupContent:ke,groupImageUrl:Te,groupNumber:Ae,..."ARTICLES"===er?{articles:Se}:{}};return r("div",{className:"space-y-4",children:[e(g,{children:e(u,{children:r("div",{className:"flex items-center justify-between",children:[r("div",{children:[e(h,{children:"Choose the correct answer (Preview)"}),e("p",{className:"mt-1 text-sm text-gray-500",children:"Chế độ xem trước - Hiển thị đáp án đúng"})]}),r(i,{variant:"outline",size:"sm",onClick:()=>fe(!1),children:[e(y,{className:"mr-2 h-4 w-4"}),"Quay lại Edit Mode"]})]})})}),e(j,{questionData:t,questionConfig:oe,isReviewMode:!0,userAnswer:Qe||-1!==Me?Me:void 0})]})}return r("div",{className:"space-y-4",children:[ve&&"ARTICLES"===er&&e(P,{groupNumber:Ae,articleCount:ce,articles:Se,onArticlesChange:Oe,title:Ve,onTitleChange:Ee,isReadOnly:!Ce,uploadIdPrefix:`choose-correct-answer-${ae??"articles"}`}),ve&&"BASIC"===er&&e(J,{groupNumber:Ae,title:Ve,subtitle:Ue,content:ke,imageUrl:Te,onTitleChange:Ee,onSubtitleChange:qe,onContentChange:Ie,onImageChange:Ge,groupConfig:ie?le:void 0,isReadOnly:!Ce,uploadIdPrefix:`choose-correct-answer-${ae??"group"}`}),ve&&"DOCUMENT"===er&&e(D,{groupNumber:Ae,title:Ve,subtitle:Ue,content:ke,imageUrl:Te,onTitleChange:Ee,onSubtitleChange:qe,onContentChange:Ie,onImageChange:Ge,groupConfig:ie?le:void 0,isReadOnly:!Ce,uploadIdPrefix:`choose-correct-answer-${ae??"document"}`}),r(g,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50",children:[e(u,{className:"px-4 py-3",children:r("div",{className:"flex items-center justify-between",children:[r("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gray-100",children:e(V,{className:"h-4 w-4 text-gray-600"})}),r("div",{children:[e(h,{className:"text-base font-bold text-gray-900",children:"Chọn đáp án đúng"}),e("p",{className:"text-xs text-gray-500",children:"Tạo câu hỏi trắc nghiệm với nhiều lựa chọn"})]})]}),r("div",{className:"flex items-center gap-2",children:[me,r(i,{variant:"outline",size:"sm",onClick:()=>fe(!0),disabled:Le.filter(e=>"string"==typeof e&&e.trim()).length<2,className:"gap-2 border-indigo-200 text-indigo-600 hover:bg-indigo-50 hover:text-indigo-700",children:[e(v,{className:"h-4 w-4"}),"Xem trước"]})]})]})}),r(p,{className:"space-y-3 px-4 pb-4",children:[r("div",{className:"flex flex-wrap items-center justify-between gap-y-2",children:[e(m,{...he.register("points",{valueAsNumber:!0}),error:he.hasError("points")?he.getFieldError("points"):void 0}),r("div",{className:"flex items-center gap-2",children:[r("button",{type:"button",style:{paddingBlock:"calc(var(--spacing) * 1)"},onClick:()=>he.setValue("isExample",!_e),className:"inline-flex items-center gap-2 rounded-md border px-3.5 text-sm font-medium transition-colors select-none "+(_e?"border-amber-300 bg-amber-50 text-amber-700":"border-gray-200 bg-white text-gray-400 hover:text-gray-600"),children:[e(G,{className:"h-4 w-4 "+(_e?"fill-amber-400 text-amber-400":"")}),"Example"]}),r("button",{type:"button",style:{paddingBlock:"calc(var(--spacing) * 1)"},disabled:Boolean(de),onClick:()=>{const e=!Qe,r=he.getValues("correctAnswer"),t=Array.isArray(r)?r:-1===r?[]:[r];he.setValue("multipleAnswers",e,{shouldValidate:!0}),he.setValue("correctAnswer",e?t:t[0]??-1,{shouldValidate:!0})},className:`inline-flex items-center gap-2 rounded-md border px-3.5 text-sm font-medium transition-colors select-none ${Qe?"border-violet-300 bg-violet-50 text-violet-700":"border-gray-200 bg-white text-gray-400 hover:text-gray-600"} ${Boolean(de)?"cursor-not-allowed opacity-50":""}`,children:[e(O,{className:"h-4 w-4"}),"Nhiều đáp án"]})]})]}),"CONVERSATION"===ee&&r("div",{className:"space-y-3",children:[r(l,{className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(q,{className:"h-4 w-4 text-indigo-500"}),"Hội thoại ",e("span",{className:"text-sm font-normal text-gray-500",children:"(tuỳ chọn - dùng cho câu hỏi dạng hội thoại)"})]}),r("div",{className:"grid grid-cols-2 gap-4 rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4",children:[r("div",{className:"space-y-1.5",children:[r(l,{htmlFor:"askerName",className:"flex items-center gap-1 text-sm font-medium text-gray-600",children:["Tên người hỏi ",e("span",{className:"text-red-500",children:"*"})]}),e(c,{id:"askerName",...he.register("askerName"),placeholder:"VD: John, Mary, Teacher...",className:"border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 "+(he.hasError("askerName")?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),he.hasError("askerName")&&e("p",{className:"text-sm text-red-600",children:he.getFieldError("askerName")})]}),r("div",{className:"space-y-1.5",children:[r(l,{htmlFor:"answererName",className:"flex items-center gap-1 text-sm font-medium text-gray-600",children:["Tên người trả lời ",e("span",{className:"text-red-500",children:"*"})]}),e(c,{id:"answererName",...he.register("answererName"),placeholder:"VD: Tom, Anna, Student...",className:"border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 "+(he.hasError("answererName")?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),he.hasError("answererName")&&e("p",{className:"text-sm text-red-600",children:he.getFieldError("answererName")})]})]})]}),(!oe||0===oe.length||oe.includes("question"))&&r("div",{className:"space-y-2",children:[r(l,{htmlFor:"question",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(T,{className:"h-4 w-4 text-indigo-500"}),e("span",{className:"flex items-center gap-1",children:"Câu hỏi"}),e("span",{className:"text-sm font-normal text-gray-500",children:"(tuỳ chọn)"})]}),e(d,{id:"question",...he.register("question"),placeholder:"Nhập câu hỏi...",className:"min-h-[100px] border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 "+(he.hasError("question")?"border-red-400 focus:border-red-500 focus:ring-red-200":""),rows:3}),he.hasError("question")&&e("p",{className:"text-sm text-red-600",children:he.getFieldError("question")})]}),(!oe||0===oe.length||oe.includes("imageUrl")||oe.includes("imageUrl "))&&r("div",{className:"space-y-2",children:[r(l,{className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(S,{className:"h-4 w-4 text-indigo-500"}),"Hình ảnh ",e("span",{className:"text-sm font-normal text-gray-500"})]}),e($,{id:"question-image",label:"",accept:"image/*",value:He||"",onChange:e=>{he.setValue("imageUrl",e,{shouldValidate:!0})},onPresignedUrlChange:we,maxSize:5,placeholder:"Upload ảnh hoặc paste URL",autoUpload:!0,prefix:"questions"}),he.hasError("imageUrl")&&e("p",{className:"mt-1 text-sm text-red-600",children:he.getFieldError("imageUrl")}),nr&&e(t,{src:nr,alt:"Preview",className:"mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200"})]}),K&&K.length>0&&r("div",{className:"flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4",children:[e("div",{className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100",children:e("span",{className:"text-lg",children:"❌"})}),r("div",{className:"text-sm",children:[e("p",{className:"font-semibold text-red-800",children:"Lỗi:"}),e("ul",{className:"mt-1 list-inside list-disc text-red-700",children:K.map((r,t)=>e("li",{children:r},t))})]})]}),he.formState.errors.options?.message&&e("div",{className:"rounded-xl border border-red-200 bg-red-50 p-4",children:e("p",{className:"text-sm text-red-600",children:he.formState.errors.options.message})})]})]}),(!oe||0===oe.length||oe.includes("options"))&&r(g,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-indigo-100/50",children:[e(u,{className:"px-4 py-3",children:r("div",{className:"flex items-center justify-between",children:[r("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100",children:e(k,{className:"h-3.5 w-3.5 text-indigo-600"})}),r(h,{className:"text-base font-semibold text-gray-800",children:["Các lựa chọn ",r("span",{className:"text-xs font-normal text-gray-500",children:["(",Le.length," đáp án)"]})]})]}),r("div",{className:"flex items-center gap-3",children:[r("div",{className:"flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-3 py-1.5",children:[e(E,{className:"h-3.5 w-3.5 "+("text"===De?"text-indigo-600":"text-gray-400")}),e("span",{className:"text-xs font-medium "+("text"===De?"text-indigo-600":"text-gray-400"),children:"Text"}),e(f,{checked:"image"===De,disabled:!0,className:"!bg-indigo-600 !opacity-100",onCheckedChange:e=>{const r=e?"image":"text",t=he.getValues("options"),s=he.getValues("correctAnswer");"text"===De?$e.current={options:t,correctAnswer:s}:je.current={options:t,correctAnswer:s};const n="text"===r?$e.current:je.current;if(n)he.setValue("options",n.options),he.setValue("correctAnswer",n.correctAnswer);else{const e=Array.from({length:t.length},()=>"");he.setValue("options",e),he.setValue("correctAnswer",-1)}he.setValue("optionType",r),he.clearErrors(),ye({})}}),e(S,{className:"h-3.5 w-3.5 "+("image"===De?"text-indigo-600":"text-gray-400")}),e("span",{className:"text-xs font-medium "+("image"===De?"text-indigo-600":"text-gray-400"),children:"Ảnh"})]}),r(i,{variant:"outline",size:"sm",onClick:()=>{const e=he.getValues("options");he.setValue("options",[...e,""])},className:"gap-2 border-green-200 text-green-600 hover:bg-green-50 hover:text-green-700",children:[e(A,{className:"h-4 w-4"}),"Thêm đáp án"]})]})]})}),r(p,{className:"space-y-3 px-4 pb-4",children:[Qe?e("div",{className:"space-y-3",children:Le.map((t,s)=>{const n=he.formState.errors.options?.[s],a=(Array.isArray(Me)?Me:-1===Me?[]:[Me]).includes(s),o=String.fromCharCode(65+s);return e("div",{className:"group relative rounded-xl border-2 p-4 transition-all duration-200 "+(a?"border-green-300 bg-gradient-to-r from-green-50 to-emerald-50 shadow-md shadow-green-100":"border-gray-200 bg-white hover:border-gray-300 hover:shadow-sm"),children:r("div",{className:"flex items-start gap-4",children:[e("div",{className:"flex h-10 w-10 flex-shrink-0 cursor-pointer items-center justify-center rounded-xl font-bold transition-all "+(a?"bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md":"bg-gray-100 text-gray-600 hover:bg-indigo-100 hover:text-indigo-600"),onClick:()=>{if(Qe){const e=Array.isArray(he.getValues("correctAnswer"))?he.getValues("correctAnswer"):[],r=e.includes(s)?e.filter(e=>e!==s):[...e,s].sort((e,r)=>e-r);return void he.setValue("correctAnswer",r,{shouldValidate:!0})}he.setValue("correctAnswer",s,{shouldValidate:!0})},children:o}),r("div",{className:"flex-1 space-y-2",children:["image"===De?r("div",{className:"space-y-2",children:[e($,{id:`option-image-${s}`,label:"",accept:"image/*",value:t||"",onChange:e=>{const r=[...Le];r[s]=e,he.setValue("options",r,{shouldValidate:!0})},onPresignedUrlChange:e=>{ye(r=>({...r,[s]:e}))},maxSize:5,placeholder:`Upload ảnh đáp án ${o}...`,autoUpload:!0,prefix:"questions/options"}),(Ne[s]||t)&&e(Q,{src:Ne[s]||t,alt:`Đáp án ${o}`})]}):e(c,{id:`option-input-${s}`,...he.register(`options.${s}`),placeholder:`Nhập đáp án ${o}...`,className:"border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 "+(n?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),n&&e("p",{className:"text-sm text-red-600",children:n.message}),a&&r("span",{className:"inline-flex items-center gap-1.5 rounded-full bg-green-100 px-3 py-1 text-xs font-semibold text-green-700",children:[e(U,{className:"h-3.5 w-3.5"}),Qe?"Đáp án đúng đã chọn":"Đáp án đúng"]})]}),r("div",{className:"flex items-center gap-2",children:[e(i,{type:"button",variant:"ghost",size:"sm",onClick:()=>{if(Qe){const e=Array.isArray(he.getValues("correctAnswer"))?he.getValues("correctAnswer"):[],r=e.includes(s)?e.filter(e=>e!==s):[...e,s].sort((e,r)=>e-r);return void he.setValue("correctAnswer",r,{shouldValidate:!0})}he.setValue("correctAnswer",s,{shouldValidate:!0})},className:a?"text-green-600 hover:text-green-700":"text-gray-400 hover:text-green-600",title:Qe?a?"Bỏ chọn đáp án đúng":"Chọn thêm đáp án đúng":"Chọn làm đáp án đúng",children:e(U,{className:"h-4 w-4"})}),Le.length>2&&e(i,{variant:"ghost",size:"sm",onClick:()=>{const e=he.getValues("options").filter((e,r)=>r!==s);he.setValue("options",e);const r=he.getValues("correctAnswer");if(Array.isArray(r)){const e=r.filter(e=>e!==s).map(e=>e>s?e-1:e);he.setValue("correctAnswer",e,{shouldValidate:!0})}else r===s?he.setValue("correctAnswer",-1,{shouldValidate:!0}):"number"==typeof r&&r>s&&he.setValue("correctAnswer",r-1,{shouldValidate:!0})},className:"text-red-500 hover:bg-red-50 hover:text-red-600",children:e(C,{className:"h-4 w-4"})})]})]})},s)})}):e(x,{value:-1===Me?"":Me.toString(),onValueChange:e=>{he.setValue("correctAnswer",Number(e),{shouldValidate:!0})},children:Le.map((t,s)=>{const n=he.formState.errors.options?.[s],a=Me===s,o=String.fromCharCode(65+s);return e("div",{className:"group relative rounded-xl border-2 p-4 transition-all duration-200 "+(a?"border-green-300 bg-gradient-to-r from-green-50 to-emerald-50 shadow-md shadow-green-100":"border-gray-200 bg-white hover:border-gray-300 hover:shadow-sm"),children:r("div",{className:"flex items-start gap-4",children:[e("div",{className:"flex h-10 w-10 flex-shrink-0 cursor-pointer items-center justify-center rounded-xl font-bold transition-all "+(a?"bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md":"bg-gray-100 text-gray-600 hover:bg-indigo-100 hover:text-indigo-600"),onClick:()=>{he.setValue("correctAnswer",s,{shouldValidate:!0})},children:o}),r("div",{className:"flex-1 space-y-2",children:["image"===De?r("div",{className:"space-y-2",children:[e($,{id:`option-image-${s}`,label:"",accept:"image/*",value:t||"",onChange:e=>{const r=[...Le];r[s]=e,he.setValue("options",r,{shouldValidate:!0})},onPresignedUrlChange:e=>{ye(r=>({...r,[s]:e}))},maxSize:5,placeholder:`Upload ảnh đáp án ${o}...`,autoUpload:!0,prefix:"questions/options"}),(Ne[s]||t)&&e(Q,{src:Ne[s]||t,alt:`Đáp án ${o}`})]}):e(c,{id:`option-input-${s}`,...he.register(`options.${s}`),placeholder:`Nhập đáp án ${o}...`,className:"border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 "+(n?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),n&&e("p",{className:"text-sm text-red-600",children:n.message}),a&&r("span",{className:"inline-flex items-center gap-1.5 rounded-full bg-green-100 px-3 py-1 text-xs font-semibold text-green-700",children:[e(U,{className:"h-3.5 w-3.5"}),"Đáp án đúng"]})]}),r("div",{className:"flex items-center gap-2",children:[e(i,{type:"button",variant:"ghost",size:"sm",onClick:()=>{he.setValue("correctAnswer",s,{shouldValidate:!0})},className:a?"text-green-600 hover:text-green-700":"text-gray-400 hover:text-green-600",title:"Chọn làm đáp án đúng",children:e(U,{className:"h-4 w-4"})}),Le.length>2&&e(i,{variant:"ghost",size:"sm",onClick:()=>{const e=he.getValues("options").filter((e,r)=>r!==s);he.setValue("options",e);const r=he.getValues("correctAnswer");"number"==typeof r&&r===s?he.setValue("correctAnswer",-1,{shouldValidate:!0}):"number"==typeof r&&r>s&&he.setValue("correctAnswer",r-1,{shouldValidate:!0})},className:"text-red-500 hover:bg-red-50 hover:text-red-600",children:e(C,{className:"h-4 w-4"})})]})]})},s)})}),(Array.isArray(Me)&&0===Me.length||!Array.isArray(Me)&&-1===Me)&&r("div",{className:"flex items-start gap-3 rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4",children:[e("div",{className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-amber-100",children:e("span",{className:"text-lg",children:"⚠️"})}),r("div",{className:"text-sm text-amber-800",children:[e("p",{className:"font-semibold",children:"Chưa chọn đáp án đúng"}),e("p",{className:"mt-0.5 text-amber-700",children:Qe?"Click vào ô chữ cái hoặc icon ✓ để chọn một hoặc nhiều đáp án đúng":"Click vào ô chữ cái hoặc icon ✓ để chọn đáp án đúng"})]})]}),he.hasError("correctAnswer")&&e("div",{className:"rounded-xl border border-red-200 bg-red-50 p-4",children:e("p",{className:"text-sm text-red-600",children:he.getFieldError("correctAnswer")})})]})]}),r(g,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50",children:[e(u,{className:"px-4 py-3",children:r("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm",children:e(I,{className:"h-3.5 w-3.5 text-white"})}),r("div",{children:[r("div",{className:"flex items-center gap-2",children:[e(h,{className:"text-base font-semibold text-gray-800",children:"Giải thích"}),e("span",{className:"inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2 py-0.5 text-xs font-medium text-amber-600",children:"Không bắt buộc"})]}),e("p",{className:"text-xs text-gray-500 mt-0.5",children:"Giải thích cho đáp án đúng — hiển thị sau khi học sinh hoàn thành câu hỏi"})]})]})}),e(p,{className:"px-4 pb-4",children:e(d,{...he.register("explanation"),placeholder:"VD: Câu trả lời đúng là... vì...",rows:3,className:"min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none"})})]})]})}export function ChooseTheCorrectAnswerCreator(r){return e(Z,{...r})}
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { ImagePreview } from '../../../../components/ui/image-preview';
4
+ import { useState, useRef, useEffect, useMemo } from 'react';
5
+ import { Button } from '../../../../components/ui/button';
6
+ import { Label } from '../../../../components/ui/label';
7
+ import { Input } from '../../../../components/ui/input';
8
+ import { Textarea } from '../../../../components/ui/textarea';
9
+ import { PointsInput } from '../../../../components/ui/points-input';
10
+ import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
11
+ import { RadioGroup } from '../../../../components/ui/radio-group';
12
+ import { Switch } from '../../../../components/ui/switch';
13
+ import { usePresignedFileUrl, useDebouncedCallback } from '../../../../shared/lib/hooks';
14
+ import { useReactHookForm } from '../../../../shared/lib/hooks/useReactHookForm';
15
+ import { Pencil, Eye, Plus, Trash2, HelpCircle, CheckCircle2, Lightbulb, MessageSquare, ImageIcon, Type, Users, BookOpen, Star, ListChecks } from 'lucide-react';
16
+ import { FileUpload } from '../../../../components/ui/file-upload';
17
+ import { ChooseTheCorrectAnswerClient } from './ChooseTheCorrectAnswerClient';
18
+ import { normalizeChooseAnswerCorrectAnswer, normalizeChooseAnswerOptions, pickRawChooseAnswer, } from './normalize';
19
+ import { chooseCorrectAnswerFormSchema, } from '../../../../shared/lib/validation/choose-correct-answer.validation';
20
+ import { BasicQuestionGroupCard } from '../../groups/BasicQuestionGroupCard';
21
+ import { ArticlesGroupCard } from '../../groups/ArticlesGroupCard';
22
+ import { DocumentGroupCard } from '../../groups/DocumentGroupCard';
23
+ import { useBasicQuestionGroup } from '../../_shared/hooks/useBasicQuestionGroup';
24
+ import { z } from 'zod';
25
+ // Helper component to resolve presigned URL for option image preview (same pattern as Client's OptionImage)
26
+ function OptionImagePreview({ src, alt }) {
27
+ const { previewUrl, isLoading } = usePresignedFileUrl(src);
28
+ if (isLoading) {
29
+ return (_jsx("div", { className: "flex h-24 w-full max-w-[200px] items-center justify-center rounded-lg border border-gray-200 bg-gray-50", children: _jsx("div", { className: "h-5 w-5 animate-spin rounded-full border-2 border-gray-200 border-t-blue-500" }) }));
30
+ }
31
+ return (_jsx("div", { className: "relative h-24 w-full max-w-[200px] overflow-hidden rounded-lg border border-gray-200 bg-gray-50", children: _jsx("img", { src: previewUrl || src, alt: alt, className: "h-full w-full object-contain", onError: (e) => {
32
+ const target = e.target;
33
+ target.onerror = null;
34
+ target.src = '/images/placeholder-image.svg';
35
+ } }) }));
36
+ }
37
+ /**
38
+ * Build the form values from initialData, which can arrive in API format
39
+ * (content.question, correctAnswer.answer) or flat format.
40
+ *
41
+ * API options may be string[] or { id, text }[], and the correct answer may be
42
+ * an index or an option ID - both are normalized to string[] + index here so the
43
+ * rest of the component only ever deals with the flat form shape.
44
+ */
45
+ function buildNormalizedFormData(apiData, opts) {
46
+ const { optionCount, defaultValue, defaultQuestion, multipleAnswers } = opts;
47
+ const rawOptions = apiData?.content?.options || apiData?.options;
48
+ const options = rawOptions?.length > 0
49
+ ? normalizeChooseAnswerOptions(rawOptions)
50
+ : defaultValue && defaultValue.length > 0
51
+ ? defaultValue
52
+ : Array.from({ length: optionCount }, () => '');
53
+ const normalizedCorrectAnswer = normalizeChooseAnswerCorrectAnswer(pickRawChooseAnswer(apiData?.correctAnswer), rawOptions);
54
+ const isMultipleAnswers = Boolean(apiData?.multipleAnswers
55
+ ?? apiData?.content?.multipleAnswers
56
+ ?? apiData?.questionGroup?.payload?.multipleAnswers
57
+ ?? Array.isArray(normalizedCorrectAnswer)) || Boolean(multipleAnswers);
58
+ return {
59
+ // Support both API format (content.question) and flat format (question)
60
+ question: apiData?.content?.question || apiData?.question || defaultQuestion || '',
61
+ optionType: (apiData?.content?.optionType || apiData?.optionType || 'text'),
62
+ options,
63
+ correctAnswer: isMultipleAnswers
64
+ ? (Array.isArray(normalizedCorrectAnswer)
65
+ ? normalizedCorrectAnswer
66
+ : normalizedCorrectAnswer === -1
67
+ ? []
68
+ : [normalizedCorrectAnswer])
69
+ : (Array.isArray(normalizedCorrectAnswer)
70
+ ? (normalizedCorrectAnswer[0] ?? -1)
71
+ : normalizedCorrectAnswer),
72
+ multipleAnswers: isMultipleAnswers,
73
+ imageUrl: apiData?.content?.imageUrl || apiData?.imageUrl || '',
74
+ explanation: apiData?.explanation || '',
75
+ askerName: apiData?.content?.askerName || apiData?.askerName || '',
76
+ answererName: apiData?.content?.answererName || apiData?.answererName || '',
77
+ isExample: apiData?.isExample || false,
78
+ points: apiData?.points ?? 1,
79
+ };
80
+ }
81
+ function ChooseTheCorrectAnswerCreatorContent({ initialData, onSave, onCancel, onChange, externalErrors, optionCount = 4, optionMode, defaultValue, defaultQuestion, viewMode, onUnsavedChangesChange, validationRef, groups, questionIndexInPart = 0, partId = '', questionConfig, groupType, groupConfig, articleCount = 0, multipleAnswers, headerExtra, }) {
82
+ // Use react-hook-form with Zod validation
83
+ // initialData can be in API format (content.question, correctAnswer.answer) or flat format
84
+ // Cast to any to support both formats
85
+ const apiData = initialData;
86
+ const formSchema = useMemo(() => {
87
+ return chooseCorrectAnswerFormSchema.superRefine((data, ctx) => {
88
+ if (viewMode === 'CONVERSATION') {
89
+ if (!data.askerName?.trim()) {
90
+ ctx.addIssue({
91
+ code: z.ZodIssueCode.custom,
92
+ message: 'Vui lòng nhập tên người hỏi!',
93
+ path: ['askerName'],
94
+ });
95
+ }
96
+ if (!data.answererName?.trim()) {
97
+ ctx.addIssue({
98
+ code: z.ZodIssueCode.custom,
99
+ message: 'Vui lòng nhập tên người trả lời!',
100
+ path: ['answererName'],
101
+ });
102
+ }
103
+ }
104
+ });
105
+ }, [viewMode]);
106
+ const normalizeArgs = useMemo(() => ({ optionCount, defaultValue, defaultQuestion, multipleAnswers }), [optionCount, defaultValue, defaultQuestion, multipleAnswers]);
107
+ const form = useReactHookForm({
108
+ defaultValues: buildNormalizedFormData(apiData, normalizeArgs),
109
+ zodSchema: formSchema,
110
+ mode: 'onBlur',
111
+ });
112
+ // UI State
113
+ const [isClientMode, setIsClientMode] = useState(false);
114
+ const [imagePreviewUrl, setImagePreviewUrl] = useState('');
115
+ const [optionPreviewUrls, setOptionPreviewUrls] = useState({});
116
+ // ============ GROUP (shared title / content / image) ============
117
+ const { isGrouped, currentGroupNumber, isFirstInGroup, groupTitle, groupSubtitle, groupContent, imageUrl: groupImageUrl, articles, setGroupTitle, setGroupSubtitle, setGroupContent, setImageUrl: setGroupImageUrl, setArticles, } = useBasicQuestionGroup({
118
+ partId,
119
+ groups,
120
+ questionIndexInPart,
121
+ initialData: {
122
+ title: initialData?.groupTitle || initialData?.questionGroup?.payload?.title || initialData?.title || '',
123
+ subtitle: initialData?.groupSubtitle || initialData?.questionGroup?.payload?.subtitle || '',
124
+ content: initialData?.groupContent || initialData?.questionGroup?.payload?.content || '',
125
+ imageUrl: initialData?.groupImageUrl || initialData?.questionGroup?.payload?.imageUrl || '',
126
+ articles: initialData?.articles || initialData?.questionGroup?.payload?.articles || undefined,
127
+ },
128
+ articleCount,
129
+ });
130
+ // Cache options for each mode to preserve values when switching
131
+ const textOptionsCacheRef = useRef(null);
132
+ const imageOptionsCacheRef = useRef(null);
133
+ // Track previous initialData to detect changes
134
+ const previousInitialDataRef = useRef(initialData);
135
+ const isInitializingRef = useRef(false);
136
+ const onChangeRef = useRef(onChange);
137
+ const previousPayloadRef = useRef('');
138
+ // Update onChange ref when it changes
139
+ useEffect(() => {
140
+ onChangeRef.current = onChange;
141
+ }, [onChange]);
142
+ // Debounced onChange callback to prevent excessive re-renders
143
+ const debouncedOnChange = useDebouncedCallback((data) => {
144
+ onChangeRef.current?.(data);
145
+ }, 300);
146
+ // Watch form values - MUST be declared before useEffects that use them
147
+ const watchedQuestion = form.watch('question');
148
+ const watchedOptionType = form.watch('optionType');
149
+ const watchedOptions = form.watch('options');
150
+ const watchedCorrectAnswer = form.watch('correctAnswer');
151
+ const watchedMultipleAnswers = form.watch('multipleAnswers');
152
+ const watchedImageUrl = form.watch('imageUrl');
153
+ const watchedExplanation = form.watch('explanation');
154
+ const watchedAskerName = form.watch('askerName');
155
+ const watchedAnswererName = form.watch('answererName');
156
+ const watchedIsExample = form.watch('isExample');
157
+ const watchedPoints = form.watch('points');
158
+ // Normalize optionMode from template (handle uppercase 'IMAGE', 'TEXT')
159
+ // If optionMode is null or undefined, default to 'text' as explicitly requested
160
+ const normalizedOptionMode = (optionMode?.toLowerCase() === 'image' ? 'image' : 'text');
161
+ // Default group type to 'BASIC' when template does not set group.type
162
+ const effectiveGroupType = (groupType || 'BASIC').toUpperCase();
163
+ // Whether per-question imageUrl is part of this part's questionConfig.
164
+ // When false, per-question image input is hidden and must NOT be saved to content.imageUrl.
165
+ const includesImageUrl = !questionConfig || questionConfig.length === 0 || questionConfig.includes('imageUrl');
166
+ // Whether group-level imageUrl is part of this part's groupConfig.
167
+ // When false, DocumentGroupCard/BasicQuestionGroupCard hide the group image input and
168
+ // must NOT be saved to questionGroup.payload.imageUrl.
169
+ const includesGroupImageUrl = !groupConfig || groupConfig.length === 0 || groupConfig.includes('imageUrl');
170
+ // Force optionMode from template if provided
171
+ useEffect(() => {
172
+ if (normalizedOptionMode && watchedOptionType !== normalizedOptionMode) {
173
+ form.setValue('optionType', normalizedOptionMode);
174
+ }
175
+ }, [normalizedOptionMode, watchedOptionType, form]);
176
+ // Custom hooks for presigned URL
177
+ const { previewUrl: generatedPreviewUrl } = usePresignedFileUrl(watchedImageUrl);
178
+ const displayPreviewUrl = imagePreviewUrl || generatedPreviewUrl || '';
179
+ // NOTE: Status tracking is now handled entirely by ExamCreator via localStorage
180
+ // No need to track originalSavedData in Creator component
181
+ // NOTE: Unsaved changes tracking has been REMOVED from Creator component
182
+ // Status is now managed entirely by ExamCreator via localStorage (exam_question_status_{id})
183
+ // The onChange callback triggers handleQuestionDraftChange in ExamCreator which updates localStorage
184
+ // Expose validation function and getFormData via ref
185
+ useEffect(() => {
186
+ if (validationRef) {
187
+ validationRef.current = {
188
+ triggerValidation: async () => {
189
+ const result = await form.trigger();
190
+ return result;
191
+ },
192
+ // Return current form data immediately (not debounced)
193
+ getFormData: () => ({
194
+ question: watchedQuestion,
195
+ optionType: watchedOptionType,
196
+ options: watchedOptions,
197
+ correctAnswer: watchedCorrectAnswer,
198
+ multipleAnswers: watchedMultipleAnswers,
199
+ imageUrl: includesImageUrl ? watchedImageUrl : '',
200
+ explanation: watchedExplanation,
201
+ askerName: watchedAskerName,
202
+ answererName: watchedAnswererName,
203
+ isExample: watchedIsExample,
204
+ points: watchedPoints,
205
+ groupTitle,
206
+ groupSubtitle,
207
+ groupContent,
208
+ groupImageUrl: includesGroupImageUrl ? groupImageUrl : '',
209
+ groupNumber: currentGroupNumber,
210
+ ...(effectiveGroupType === 'ARTICLES' ? { articles } : {}),
211
+ }),
212
+ };
213
+ }
214
+ }, [validationRef, form, watchedCorrectAnswer, watchedMultipleAnswers, watchedQuestion, watchedOptionType, watchedOptions, watchedImageUrl, watchedExplanation, watchedAskerName, watchedAnswererName, watchedIsExample, watchedPoints, groupTitle, groupSubtitle, groupContent, groupImageUrl, currentGroupNumber, groupType, articles]);
215
+ // Sync state from initialData when it changes from EXTERNAL sources only
216
+ // We compare incoming initialData with current FORM values to detect true external changes
217
+ useEffect(() => {
218
+ // Cast to any to support both API format and flat format
219
+ const apiData = initialData;
220
+ // Normalize initialData for comparison - support both API and flat format
221
+ const normalizedInitialData = {
222
+ ...buildNormalizedFormData(apiData, normalizeArgs),
223
+ groupTitle: apiData?.groupTitle || apiData?.questionGroup?.payload?.title || '',
224
+ groupContent: apiData?.groupContent || apiData?.questionGroup?.payload?.content || '',
225
+ groupImageUrl: apiData?.groupImageUrl || apiData?.questionGroup?.payload?.imageUrl || '',
226
+ };
227
+ // Check if initialData actually changed from previous prop
228
+ const initialDataChanged = JSON.stringify(previousInitialDataRef.current) !== JSON.stringify(initialData);
229
+ if (!initialDataChanged) {
230
+ return; // No change, nothing to do
231
+ }
232
+ // Update ref to track latest prop value
233
+ previousInitialDataRef.current = initialData;
234
+ // Get current form data to compare
235
+ const currentFormData = {
236
+ question: watchedQuestion,
237
+ optionType: watchedOptionType,
238
+ options: watchedOptions,
239
+ correctAnswer: watchedCorrectAnswer,
240
+ multipleAnswers: watchedMultipleAnswers,
241
+ imageUrl: watchedImageUrl,
242
+ explanation: watchedExplanation,
243
+ askerName: watchedAskerName,
244
+ answererName: watchedAnswererName,
245
+ isExample: watchedIsExample,
246
+ points: watchedPoints,
247
+ };
248
+ // Check if this is truly new external data (not echoed from our own onChange)
249
+ // IMPORTANT: Must check ALL fields including points, explanation, and imageUrl
250
+ const isEchoFromOnChange = currentFormData.question === normalizedInitialData.question &&
251
+ currentFormData.optionType === normalizedInitialData.optionType &&
252
+ JSON.stringify(currentFormData.correctAnswer) === JSON.stringify(normalizedInitialData.correctAnswer) &&
253
+ currentFormData.multipleAnswers === normalizedInitialData.multipleAnswers &&
254
+ currentFormData.points === normalizedInitialData.points &&
255
+ currentFormData.explanation === normalizedInitialData.explanation &&
256
+ currentFormData.imageUrl === normalizedInitialData.imageUrl &&
257
+ JSON.stringify(currentFormData.options) === JSON.stringify(normalizedInitialData.options);
258
+ // IMPORTANT: If initialData changed and matches current form AND isCompleted is true,
259
+ // this means the question was just saved. Update originalSavedDataRef to the new baseline.
260
+ // The isCompleted check is crucial - it differentiates actual save from user typing echo.
261
+ const wasJustSaved = initialData?.isCompleted === true;
262
+ // If echo from save, just skip the reset (no need to update old refs anymore)
263
+ if (isEchoFromOnChange && wasJustSaved) {
264
+ return;
265
+ }
266
+ // Only reset if external data has different values than current form
267
+ // This prevents resetting when user just edits the question text
268
+ const shouldReset = !isEchoFromOnChange &&
269
+ (JSON.stringify(normalizedInitialData.correctAnswer) !== JSON.stringify(currentFormData.correctAnswer) ||
270
+ normalizedInitialData.multipleAnswers !== currentFormData.multipleAnswers ||
271
+ normalizedInitialData.question !== currentFormData.question ||
272
+ normalizedInitialData.imageUrl !== currentFormData.imageUrl);
273
+ const hasInitialCorrectAnswer = Array.isArray(normalizedInitialData.correctAnswer)
274
+ ? normalizedInitialData.correctAnswer.length > 0
275
+ : normalizedInitialData.correctAnswer !== -1;
276
+ if (shouldReset && hasInitialCorrectAnswer) {
277
+ isInitializingRef.current = true;
278
+ form.reset(normalizedInitialData);
279
+ setTimeout(() => {
280
+ isInitializingRef.current = false;
281
+ }, 150);
282
+ }
283
+ }, [initialData, optionCount, multipleAnswers, form, watchedQuestion, watchedOptionType, watchedOptions, watchedCorrectAnswer, watchedMultipleAnswers, watchedImageUrl, watchedExplanation, watchedAskerName, watchedAnswererName, watchedIsExample, watchedPoints, normalizeArgs]);
284
+ // Convert to payload format and call onChange
285
+ // NOTE: We need to stringify options for proper change detection since array reference doesn't change
286
+ const watchedOptionsString = JSON.stringify(watchedOptions);
287
+ useEffect(() => {
288
+ if (isInitializingRef.current) {
289
+ const currentPayload = JSON.stringify({
290
+ question: watchedQuestion,
291
+ optionType: watchedOptionType,
292
+ options: watchedOptions,
293
+ correctAnswer: watchedCorrectAnswer,
294
+ multipleAnswers: watchedMultipleAnswers,
295
+ imageUrl: watchedImageUrl,
296
+ explanation: watchedExplanation,
297
+ askerName: watchedAskerName,
298
+ answererName: watchedAnswererName,
299
+ isExample: watchedIsExample,
300
+ points: watchedPoints,
301
+ groupTitle,
302
+ groupSubtitle,
303
+ groupContent,
304
+ groupImageUrl,
305
+ groupNumber: currentGroupNumber,
306
+ ...(effectiveGroupType === 'ARTICLES' ? { articles } : {}),
307
+ });
308
+ previousPayloadRef.current = currentPayload;
309
+ return;
310
+ }
311
+ const payload = {
312
+ question: watchedQuestion,
313
+ optionType: watchedOptionType,
314
+ options: watchedOptions,
315
+ correctAnswer: watchedCorrectAnswer,
316
+ multipleAnswers: watchedMultipleAnswers,
317
+ imageUrl: includesImageUrl ? watchedImageUrl : '',
318
+ explanation: watchedExplanation,
319
+ askerName: watchedAskerName,
320
+ answererName: watchedAnswererName,
321
+ isExample: watchedIsExample,
322
+ points: watchedPoints,
323
+ groupTitle,
324
+ groupSubtitle,
325
+ groupContent,
326
+ groupImageUrl: includesGroupImageUrl ? groupImageUrl : '',
327
+ groupNumber: currentGroupNumber,
328
+ ...(effectiveGroupType === 'ARTICLES' ? { articles } : {}),
329
+ };
330
+ const payloadString = JSON.stringify(payload);
331
+ if (previousPayloadRef.current !== payloadString) {
332
+ previousPayloadRef.current = payloadString;
333
+ debouncedOnChange(payload);
334
+ }
335
+ }, [
336
+ watchedQuestion,
337
+ watchedOptionType,
338
+ watchedOptionsString, // Use stringified version for proper change detection
339
+ watchedCorrectAnswer,
340
+ watchedMultipleAnswers,
341
+ watchedImageUrl,
342
+ watchedExplanation,
343
+ watchedAskerName,
344
+ watchedAnswererName,
345
+ watchedIsExample,
346
+ watchedPoints,
347
+ groupTitle,
348
+ groupSubtitle,
349
+ groupContent,
350
+ groupImageUrl,
351
+ currentGroupNumber,
352
+ groupType,
353
+ articles,
354
+ ]);
355
+ // Handle save with validation
356
+ const handleSave = form.handleSubmit((data) => {
357
+ if (data.multipleAnswers) {
358
+ const correctAnswers = Array.isArray(data.correctAnswer)
359
+ ? data.correctAnswer
360
+ : data.correctAnswer === -1
361
+ ? []
362
+ : [data.correctAnswer];
363
+ const hasInvalidAnswer = correctAnswers.some((answer) => answer < 0 || answer >= data.options.length);
364
+ if (hasInvalidAnswer) {
365
+ form.setError('correctAnswer', { message: 'Đáp án đúng không hợp lệ!' });
366
+ return;
367
+ }
368
+ }
369
+ else {
370
+ // Validate correctAnswer is within bounds (but allow -1 = not selected)
371
+ const correctAnswer = typeof data.correctAnswer === 'number' ? data.correctAnswer : -1;
372
+ if (correctAnswer !== -1 && correctAnswer >= data.options.length) {
373
+ form.setError('correctAnswer', { message: 'Đáp án đúng không hợp lệ!' });
374
+ return;
375
+ }
376
+ }
377
+ // NOTE: Save status is now managed by ExamCreator via localStorage
378
+ // No need to update old tracking refs here
379
+ // Clear cached options for the non-active mode after saving
380
+ textOptionsCacheRef.current = null;
381
+ imageOptionsCacheRef.current = null;
382
+ onSave?.(data);
383
+ // Note: Toast is shown by parent ExamCreator with question number
384
+ });
385
+ // Client mode preview
386
+ if (isClientMode) {
387
+ const questionData = {
388
+ question: watchedQuestion || '',
389
+ optionType: watchedOptionType,
390
+ options: watchedOptions,
391
+ correctAnswer: watchedCorrectAnswer,
392
+ multipleAnswers: watchedMultipleAnswers,
393
+ imageUrl: displayPreviewUrl || watchedImageUrl,
394
+ explanation: watchedExplanation,
395
+ askerName: watchedAskerName,
396
+ answererName: watchedAnswererName,
397
+ isExample: watchedIsExample,
398
+ isGroup: isGrouped,
399
+ groupTitle,
400
+ groupSubtitle,
401
+ groupContent,
402
+ groupImageUrl,
403
+ groupNumber: currentGroupNumber,
404
+ ...(effectiveGroupType === 'ARTICLES' ? { articles } : {}),
405
+ };
406
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Choose the correct answer (Preview)" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Ch\u1EBF \u0111\u1ED9 xem tr\u01B0\u1EDBc - Hi\u1EC3n th\u1ECB \u0111\u00E1p \u00E1n \u0111\u00FAng" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(false), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), "Quay l\u1EA1i Edit Mode"] })] }) }) }), _jsx(ChooseTheCorrectAnswerClient, { questionData: questionData, questionConfig: questionConfig, isReviewMode: true, userAnswer: watchedMultipleAnswers
407
+ ? watchedCorrectAnswer
408
+ : watchedCorrectAnswer !== -1
409
+ ? watchedCorrectAnswer
410
+ : undefined })] }));
411
+ }
412
+ // Creator UI
413
+ return (_jsxs("div", { className: "space-y-4", children: [isGrouped && effectiveGroupType === 'ARTICLES' && (_jsx(ArticlesGroupCard, { groupNumber: currentGroupNumber, articleCount: articleCount, articles: articles, onArticlesChange: setArticles, title: groupTitle, onTitleChange: setGroupTitle, isReadOnly: !isFirstInGroup, uploadIdPrefix: `choose-correct-answer-${partId ?? 'articles'}` })), isGrouped && effectiveGroupType === 'BASIC' && (_jsx(BasicQuestionGroupCard, { groupNumber: currentGroupNumber, title: groupTitle, subtitle: groupSubtitle, content: groupContent, imageUrl: groupImageUrl, onTitleChange: setGroupTitle, onSubtitleChange: setGroupSubtitle, onContentChange: setGroupContent, onImageChange: setGroupImageUrl, groupConfig: groupType ? groupConfig : undefined, isReadOnly: !isFirstInGroup, uploadIdPrefix: `choose-correct-answer-${partId ?? 'group'}` })), isGrouped && effectiveGroupType === 'DOCUMENT' && (_jsx(DocumentGroupCard, { groupNumber: currentGroupNumber, title: groupTitle, subtitle: groupSubtitle, content: groupContent, imageUrl: groupImageUrl, onTitleChange: setGroupTitle, onSubtitleChange: setGroupSubtitle, onContentChange: setGroupContent, onImageChange: setGroupImageUrl, groupConfig: groupType ? groupConfig : undefined, isReadOnly: !isFirstInGroup, uploadIdPrefix: `choose-correct-answer-${partId ?? 'document'}` })), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50", children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gray-100", children: _jsx(HelpCircle, { className: "h-4 w-4 text-gray-600" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-base font-bold text-gray-900", children: "Ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng" }), _jsx("p", { className: "text-xs text-gray-500", children: "T\u1EA1o c\u00E2u h\u1ECFi tr\u1EAFc nghi\u1EC7m v\u1EDBi nhi\u1EC1u l\u1EF1a ch\u1ECDn" })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [headerExtra, _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: watchedOptions.filter((o) => typeof o === 'string' && o.trim()).length < 2, className: "gap-2 border-indigo-200 text-indigo-600 hover:bg-indigo-50 hover:text-indigo-700", children: [_jsx(Eye, { className: "h-4 w-4" }), "Xem tr\u01B0\u1EDBc"] })] })] }) }), _jsxs(CardContent, { className: "space-y-3 px-4 pb-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-y-2", children: [_jsx(PointsInput, { ...form.register('points', { valueAsNumber: true }), error: form.hasError('points') ? form.getFieldError('points') : undefined }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("button", { type: "button", style: { paddingBlock: 'calc(var(--spacing) * 1)' }, onClick: () => form.setValue('isExample', !watchedIsExample), className: `inline-flex items-center gap-2 rounded-md border px-3.5 text-sm font-medium transition-colors select-none ${watchedIsExample
414
+ ? 'border-amber-300 bg-amber-50 text-amber-700'
415
+ : 'border-gray-200 bg-white text-gray-400 hover:text-gray-600'}`, children: [_jsx(Star, { className: `h-4 w-4 ${watchedIsExample ? 'fill-amber-400 text-amber-400' : ''}` }), "Example"] }), _jsxs("button", { type: "button", style: { paddingBlock: 'calc(var(--spacing) * 1)' }, disabled: Boolean(multipleAnswers), onClick: () => {
416
+ const enabled = !watchedMultipleAnswers;
417
+ const currentValue = form.getValues('correctAnswer');
418
+ const normalized = Array.isArray(currentValue)
419
+ ? currentValue
420
+ : currentValue === -1 ? [] : [currentValue];
421
+ form.setValue('multipleAnswers', enabled, { shouldValidate: true });
422
+ form.setValue('correctAnswer', enabled ? normalized : (normalized[0] ?? -1), { shouldValidate: true });
423
+ }, className: `inline-flex items-center gap-2 rounded-md border px-3.5 text-sm font-medium transition-colors select-none ${watchedMultipleAnswers
424
+ ? 'border-violet-300 bg-violet-50 text-violet-700'
425
+ : 'border-gray-200 bg-white text-gray-400 hover:text-gray-600'} ${Boolean(multipleAnswers) ? 'cursor-not-allowed opacity-50' : ''}`, children: [_jsx(ListChecks, { className: "h-4 w-4" }), "Nhi\u1EC1u \u0111\u00E1p \u00E1n"] })] })] }), viewMode === 'CONVERSATION' && (_jsxs("div", { className: "space-y-3", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(Users, { className: "h-4 w-4 text-indigo-500" }), "H\u1ED9i tho\u1EA1i ", _jsx("span", { className: "text-sm font-normal text-gray-500", children: "(tu\u1EF3 ch\u1ECDn - d\u00F9ng cho c\u00E2u h\u1ECFi d\u1EA1ng h\u1ED9i tho\u1EA1i)" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4 rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { htmlFor: "askerName", className: "flex items-center gap-1 text-sm font-medium text-gray-600", children: ["T\u00EAn ng\u01B0\u1EDDi h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "askerName", ...form.register('askerName'), placeholder: "VD: John, Mary, Teacher...", className: `border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${form.hasError('askerName') ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), form.hasError('askerName') && (_jsx("p", { className: "text-sm text-red-600", children: form.getFieldError('askerName') }))] }), _jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { htmlFor: "answererName", className: "flex items-center gap-1 text-sm font-medium text-gray-600", children: ["T\u00EAn ng\u01B0\u1EDDi tr\u1EA3 l\u1EDDi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "answererName", ...form.register('answererName'), placeholder: "VD: Tom, Anna, Student...", className: `border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${form.hasError('answererName') ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), form.hasError('answererName') && (_jsx("p", { className: "text-sm text-red-600", children: form.getFieldError('answererName') }))] })] })] })), (!questionConfig || questionConfig.length === 0 || questionConfig.includes('question')) && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "question", className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-4 w-4 text-indigo-500" }), _jsx("span", { className: "flex items-center gap-1", children: "C\u00E2u h\u1ECFi" }), _jsx("span", { className: "text-sm font-normal text-gray-500", children: "(tu\u1EF3 ch\u1ECDn)" })] }), _jsx(Textarea, { id: "question", ...form.register('question'), placeholder: "Nh\u1EADp c\u00E2u h\u1ECFi...", className: `min-h-[100px] border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${form.hasError('question') ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}`, rows: 3 }), form.hasError('question') && (_jsx("p", { className: "text-sm text-red-600", children: form.getFieldError('question') }))] })), (!questionConfig || questionConfig.length === 0 || questionConfig.includes('imageUrl') || questionConfig.includes('imageUrl ')) && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-indigo-500" }), "H\u00ECnh \u1EA3nh ", _jsx("span", { className: "text-sm font-normal text-gray-500" })] }), _jsx(FileUpload, { id: "question-image", label: "", accept: "image/*", value: watchedImageUrl || '', onChange: (url) => {
426
+ form.setValue('imageUrl', url, { shouldValidate: true });
427
+ }, onPresignedUrlChange: setImagePreviewUrl, maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" }), form.hasError('imageUrl') && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: form.getFieldError('imageUrl') })), displayPreviewUrl && (_jsx(ImagePreview, { src: displayPreviewUrl, alt: "Preview", className: "mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200" }))] })), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100", children: _jsx("span", { className: "text-lg", children: "\u274C" }) }), _jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("ul", { className: "mt-1 list-inside list-disc text-red-700", children: externalErrors.map((error, index) => (_jsx("li", { children: error }, index))) })] })] })), form.formState.errors.options?.message && (_jsx("div", { className: "rounded-xl border border-red-200 bg-red-50 p-4", children: _jsx("p", { className: "text-sm text-red-600", children: form.formState.errors.options.message }) }))] })] }), (!questionConfig || questionConfig.length === 0 || questionConfig.includes('options')) && (_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-indigo-100/50", children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100", children: _jsx(Lightbulb, { className: "h-3.5 w-3.5 text-indigo-600" }) }), _jsxs(CardTitle, { className: "text-base font-semibold text-gray-800", children: ["C\u00E1c l\u1EF1a ch\u1ECDn ", _jsxs("span", { className: "text-xs font-normal text-gray-500", children: ["(", watchedOptions.length, " \u0111\u00E1p \u00E1n)"] })] })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-3 py-1.5", children: [_jsx(Type, { className: `h-3.5 w-3.5 ${watchedOptionType === 'text' ? 'text-indigo-600' : 'text-gray-400'}` }), _jsx("span", { className: `text-xs font-medium ${watchedOptionType === 'text' ? 'text-indigo-600' : 'text-gray-400'}`, children: "Text" }), _jsx(Switch, { checked: watchedOptionType === 'image', disabled: true, className: watchedOptionType === 'image' ? '!bg-indigo-600 !opacity-100' : '!bg-indigo-600 !opacity-100', onCheckedChange: (checked) => {
428
+ const newType = checked ? 'image' : 'text';
429
+ const currentOptions = form.getValues('options');
430
+ const currentCorrectAnswer = form.getValues('correctAnswer');
431
+ // Save current mode's options to cache
432
+ if (watchedOptionType === 'text') {
433
+ textOptionsCacheRef.current = { options: currentOptions, correctAnswer: currentCorrectAnswer };
434
+ }
435
+ else {
436
+ imageOptionsCacheRef.current = { options: currentOptions, correctAnswer: currentCorrectAnswer };
437
+ }
438
+ // Restore cached values for the target mode, or use empty defaults
439
+ const targetCache = newType === 'text' ? textOptionsCacheRef.current : imageOptionsCacheRef.current;
440
+ if (targetCache) {
441
+ form.setValue('options', targetCache.options);
442
+ form.setValue('correctAnswer', targetCache.correctAnswer);
443
+ }
444
+ else {
445
+ const emptyOptions = Array.from({ length: currentOptions.length }, () => '');
446
+ form.setValue('options', emptyOptions);
447
+ form.setValue('correctAnswer', -1);
448
+ }
449
+ form.setValue('optionType', newType);
450
+ // Clear all validation errors from previous mode
451
+ form.clearErrors();
452
+ setOptionPreviewUrls({});
453
+ } }), _jsx(ImageIcon, { className: `h-3.5 w-3.5 ${watchedOptionType === 'image' ? 'text-indigo-600' : 'text-gray-400'}` }), _jsx("span", { className: `text-xs font-medium ${watchedOptionType === 'image' ? 'text-indigo-600' : 'text-gray-400'}`, children: "\u1EA2nh" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => {
454
+ const currentOptions = form.getValues('options');
455
+ form.setValue('options', [...currentOptions, '']);
456
+ }, className: "gap-2 border-green-200 text-green-600 hover:bg-green-50 hover:text-green-700", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm \u0111\u00E1p \u00E1n"] })] })] }) }), _jsxs(CardContent, { className: "space-y-3 px-4 pb-4", children: [watchedMultipleAnswers ? (_jsx("div", { className: "space-y-3", children: watchedOptions.map((option, index) => {
457
+ const optionError = form.formState.errors.options?.[index];
458
+ const selectedAnswers = Array.isArray(watchedCorrectAnswer)
459
+ ? watchedCorrectAnswer
460
+ : watchedCorrectAnswer === -1
461
+ ? []
462
+ : [watchedCorrectAnswer];
463
+ const isCorrect = selectedAnswers.includes(index);
464
+ const optionLetter = String.fromCharCode(65 + index);
465
+ return (_jsx("div", { className: `group relative rounded-xl border-2 p-4 transition-all duration-200 ${isCorrect
466
+ ? 'border-green-300 bg-gradient-to-r from-green-50 to-emerald-50 shadow-md shadow-green-100'
467
+ : 'border-gray-200 bg-white hover:border-gray-300 hover:shadow-sm'}`, children: _jsxs("div", { className: "flex items-start gap-4", children: [_jsx("div", { className: `flex h-10 w-10 flex-shrink-0 cursor-pointer items-center justify-center rounded-xl font-bold transition-all ${isCorrect
468
+ ? 'bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md'
469
+ : 'bg-gray-100 text-gray-600 hover:bg-indigo-100 hover:text-indigo-600'}`, onClick: () => {
470
+ if (watchedMultipleAnswers) {
471
+ const currentAnswers = Array.isArray(form.getValues('correctAnswer'))
472
+ ? form.getValues('correctAnswer')
473
+ : [];
474
+ const nextAnswers = currentAnswers.includes(index)
475
+ ? currentAnswers.filter((answer) => answer !== index)
476
+ : [...currentAnswers, index].sort((a, b) => a - b);
477
+ form.setValue('correctAnswer', nextAnswers, { shouldValidate: true });
478
+ return;
479
+ }
480
+ form.setValue('correctAnswer', index, { shouldValidate: true });
481
+ }, children: optionLetter }), _jsxs("div", { className: "flex-1 space-y-2", children: [watchedOptionType === 'image' ? (
482
+ /* Image Upload Mode */
483
+ _jsxs("div", { className: "space-y-2", children: [_jsx(FileUpload, { id: `option-image-${index}`, label: "", accept: "image/*", value: option || '', onChange: (url) => {
484
+ const newOptions = [...watchedOptions];
485
+ newOptions[index] = url;
486
+ form.setValue('options', newOptions, { shouldValidate: true });
487
+ }, onPresignedUrlChange: (presignedUrl) => {
488
+ setOptionPreviewUrls((prev) => ({ ...prev, [index]: presignedUrl }));
489
+ }, maxSize: 5, placeholder: `Upload ảnh đáp án ${optionLetter}...`, autoUpload: true, prefix: "questions/options" }), (optionPreviewUrls[index] || option) && (_jsx(OptionImagePreview, { src: optionPreviewUrls[index] || option, alt: `Đáp án ${optionLetter}` }))] })) : (
490
+ /* Text Input Mode */
491
+ _jsx(Input, { id: `option-input-${index}`, ...form.register(`options.${index}`), placeholder: `Nhập đáp án ${optionLetter}...`, className: `border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${optionError ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` })), optionError && (_jsx("p", { className: "text-sm text-red-600", children: optionError.message })), isCorrect && (_jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-full bg-green-100 px-3 py-1 text-xs font-semibold text-green-700", children: [_jsx(CheckCircle2, { className: "h-3.5 w-3.5" }), watchedMultipleAnswers ? 'Đáp án đúng đã chọn' : 'Đáp án đúng'] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => {
492
+ if (watchedMultipleAnswers) {
493
+ const currentAnswers = Array.isArray(form.getValues('correctAnswer'))
494
+ ? form.getValues('correctAnswer')
495
+ : [];
496
+ const nextAnswers = currentAnswers.includes(index)
497
+ ? currentAnswers.filter((answer) => answer !== index)
498
+ : [...currentAnswers, index].sort((a, b) => a - b);
499
+ form.setValue('correctAnswer', nextAnswers, { shouldValidate: true });
500
+ return;
501
+ }
502
+ form.setValue('correctAnswer', index, { shouldValidate: true });
503
+ }, className: isCorrect ? 'text-green-600 hover:text-green-700' : 'text-gray-400 hover:text-green-600', title: watchedMultipleAnswers
504
+ ? (isCorrect ? 'Bỏ chọn đáp án đúng' : 'Chọn thêm đáp án đúng')
505
+ : 'Chọn làm đáp án đúng', children: _jsx(CheckCircle2, { className: "h-4 w-4" }) }), watchedOptions.length > 2 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => {
506
+ const currentOptions = form.getValues('options');
507
+ const newOptions = currentOptions.filter((_, i) => i !== index);
508
+ form.setValue('options', newOptions);
509
+ // Adjust correctAnswer if needed
510
+ const currentCorrect = form.getValues('correctAnswer');
511
+ if (Array.isArray(currentCorrect)) {
512
+ const nextCorrect = currentCorrect
513
+ .filter((answer) => answer !== index)
514
+ .map((answer) => (answer > index ? answer - 1 : answer));
515
+ form.setValue('correctAnswer', nextCorrect, { shouldValidate: true });
516
+ }
517
+ else if (currentCorrect === index) {
518
+ form.setValue('correctAnswer', -1, { shouldValidate: true });
519
+ }
520
+ else if (typeof currentCorrect === 'number' && currentCorrect > index) {
521
+ form.setValue('correctAnswer', currentCorrect - 1, { shouldValidate: true });
522
+ }
523
+ }, className: "text-red-500 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] })] }) }, index));
524
+ }) })) : (_jsx(RadioGroup, { value: watchedCorrectAnswer === -1 ? '' : watchedCorrectAnswer.toString(), onValueChange: (value) => {
525
+ form.setValue('correctAnswer', Number(value), { shouldValidate: true });
526
+ }, children: watchedOptions.map((option, index) => {
527
+ const optionError = form.formState.errors.options?.[index];
528
+ const isCorrect = watchedCorrectAnswer === index;
529
+ const optionLetter = String.fromCharCode(65 + index);
530
+ return (_jsx("div", { className: `group relative rounded-xl border-2 p-4 transition-all duration-200 ${isCorrect
531
+ ? 'border-green-300 bg-gradient-to-r from-green-50 to-emerald-50 shadow-md shadow-green-100'
532
+ : 'border-gray-200 bg-white hover:border-gray-300 hover:shadow-sm'}`, children: _jsxs("div", { className: "flex items-start gap-4", children: [_jsx("div", { className: `flex h-10 w-10 flex-shrink-0 cursor-pointer items-center justify-center rounded-xl font-bold transition-all ${isCorrect
533
+ ? 'bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md'
534
+ : 'bg-gray-100 text-gray-600 hover:bg-indigo-100 hover:text-indigo-600'}`, onClick: () => {
535
+ form.setValue('correctAnswer', index, { shouldValidate: true });
536
+ }, children: optionLetter }), _jsxs("div", { className: "flex-1 space-y-2", children: [watchedOptionType === 'image' ? (
537
+ /* Image Upload Mode */
538
+ _jsxs("div", { className: "space-y-2", children: [_jsx(FileUpload, { id: `option-image-${index}`, label: "", accept: "image/*", value: option || '', onChange: (url) => {
539
+ const newOptions = [...watchedOptions];
540
+ newOptions[index] = url;
541
+ form.setValue('options', newOptions, { shouldValidate: true });
542
+ }, onPresignedUrlChange: (presignedUrl) => {
543
+ setOptionPreviewUrls((prev) => ({ ...prev, [index]: presignedUrl }));
544
+ }, maxSize: 5, placeholder: `Upload ảnh đáp án ${optionLetter}...`, autoUpload: true, prefix: "questions/options" }), (optionPreviewUrls[index] || option) && (_jsx(OptionImagePreview, { src: optionPreviewUrls[index] || option, alt: `Đáp án ${optionLetter}` }))] })) : (
545
+ /* Text Input Mode */
546
+ _jsx(Input, { id: `option-input-${index}`, ...form.register(`options.${index}`), placeholder: `Nhập đáp án ${optionLetter}...`, className: `border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${optionError ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` })), optionError && (_jsx("p", { className: "text-sm text-red-600", children: optionError.message })), isCorrect && (_jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-full bg-green-100 px-3 py-1 text-xs font-semibold text-green-700", children: [_jsx(CheckCircle2, { className: "h-3.5 w-3.5" }), "\u0110\u00E1p \u00E1n \u0111\u00FAng"] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => {
547
+ form.setValue('correctAnswer', index, { shouldValidate: true });
548
+ }, className: isCorrect ? 'text-green-600 hover:text-green-700' : 'text-gray-400 hover:text-green-600', title: "Ch\u1ECDn l\u00E0m \u0111\u00E1p \u00E1n \u0111\u00FAng", children: _jsx(CheckCircle2, { className: "h-4 w-4" }) }), watchedOptions.length > 2 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => {
549
+ const currentOptions = form.getValues('options');
550
+ const newOptions = currentOptions.filter((_, i) => i !== index);
551
+ form.setValue('options', newOptions);
552
+ // Adjust correctAnswer if needed
553
+ const currentCorrect = form.getValues('correctAnswer');
554
+ if (typeof currentCorrect === 'number' && currentCorrect === index) {
555
+ form.setValue('correctAnswer', -1, { shouldValidate: true });
556
+ }
557
+ else if (typeof currentCorrect === 'number' && currentCorrect > index) {
558
+ form.setValue('correctAnswer', currentCorrect - 1, { shouldValidate: true });
559
+ }
560
+ }, className: "text-red-500 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] })] }) }, index));
561
+ }) })), ((Array.isArray(watchedCorrectAnswer) && watchedCorrectAnswer.length === 0)
562
+ || (!Array.isArray(watchedCorrectAnswer) && watchedCorrectAnswer === -1)) && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-amber-100", children: _jsx("span", { className: "text-lg", children: "\u26A0\uFE0F" }) }), _jsxs("div", { className: "text-sm text-amber-800", children: [_jsx("p", { className: "font-semibold", children: "Ch\u01B0a ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng" }), _jsx("p", { className: "mt-0.5 text-amber-700", children: watchedMultipleAnswers
563
+ ? 'Click vào ô chữ cái hoặc icon ✓ để chọn một hoặc nhiều đáp án đúng'
564
+ : 'Click vào ô chữ cái hoặc icon ✓ để chọn đáp án đúng' })] })] })), form.hasError('correctAnswer') && (_jsx("div", { className: "rounded-xl border border-red-200 bg-red-50 p-4", children: _jsx("p", { className: "text-sm text-red-600", children: form.getFieldError('correctAnswer') }) }))] })] })), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50", children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm", children: _jsx(BookOpen, { className: "h-3.5 w-3.5 text-white" }) }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-base font-semibold text-gray-800", children: "Gi\u1EA3i th\u00EDch" }), _jsx("span", { className: "inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2 py-0.5 text-xs font-medium text-amber-600", children: "Kh\u00F4ng b\u1EAFt bu\u1ED9c" })] }), _jsx("p", { className: "text-xs text-gray-500 mt-0.5", children: "Gi\u1EA3i th\u00EDch cho \u0111\u00E1p \u00E1n \u0111\u00FAng \u2014 hi\u1EC3n th\u1ECB sau khi h\u1ECDc sinh ho\u00E0n th\u00E0nh c\u00E2u h\u1ECFi" })] })] }) }), _jsx(CardContent, { className: "px-4 pb-4", children: _jsx(Textarea, { ...form.register('explanation'), placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0... v\u00EC...", rows: 3, className: "min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none" }) })] })] }));
565
+ }
566
+ export function ChooseTheCorrectAnswerCreator(props) {
567
+ return _jsx(ChooseTheCorrectAnswerCreatorContent, { ...props });
568
+ }