@tinyweb_dev/oe-exam-sdk 0.1.1 → 0.1.3

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 (514) hide show
  1. package/dist/api/exam-create.js +1 -179
  2. package/dist/api/exam-create.types.js +1 -1
  3. package/dist/api/exam-questions.d.ts +1 -1
  4. package/dist/api/exam-questions.js +1 -98
  5. package/dist/api/exam-questions.types.d.ts +45 -1
  6. package/dist/api/exam-questions.types.js +1 -1
  7. package/dist/api/exam-taking.js +1 -73
  8. package/dist/api/exam-taking.types.js +1 -1
  9. package/dist/api/index.js +1 -6
  10. package/dist/api/result-review.js +1 -50
  11. package/dist/api/result-review.types.js +1 -1
  12. package/dist/components/CueCardEditor.js +1 -58
  13. package/dist/components/common/ResolvedImage.js +1 -15
  14. package/dist/components/exams/CompletionDialog.d.ts +8 -0
  15. package/dist/components/exams/CompletionDialog.js +1 -0
  16. package/dist/components/exams/CreateExamInfoForm.d.ts +4 -1
  17. package/dist/components/exams/CreateExamInfoForm.js +1 -35
  18. package/dist/components/exams/CreateExamPageContainer.d.ts +3 -0
  19. package/dist/components/exams/CreateExamPageContainer.js +1 -103
  20. package/dist/components/exams/ExamCreator.d.ts +9 -3
  21. package/dist/components/exams/ExamCreator.js +1 -89
  22. package/dist/components/exams/ExamPartTabs.d.ts +1 -1
  23. package/dist/components/exams/ExamPartTabs.js +1 -40
  24. package/dist/components/exams/ExamPreviewDialog.d.ts +20 -0
  25. package/dist/components/exams/ExamPreviewDialog.js +1 -0
  26. package/dist/components/exams/ExamQuestionGrid.d.ts +1 -1
  27. package/dist/components/exams/ExamQuestionGrid.js +1 -18
  28. package/dist/components/exams/ExamQuestionsPageContainer.d.ts +3 -0
  29. package/dist/components/exams/ExamQuestionsPageContainer.js +1 -90
  30. package/dist/components/exams/ExamViewDialog.d.ts +24 -0
  31. package/dist/components/exams/ExamViewDialog.js +1 -0
  32. package/dist/components/exams/PartAudioUploader.js +1 -9
  33. package/dist/components/exams/TemplateSelector.js +1 -66
  34. package/dist/components/exams/UnsavedChangesDialog.d.ts +8 -0
  35. package/dist/components/exams/UnsavedChangesDialog.js +1 -0
  36. package/dist/components/exams/api.d.ts +1 -1
  37. package/dist/components/exams/api.js +1 -3
  38. package/dist/components/exams/create-exam.texts.d.ts +58 -0
  39. package/dist/components/exams/create-exam.texts.js +1 -0
  40. package/dist/components/exams/create-exam.validation.js +1 -18
  41. package/dist/components/exams/exam-question-status.d.ts +9 -0
  42. package/dist/components/exams/exam-question-status.js +1 -0
  43. package/dist/components/exams/exam-template.utils.js +1 -18
  44. package/dist/components/exams/index.d.ts +11 -1
  45. package/dist/components/exams/index.js +1 -10
  46. package/dist/components/exams/take/ExamTakingPageContainer.js +1 -153
  47. package/dist/components/exams/take/GenericQuestionRenderer.js +1 -14
  48. package/dist/components/exams/take/exam-taking.utils.js +1 -88
  49. package/dist/components/exams/take/hooks/useSpeakingRecorder.js +1 -24
  50. package/dist/components/exams/take/index.js +1 -2
  51. package/dist/components/exams/take/types.js +1 -1
  52. package/dist/components/index.js +1 -42
  53. package/dist/components/questions/_shared/config/question-types.config.js +1 -333
  54. package/dist/components/questions/_shared/constants/color-palette.js +1 -21
  55. package/dist/components/questions/_shared/hooks/useAnswerGroupQuestionGroup.js +1 -105
  56. package/dist/components/questions/_shared/hooks/useBasicQuestionGroup.js +1 -222
  57. package/dist/components/questions/_shared/hooks/useCompareImagesGroup.js +1 -140
  58. package/dist/components/questions/_shared/hooks/useImageQuestionGroup.js +1 -123
  59. package/dist/components/questions/_shared/hooks/useInfoExchangeGroup.js +1 -50
  60. package/dist/components/questions/_shared/hooks/useQuestionFormState.js +1 -54
  61. package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +1 -123
  62. package/dist/components/questions/_shared/hooks/useQuestionListGroup.js +1 -121
  63. package/dist/components/questions/_shared/hooks/useQuestionOnChange.js +1 -70
  64. package/dist/components/questions/_shared/hooks/useSceneQuestionGroup.js +1 -194
  65. package/dist/components/questions/_shared/index.js +1 -7
  66. package/dist/components/questions/_shared/types/answer-the-question.type.js +1 -1
  67. package/dist/components/questions/_shared/types/arrange-letters-into-words.type.js +1 -1
  68. package/dist/components/questions/_shared/types/choose-correct-adjective.type.js +1 -3
  69. package/dist/components/questions/_shared/types/color-region.config.js +1 -1
  70. package/dist/components/questions/_shared/types/fill-in-blank.type.js +1 -1
  71. package/dist/components/questions/_shared/types/fill-missing-words-in-grid.type.js +1 -1
  72. package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +1 -1
  73. package/dist/components/questions/_shared/types/index.js +1 -12
  74. package/dist/components/questions/_shared/types/label-the-picture.type.js +1 -1
  75. package/dist/components/questions/_shared/types/listen-and-choose-from-answer-group.type.js +1 -1
  76. package/dist/components/questions/_shared/types/listen-and-choose-objects-in-scene.type.js +1 -1
  77. package/dist/components/questions/_shared/types/listen-and-drag-objects-into-scene.type.js +1 -1
  78. package/dist/components/questions/_shared/types/listen-and-speak-answer.type.js +1 -1
  79. package/dist/components/questions/_shared/types/listen-and-speak-compare-images.type.js +1 -1
  80. package/dist/components/questions/_shared/types/listen-and-speak-image-group.type.js +1 -1
  81. package/dist/components/questions/_shared/types/listen-and-speak-info-exchange.type.js +1 -1
  82. package/dist/components/questions/_shared/types/listen-and-speak-odd-one-out.type.js +1 -3
  83. package/dist/components/questions/_shared/types/listen-and-speak-question-list.type.js +1 -1
  84. package/dist/components/questions/_shared/types/listen-and-speak-with-story-images.type.js +1 -3
  85. package/dist/components/questions/_shared/types/listen-and-tick-answer.type.js +1 -1
  86. package/dist/components/questions/_shared/types/listening-drawline.type.js +1 -1
  87. package/dist/components/questions/_shared/types/look-and-choose-answer.type.js +1 -1
  88. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +1 -1
  89. package/dist/components/questions/_shared/types/match-by-writing-answer.type.js +1 -1
  90. package/dist/components/questions/_shared/types/multiple-choice.type.js +1 -1
  91. package/dist/components/questions/_shared/types/question-group.type.js +1 -11
  92. package/dist/components/questions/_shared/types/read-and-color-objects.type.js +1 -1
  93. package/dist/components/questions/_shared/types/read-displayed-content.type.js +1 -1
  94. package/dist/components/questions/_shared/types/read-passage-and-answer-questions.type.js +1 -1
  95. package/dist/components/questions/_shared/types/speaking-describe-image.type.js +1 -1
  96. package/dist/components/questions/_shared/types/spontaneous-qa.type.js +1 -1
  97. package/dist/components/questions/_shared/types/word-fill-paragraph-with-options.type.js +1 -1
  98. package/dist/components/questions/_shared/types/word-fill-paragraph.type.js +1 -1
  99. package/dist/components/questions/_shared/types/word-fill-structured-form.type.js +1 -1
  100. package/dist/components/questions/_shared/types/word-ordering.type.js +1 -1
  101. package/dist/components/questions/_shared/types/write-a-short-letter.type.js +1 -1
  102. package/dist/components/questions/_shared/types/write-correct-verb-form.type.js +1 -3
  103. package/dist/components/questions/_shared/types/write-sentences.type.js +1 -1
  104. package/dist/components/questions/_shared/types/write-short-paragraph.type.js +1 -1
  105. package/dist/components/questions/_shared/utils/answer-builder.js +1 -79
  106. package/dist/components/questions/_shared/utils/createColorRegionRegistration.js +1 -39
  107. package/dist/components/questions/_shared/utils/id-generator.js +1 -3
  108. package/dist/components/questions/_shared/utils/question-validation.js +1 -21
  109. package/dist/components/questions/components/ColorNode.js +1 -85
  110. package/dist/components/questions/components/ColorRegionClient.js +1 -545
  111. package/dist/components/questions/components/ColorRegionCreator.js +1 -420
  112. package/dist/components/questions/components/ColorRegionNode.js +1 -59
  113. package/dist/components/questions/components/CreatorGuide.js +1 -103
  114. package/dist/components/questions/components/NameNode.js +1 -26
  115. package/dist/components/questions/components/QuestionBankOption.js +1 -46
  116. package/dist/components/questions/components/QuestionBasicInfoForm.js +1 -9
  117. package/dist/components/questions/components/QuestionCommonFields.js +1 -33
  118. package/dist/components/questions/components/QuestionSearchDropdown.js +1 -258
  119. package/dist/components/questions/components/QuestionTypeSelector.js +1 -32
  120. package/dist/components/questions/components/QuestionTypeSpecificForms.js +1 -6
  121. package/dist/components/questions/components/RegionNode.js +1 -94
  122. package/dist/components/questions/components/index.js +1 -14
  123. package/dist/components/questions/creator/QuestionCreator.js +1 -90
  124. package/dist/components/questions/creator/QuestionGroupCreator.js +1 -119
  125. package/dist/components/questions/creator/dedicated-component-router.js +1 -79
  126. package/dist/components/questions/creator/index.js +1 -3
  127. package/dist/components/questions/creator/question-type-registry.js +1 -80
  128. package/dist/components/questions/groups/ArticlesGroupCard.js +1 -121
  129. package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -50
  130. package/dist/components/questions/groups/DocumentGroupCard.js +1 -114
  131. package/dist/components/questions/groups/MatchAnswerGroupCard.js +1 -25
  132. package/dist/components/questions/index.js +1 -10
  133. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +1 -37
  134. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +1 -351
  135. package/dist/components/questions/types/answer-the-question/register.js +1 -67
  136. package/dist/components/questions/types/answer-the-question/transform.js +1 -36
  137. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +1 -141
  138. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +1 -118
  139. package/dist/components/questions/types/arrange-letters-into-words/index.js +1 -2
  140. package/dist/components/questions/types/arrange-letters-into-words/register.js +1 -4
  141. package/dist/components/questions/types/arrange-letters-into-words/transform.js +1 -24
  142. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -64
  143. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +1 -169
  144. package/dist/components/questions/types/choose-correct-adjective/index.js +1 -2
  145. package/dist/components/questions/types/choose-correct-adjective/register.js +1 -55
  146. package/dist/components/questions/types/choose-correct-adjective/transform.js +1 -16
  147. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +1 -131
  148. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +1 -581
  149. package/dist/components/questions/types/choose-the-correct-answer/index.js +1 -2
  150. package/dist/components/questions/types/choose-the-correct-answer/register.js +1 -101
  151. package/dist/components/questions/types/choose-the-correct-answer/transform.js +1 -43
  152. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +1 -275
  153. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +1 -448
  154. package/dist/components/questions/types/fill-in-blank/index.js +1 -2
  155. package/dist/components/questions/types/fill-in-blank/register.js +1 -138
  156. package/dist/components/questions/types/fill-in-blank/transform.js +1 -59
  157. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +1 -117
  158. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +1 -671
  159. package/dist/components/questions/types/fill-missing-words-in-grid/index.js +1 -2
  160. package/dist/components/questions/types/fill-missing-words-in-grid/register.js +1 -37
  161. package/dist/components/questions/types/fill-missing-words-in-grid/transform.js +1 -15
  162. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +1 -262
  163. package/dist/components/questions/types/gn-speaking-interview/register.js +1 -121
  164. package/dist/components/questions/types/gn-speaking-interview/transform.js +1 -48
  165. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingClient.js +1 -16
  166. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingCreator.js +1 -27
  167. package/dist/components/questions/types/image-object-matching/index.js +1 -2
  168. package/dist/components/questions/types/image-object-matching/register.js +1 -3
  169. package/dist/components/questions/types/image-object-matching/transform.js +1 -2
  170. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +1 -102
  171. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +1 -344
  172. package/dist/components/questions/types/label-the-picture/index.js +1 -2
  173. package/dist/components/questions/types/label-the-picture/register.js +1 -54
  174. package/dist/components/questions/types/label-the-picture/transform.js +1 -28
  175. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +1 -78
  176. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +1 -232
  177. package/dist/components/questions/types/listen-and-choose-from-answer-group/index.js +1 -2
  178. package/dist/components/questions/types/listen-and-choose-from-answer-group/register.js +1 -82
  179. package/dist/components/questions/types/listen-and-choose-from-answer-group/transform.js +1 -27
  180. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +1 -70
  181. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +1 -386
  182. package/dist/components/questions/types/listen-and-choose-objects-in-scene/index.js +1 -2
  183. package/dist/components/questions/types/listen-and-choose-objects-in-scene/register.js +1 -100
  184. package/dist/components/questions/types/listen-and-choose-objects-in-scene/transform.js +1 -33
  185. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +1 -106
  186. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +1 -457
  187. package/dist/components/questions/types/listen-and-drag-objects-into-scene/index.js +1 -2
  188. package/dist/components/questions/types/listen-and-drag-objects-into-scene/register.js +1 -110
  189. package/dist/components/questions/types/listen-and-drag-objects-into-scene/transform.js +1 -35
  190. package/dist/components/questions/types/listen-and-speak-answer/ArrowIndicator.js +1 -8
  191. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -22
  192. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +1 -291
  193. package/dist/components/questions/types/listen-and-speak-answer/index.js +1 -2
  194. package/dist/components/questions/types/listen-and-speak-answer/register.js +1 -84
  195. package/dist/components/questions/types/listen-and-speak-answer/transform.js +1 -27
  196. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -32
  197. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +1 -295
  198. package/dist/components/questions/types/listen-and-speak-compare-images/register.js +1 -79
  199. package/dist/components/questions/types/listen-and-speak-compare-images/transform.js +1 -27
  200. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -34
  201. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +1 -285
  202. package/dist/components/questions/types/listen-and-speak-image-group/register.js +1 -81
  203. package/dist/components/questions/types/listen-and-speak-image-group/transform.js +1 -26
  204. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -38
  205. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +1 -385
  206. package/dist/components/questions/types/listen-and-speak-info-exchange/register.js +1 -87
  207. package/dist/components/questions/types/listen-and-speak-info-exchange/transform.js +1 -32
  208. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -26
  209. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +1 -247
  210. package/dist/components/questions/types/listen-and-speak-odd-one-out/register.js +1 -59
  211. package/dist/components/questions/types/listen-and-speak-odd-one-out/transform.js +1 -18
  212. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -26
  213. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +1 -256
  214. package/dist/components/questions/types/listen-and-speak-question-list/register.js +1 -81
  215. package/dist/components/questions/types/listen-and-speak-question-list/transform.js +1 -26
  216. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -26
  217. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +1 -230
  218. package/dist/components/questions/types/listen-and-speak-with-story-images/register.js +1 -56
  219. package/dist/components/questions/types/listen-and-speak-with-story-images/transform.js +1 -16
  220. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +1 -72
  221. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +1 -230
  222. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +1 -1
  223. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +1 -66
  224. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +1 -42
  225. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +1 -96
  226. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +1 -238
  227. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +1 -2
  228. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +1 -76
  229. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +1 -19
  230. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +1 -134
  231. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +1 -617
  232. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +1 -2
  233. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +1 -82
  234. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +1 -29
  235. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +1 -38
  236. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +1 -359
  237. package/dist/components/questions/types/match-by-writing-answer/index.js +1 -3
  238. package/dist/components/questions/types/match-by-writing-answer/register.js +1 -58
  239. package/dist/components/questions/types/match-by-writing-answer/transform.js +1 -61
  240. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsClient.js +1 -16
  241. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsCreator.js +1 -23
  242. package/dist/components/questions/types/read-and-color-objects/index.js +1 -2
  243. package/dist/components/questions/types/read-and-color-objects/register.js +1 -3
  244. package/dist/components/questions/types/read-and-color-objects/transform.js +1 -36
  245. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +1 -125
  246. package/dist/components/questions/types/read-displayed-content/register.js +1 -64
  247. package/dist/components/questions/types/read-displayed-content/transform.js +1 -12
  248. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -64
  249. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +1 -511
  250. package/dist/components/questions/types/read-passage-and-answer-questions/index.js +1 -2
  251. package/dist/components/questions/types/read-passage-and-answer-questions/register.js +1 -58
  252. package/dist/components/questions/types/read-passage-and-answer-questions/transform.js +1 -28
  253. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +1 -373
  254. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +1 -41
  255. package/dist/components/questions/types/speaking-conversation/register.js +1 -110
  256. package/dist/components/questions/types/speaking-conversation/transform.js +1 -34
  257. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +1 -74
  258. package/dist/components/questions/types/speaking-cue-card/register.js +1 -52
  259. package/dist/components/questions/types/speaking-cue-card/transform.js +1 -12
  260. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -10
  261. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +1 -161
  262. package/dist/components/questions/types/speaking-describe-image/index.js +1 -2
  263. package/dist/components/questions/types/speaking-describe-image/register.js +1 -69
  264. package/dist/components/questions/types/speaking-describe-image/transform.js +1 -16
  265. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +1 -234
  266. package/dist/components/questions/types/spontaneous-qa/register.js +1 -93
  267. package/dist/components/questions/types/spontaneous-qa/transform.js +1 -34
  268. package/dist/components/questions/types/true-false/TrueFalseClient.js +1 -75
  269. package/dist/components/questions/types/true-false/TrueFalseCreator.js +1 -244
  270. package/dist/components/questions/types/true-false/index.js +1 -2
  271. package/dist/components/questions/types/true-false/register.js +1 -77
  272. package/dist/components/questions/types/true-false/transform.js +1 -32
  273. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +1 -233
  274. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +1 -631
  275. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphWrapper.js +1 -28
  276. package/dist/components/questions/types/word-fill-paragraph/index.js +1 -2
  277. package/dist/components/questions/types/word-fill-paragraph/register.js +1 -136
  278. package/dist/components/questions/types/word-fill-paragraph/transform.js +1 -56
  279. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -138
  280. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +1 -261
  281. package/dist/components/questions/types/word-fill-structured-form/index.js +1 -2
  282. package/dist/components/questions/types/word-fill-structured-form/register.js +1 -67
  283. package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -40
  284. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -84
  285. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +1 -192
  286. package/dist/components/questions/types/word-ordering/index.js +1 -2
  287. package/dist/components/questions/types/word-ordering/register.js +1 -46
  288. package/dist/components/questions/types/word-ordering/transform.js +1 -24
  289. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +1 -98
  290. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +1 -320
  291. package/dist/components/questions/types/write-a-short-letter/index.js +1 -2
  292. package/dist/components/questions/types/write-a-short-letter/register.js +1 -68
  293. package/dist/components/questions/types/write-a-short-letter/transform.js +1 -25
  294. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +1 -61
  295. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +1 -196
  296. package/dist/components/questions/types/write-correct-verb-form/index.js +1 -2
  297. package/dist/components/questions/types/write-correct-verb-form/register.js +1 -62
  298. package/dist/components/questions/types/write-correct-verb-form/transform.js +1 -17
  299. package/dist/components/questions/types/write-sentences/WriteSentencesClient.js +1 -22
  300. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +1 -174
  301. package/dist/components/questions/types/write-sentences/index.js +1 -2
  302. package/dist/components/questions/types/write-sentences/register.js +1 -58
  303. package/dist/components/questions/types/write-sentences/transform.js +1 -28
  304. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -96
  305. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +1 -161
  306. package/dist/components/questions/types/write-short-paragraph/index.js +1 -2
  307. package/dist/components/questions/types/write-short-paragraph/register.js +1 -4
  308. package/dist/components/questions/types/write-short-paragraph/transform.js +1 -21
  309. package/dist/components/questions/viewer/QuestionGroupViewer.js +1 -36
  310. package/dist/components/questions/viewer/QuestionViewer.js +1 -446
  311. package/dist/components/questions/viewer/index.js +1 -2
  312. package/dist/components/results/ResultReviewPageContainer.js +1 -98
  313. package/dist/components/results/ReviewQuestionRenderer.js +1 -72
  314. package/dist/components/results/index.js +1 -2
  315. package/dist/components/results/result-review.utils.js +1 -6
  316. package/dist/components/results/review-data.js +1 -150
  317. package/dist/components/results/review-renderer.utils.js +1 -97
  318. package/dist/components/results/review-types.js +1 -1
  319. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +1 -138
  320. package/dist/components/themes/cambridge-yle/CambridgeYleBottomNav.js +1 -12
  321. package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.js +1 -18
  322. package/dist/components/themes/cambridge-yle/CambridgeYleExampleText.js +1 -11
  323. package/dist/components/themes/cambridge-yle/CambridgeYleHeader.js +1 -11
  324. package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.js +1 -9
  325. package/dist/components/themes/cambridge-yle/CambridgeYleNavButton.js +1 -13
  326. package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.js +1 -8
  327. package/dist/components/themes/cambridge-yle/CambridgeYleProgressBar.js +1 -6
  328. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCard.js +1 -5
  329. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCardFullWidth.js +1 -9
  330. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.js +1 -41
  331. package/dist/components/themes/cambridge-yle/CambridgeYleTimer.js +1 -25
  332. package/dist/components/themes/cambridge-yle/index.js +1 -13
  333. package/dist/components/themes/english-certification/EcAnswerTheQuestion.js +1 -24
  334. package/dist/components/themes/english-certification/EcMatchByWritingAnswer.js +1 -88
  335. package/dist/components/themes/english-certification/EcWordFillParagraph.js +1 -55
  336. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -156
  337. package/dist/components/themes/english-certification/EcWritingTask.js +1 -34
  338. package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +1 -114
  339. package/dist/components/themes/english-certification/EnglishCertificationBrand.js +1 -11
  340. package/dist/components/themes/english-certification/EnglishCertificationExamLayout.js +1 -21
  341. package/dist/components/themes/english-certification/EnglishCertificationFlagButton.js +1 -15
  342. package/dist/components/themes/english-certification/EnglishCertificationFooterBar.js +1 -19
  343. package/dist/components/themes/english-certification/EnglishCertificationGroupedQuestion.js +1 -148
  344. package/dist/components/themes/english-certification/EnglishCertificationHeader.js +1 -31
  345. package/dist/components/themes/english-certification/EnglishCertificationNavigationPanel.js +1 -71
  346. package/dist/components/themes/english-certification/EnglishCertificationOptionSelector.js +1 -33
  347. package/dist/components/themes/english-certification/EnglishCertificationPassagePanel.js +1 -16
  348. package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.js +1 -22
  349. package/dist/components/themes/english-certification/EnglishCertificationQuestionItem.js +1 -37
  350. package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +1 -72
  351. package/dist/components/themes/english-certification/EnglishCertificationReadingSection.js +1 -90
  352. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestion.js +1 -18
  353. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js +1 -29
  354. package/dist/components/themes/english-certification/english-certification-theme.js +1 -58
  355. package/dist/components/themes/english-certification/grouped-question-utils.js +1 -218
  356. package/dist/components/themes/english-certification/index.js +1 -21
  357. package/dist/components/themes/index.js +1 -6
  358. package/dist/components/themes/summer-sky/SummerSkyAudioPlayer.js +1 -170
  359. package/dist/components/themes/summer-sky/SummerSkyLayout.js +1 -221
  360. package/dist/components/themes/summer-sky/SummerSkySubmitModal.js +1 -18
  361. package/dist/components/themes/summer-sky/SummerSkyWaitingRoom.js +1 -194
  362. package/dist/components/themes/types.js +1 -1
  363. package/dist/components/ui/alert-dialog.js +1 -26
  364. package/dist/components/ui/alert.js +1 -22
  365. package/dist/components/ui/avatar.js +1 -12
  366. package/dist/components/ui/badge.js +1 -38
  367. package/dist/components/ui/button.js +1 -35
  368. package/dist/components/ui/calendar.js +1 -76
  369. package/dist/components/ui/card.js +1 -16
  370. package/dist/components/ui/carousel.js +1 -91
  371. package/dist/components/ui/checkbox.js +1 -9
  372. package/dist/components/ui/collapsible.js +1 -13
  373. package/dist/components/ui/command.js +1 -34
  374. package/dist/components/ui/confirm-dialog.js +1 -49
  375. package/dist/components/ui/dialog.js +1 -23
  376. package/dist/components/ui/drawer.js +1 -35
  377. package/dist/components/ui/dropdown-menu.js +1 -33
  378. package/dist/components/ui/file-upload.js +1 -155
  379. package/dist/components/ui/form.js +1 -62
  380. package/dist/components/ui/hover-card.js +1 -10
  381. package/dist/components/ui/image-preview.js +1 -29
  382. package/dist/components/ui/input.js +1 -8
  383. package/dist/components/ui/label.js +1 -9
  384. package/dist/components/ui/language-switcher.js +1 -15
  385. package/dist/components/ui/multi-select.js +1 -90
  386. package/dist/components/ui/points-input.js +1 -33
  387. package/dist/components/ui/popover.js +1 -26
  388. package/dist/components/ui/progress.js +1 -16
  389. package/dist/components/ui/radio-group.js +1 -12
  390. package/dist/components/ui/scroll-area.js +1 -11
  391. package/dist/components/ui/select.js +1 -38
  392. package/dist/components/ui/separator.js +1 -8
  393. package/dist/components/ui/sheet.js +1 -40
  394. package/dist/components/ui/single-select.js +1 -76
  395. package/dist/components/ui/skeleton.js +1 -6
  396. package/dist/components/ui/switch.js +1 -8
  397. package/dist/components/ui/table.js +1 -20
  398. package/dist/components/ui/tabs.js +1 -17
  399. package/dist/components/ui/textarea.js +1 -8
  400. package/dist/components/ui/tooltip.js +1 -11
  401. package/dist/index.d.ts +3 -3
  402. package/dist/index.js +1 -19
  403. package/dist/mcp/config.js +1 -32
  404. package/dist/mcp/http-client.js +1 -48
  405. package/dist/mcp/index.js +1 -29
  406. package/dist/mcp/tools/_helpers.js +1 -25
  407. package/dist/mcp/tools/exams.js +1 -439
  408. package/dist/mcp/tools/index.js +1 -6
  409. package/dist/mcp/tools/results.js +1 -19
  410. package/dist/shared/constants/ApiConstant.js +1 -276
  411. package/dist/shared/constants/QueryKeyConstant.js +1 -112
  412. package/dist/shared/constants/exam-level.enum.js +1 -41
  413. package/dist/shared/constants/landings/placement-test.constants.js +1 -431
  414. package/dist/shared/constants/question-skills.js +1 -267
  415. package/dist/shared/constants/routes.js +1 -81
  416. package/dist/shared/index.d.ts +3 -0
  417. package/dist/shared/index.js +1 -7
  418. package/dist/shared/lib/hooks/index.js +1 -10
  419. package/dist/shared/lib/hooks/useAlertDialog.js +1 -57
  420. package/dist/shared/lib/hooks/useAudioPlayCounter.js +1 -30
  421. package/dist/shared/lib/hooks/useAudioPreview.js +1 -81
  422. package/dist/shared/lib/hooks/useAudioUpload.js +1 -24
  423. package/dist/shared/lib/hooks/useConfirmDialog.js +1 -89
  424. package/dist/shared/lib/hooks/useDebouncedCallback.js +1 -32
  425. package/dist/shared/lib/hooks/useDelayedAutoPlay.js +1 -12
  426. package/dist/shared/lib/hooks/useFileUpload.js +1 -24
  427. package/dist/shared/lib/hooks/useImageUrl.js +1 -5
  428. package/dist/shared/lib/hooks/usePresignedFileUrl.js +1 -10
  429. package/dist/shared/lib/hooks/useReactHookForm.js +1 -31
  430. package/dist/shared/lib/hooks/useToast.js +1 -62
  431. package/dist/shared/lib/i18n/index.js +1 -54
  432. package/dist/shared/lib/i18n/messages/en/admin.js +1 -2104
  433. package/dist/shared/lib/i18n/messages/en/auth.js +1 -86
  434. package/dist/shared/lib/i18n/messages/en/common.js +1 -140
  435. package/dist/shared/lib/i18n/messages/en/file-upload.js +1 -18
  436. package/dist/shared/lib/i18n/messages/en/student.js +1 -133
  437. package/dist/shared/lib/i18n/messages/en/terms.js +1 -37
  438. package/dist/shared/lib/i18n/messages/en.js +1 -15
  439. package/dist/shared/lib/i18n/messages/vi/admin.js +1 -2104
  440. package/dist/shared/lib/i18n/messages/vi/auth.js +1 -86
  441. package/dist/shared/lib/i18n/messages/vi/common.js +1 -140
  442. package/dist/shared/lib/i18n/messages/vi/file-upload.js +1 -18
  443. package/dist/shared/lib/i18n/messages/vi/student.js +1 -133
  444. package/dist/shared/lib/i18n/messages/vi/terms.js +1 -37
  445. package/dist/shared/lib/i18n/messages/vi.js +1 -15
  446. package/dist/shared/lib/i18n/types.js +1 -1
  447. package/dist/shared/lib/stores/examQuestionStore.js +1 -212
  448. package/dist/shared/lib/stores/localeStore.js +1 -10
  449. package/dist/shared/lib/utils/public-page.js +1 -44
  450. package/dist/shared/lib/utils/question-reverse-transform.d.ts +6 -0
  451. package/dist/shared/lib/utils/question-reverse-transform.js +1 -0
  452. package/dist/shared/lib/utils/question-transform-types.js +1 -1
  453. package/dist/shared/lib/utils/question-transform-validation.d.ts +6 -0
  454. package/dist/shared/lib/utils/question-transform-validation.js +1 -0
  455. package/dist/shared/lib/utils/question-transform.d.ts +6 -2
  456. package/dist/shared/lib/utils/question-transform.js +1 -174
  457. package/dist/shared/lib/utils.js +1 -168
  458. package/dist/shared/lib/validation/choose-correct-answer.validation.js +1 -111
  459. package/dist/shared/lib/validation/label-the-picture.validation.js +1 -19
  460. package/dist/shared/lib/validation/listen-and-choose-from-answer-group.validation.js +1 -46
  461. package/dist/shared/lib/validation/listen-and-choose-objects-in-scene.validation.js +1 -21
  462. package/dist/shared/lib/validation/listen-and-drag-objects-into-scene.validation.js +1 -34
  463. package/dist/shared/lib/validation/listen-and-speak-answer.validation.js +1 -40
  464. package/dist/shared/lib/validation/listen-and-speak-compare-images.validation.js +1 -45
  465. package/dist/shared/lib/validation/listen-and-speak-image-group.validation.js +1 -19
  466. package/dist/shared/lib/validation/listen-and-speak-info-exchange.validation.js +1 -39
  467. package/dist/shared/lib/validation/listen-and-speak-odd-one-out.validation.js +1 -32
  468. package/dist/shared/lib/validation/listen-and-speak-question-list.validation.js +1 -19
  469. package/dist/shared/lib/validation/listen-and-speak-with-story-images.validation.js +1 -28
  470. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +1 -12
  471. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +1 -47
  472. package/dist/shared/lib/validation/read-and-color-objects.validation.js +1 -29
  473. package/dist/shared/lib/validation/read-displayed-content.validation.js +1 -28
  474. package/dist/shared/lib/validation/speaking-conversation.validation.js +1 -75
  475. package/dist/shared/lib/validation/speaking-describe-image.validation.js +1 -31
  476. package/dist/shared/lib/validation/spontaneous-qa.validation.js +1 -48
  477. package/dist/shared/lib/validation/word-fill-paragraph-default.validation.js +1 -38
  478. package/dist/shared/lib/validation/word-fill-paragraph-with-options.validation.js +1 -39
  479. package/dist/shared/lib/validation/word-fill-paragraph.validation.js +1 -43
  480. package/dist/shared/lib/validation/word-ordering.validation.js +1 -33
  481. package/dist/shared/types/branch.types.js +1 -6
  482. package/dist/shared/types/common.types.js +1 -50
  483. package/dist/shared/types/entities/exam-schedule.types.js +1 -5
  484. package/dist/shared/types/entities/exam.types.js +1 -1
  485. package/dist/shared/types/entities/question.types.js +1 -4
  486. package/dist/shared/types/entities/result.types.js +1 -4
  487. package/dist/shared/types/entities/user.types.js +1 -4
  488. package/dist/shared/types/exam-taking.types.js +1 -2
  489. package/dist/shared/types/history.types.js +1 -34
  490. package/dist/shared/types/index.js +1 -1
  491. package/dist/shared/types/questions/choose-correct-adjective.js +1 -4
  492. package/dist/shared/types/questions/choose-the-correct-answer.js +1 -9
  493. package/dist/shared/types/questions/fill-missing-words-in-grid.js +1 -11
  494. package/dist/shared/types/questions/index.js +1 -40
  495. package/dist/shared/types/questions/listen-and-speak-answer.js +1 -1
  496. package/dist/shared/types/questions/listen-and-speak-compare-images.js +1 -1
  497. package/dist/shared/types/questions/listen-and-speak-image-group.js +1 -1
  498. package/dist/shared/types/questions/listen-and-speak-info-exchange.js +1 -3
  499. package/dist/shared/types/questions/listen-and-speak-odd-one-out.js +1 -3
  500. package/dist/shared/types/questions/listen-and-speak-question-list.js +1 -1
  501. package/dist/shared/types/questions/listen-and-speak-with-story-images.js +1 -3
  502. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +1 -5
  503. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +1 -9
  504. package/dist/shared/types/questions/look-picture-fill-word-hint.js +1 -7
  505. package/dist/shared/types/questions/read-displayed-content.js +1 -1
  506. package/dist/shared/types/questions/speaking-conversation.js +1 -7
  507. package/dist/shared/types/questions/speaking-describe-image.js +1 -1
  508. package/dist/shared/types/questions/spontaneous-qa.js +1 -1
  509. package/dist/shared/types/questions/write-correct-verb-form.js +1 -12
  510. package/dist/shared/types/role.types.js +1 -1
  511. package/dist/shared/types/structure-management.js +1 -28
  512. package/dist/shared/types/student.types.js +1 -54
  513. package/dist/shared/types/teacher-dashboard-rooms.js +1 -2
  514. package/package.json +12 -3
@@ -1,446 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Button } from '../../../components/ui/button';
4
- import { Card, CardContent, CardHeader, CardTitle } from '../../../components/ui/card';
5
- import { Input } from '../../../components/ui/input';
6
- import { Check, Volume2, X } from 'lucide-react';
7
- import { useRef, useState } from 'react';
8
- import { toast } from 'sonner';
9
- import { t } from '../../../shared/lib/i18n';
10
- import { ReadAndColorObjectsClient } from '../types/read-and-color-objects/ReadAndColorObjectsClient';
11
- import { ArrangeLettersIntoWordsClient } from '../types/arrange-letters-into-words/ArrangeLettersIntoWordsClient';
12
- import { WriteShortParagraphClient } from '../types/write-short-paragraph/WriteShortParagraphClient';
13
- import { LabelThePictureClient } from '../types/label-the-picture/LabelThePictureClient';
14
- import { LookPictureFillWordHintClient } from '../types/look-picture-fill-word-hint/LookPictureFillWordHintClient';
15
- import { ReadPassageAndAnswerQuestionsClient } from '../types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient';
16
- import { ChooseTheCorrectAnswerClient } from '../types/choose-the-correct-answer/ChooseTheCorrectAnswerClient';
17
- import { WriteAShortLetterClient } from '../types/write-a-short-letter/WriteAShortLetterClient';
18
- import { LookPictureChooseCorrectAnswerClient } from '../types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient';
19
- import { LookPictureFillBlankChooseAnswerClient } from '../types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient';
20
- import { FillMissingWordsInGridClient } from '../types/fill-missing-words-in-grid/FillMissingWordsInGridClient';
21
- export function QuestionViewer({ question, onSubmitAnswer, isReviewMode = false, userAnswer, }) {
22
- const [selectedAnswer, setSelectedAnswer] = useState(userAnswer || null);
23
- const [playCount, setPlayCount] = useState(0);
24
- const audioRef = useRef(null);
25
- // State for TICK_TRUE_OR_FALSE / TICK_YES_OR_NO
26
- const [tickCrossAnswers, setTickCrossAnswers] = useState(userAnswer || {});
27
- // State for LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE
28
- const answer = question.answer;
29
- const [fillBlankAnswers, setFillBlankAnswers] = useState(userAnswer || Array(answer?.blanks?.length || 0).fill(''));
30
- // State for LISTEN_AND_COLOR_OBJECTS (legacy - kept for compatibility but not used in new implementation)
31
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
32
- const [colorAnswers, setColorAnswers] = useState(userAnswer || {});
33
- // State for READ_PASSAGE_AND_COMPLETE_STORY
34
- const [passageFillAnswers, setPassageFillAnswers] = useState(userAnswer || {});
35
- // State for VIEW_IMAGE_AND_FILL_IN_THE_BLANK
36
- const [viewImageFillAnswers, setViewImageFillAnswers] = useState(userAnswer || {});
37
- const handleAudioPlay = () => {
38
- const answer = question.answer;
39
- const maxPlay = answer.maxPlayCount || 3;
40
- if (playCount >= maxPlay) {
41
- toast.warning(t('common.audio.maxPlayReached', { maxPlay: String(maxPlay) }));
42
- return;
43
- }
44
- audioRef.current?.play();
45
- setPlayCount(playCount + 1);
46
- };
47
- const handleSubmit = () => {
48
- onSubmitAnswer?.(selectedAnswer);
49
- };
50
- ;
51
- const renderListeningFillBlank = () => {
52
- const answer = question.answer;
53
- const { audioUrl, blanks } = answer;
54
- return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center gap-4 rounded-lg bg-blue-50 p-4", children: [_jsxs(Button, { onClick: handleAudioPlay, variant: "outline", className: "gap-2", children: [_jsx(Volume2, { className: "h-5 w-5" }), "Ph\u00E1t \u00E2m thanh (", playCount, "/", answer.maxPlayCount || 3, ")"] }), _jsx("audio", { ref: audioRef, src: audioUrl })] }), _jsx("div", { className: "space-y-3", children: blanks.map((_, idx) => (_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("span", { className: "w-20 font-medium", children: ["Blank ", idx + 1, ":"] }), _jsx(Input, { value: fillBlankAnswers[idx], onChange: (e) => {
55
- const newAnswers = [...fillBlankAnswers];
56
- newAnswers[idx] = e.target.value;
57
- setFillBlankAnswers(newAnswers);
58
- setSelectedAnswer(newAnswers);
59
- }, placeholder: "Nh\u1EADp c\u00E2u tr\u1EA3 l\u1EDDi...", disabled: isReviewMode, className: isReviewMode
60
- ? fillBlankAnswers[idx]?.toLowerCase() === blanks[idx]?.toLowerCase()
61
- ? 'border-green-500 bg-green-50'
62
- : 'border-red-500 bg-red-50'
63
- : '' }), isReviewMode && (_jsxs("span", { className: "text-sm text-gray-600", children: ["\u0110\u00E1p \u00E1n: ", _jsx("strong", { children: blanks[idx] })] }))] }, idx))) })] }));
64
- };
65
- ;
66
- ;
67
- const renderReadingTickCross = () => {
68
- const answer = question.answer;
69
- const { statements } = answer;
70
- return (_jsx("div", { className: "space-y-3", children: statements.map((statement) => (_jsx("div", { className: `rounded-lg border-2 p-4 ${isReviewMode && tickCrossAnswers[statement.id] === statement.isCorrect ? 'border-green-500 bg-green-50' : ''} ${isReviewMode && tickCrossAnswers[statement.id] !== statement.isCorrect ? 'border-red-500 bg-red-50' : 'border-gray-200'} `, children: _jsxs("div", { className: "flex items-start gap-4", children: [_jsxs("div", { className: "mt-1 flex gap-2", children: [_jsx("button", { onClick: () => {
71
- const newAnswers = { ...tickCrossAnswers, [statement.id]: true };
72
- setTickCrossAnswers(newAnswers);
73
- setSelectedAnswer(newAnswers);
74
- }, disabled: isReviewMode, className: `flex h-8 w-8 items-center justify-center rounded border-2 ${tickCrossAnswers[statement.id] === true ? 'border-green-500 bg-green-500 text-white' : 'border-gray-300'} `, children: _jsx(Check, { className: "h-5 w-5" }) }), _jsx("button", { onClick: () => {
75
- const newAnswers = { ...tickCrossAnswers, [statement.id]: false };
76
- setTickCrossAnswers(newAnswers);
77
- setSelectedAnswer(newAnswers);
78
- }, disabled: isReviewMode, className: `flex h-8 w-8 items-center justify-center rounded border-2 ${tickCrossAnswers[statement.id] === false ? 'border-red-500 bg-red-500 text-white' : 'border-gray-300'} `, children: _jsx(X, { className: "h-5 w-5" }) })] }), _jsx("p", { className: "flex-1", children: statement.text }), isReviewMode && (_jsx("span", { className: "text-sm font-medium", children: statement.isCorrect ? '✓ Đúng' : '✗ Sai' }))] }) }, statement.id))) }));
79
- };
80
- const renderReadingLetterArrange = () => {
81
- const answer = question.answer;
82
- const correctAnswer = question.correctAnswer || question.correct_answer;
83
- // Merge correctWord from correct_answer into subQuestions
84
- const subQuestions = (answer?.subQuestions || []).map((sq) => {
85
- const correctWord = correctAnswer?.answers?.[sq.id] || sq.correctWord || '';
86
- return {
87
- ...sq,
88
- correctWord,
89
- };
90
- });
91
- const questionData = {
92
- subQuestions,
93
- };
94
- return (_jsx(ArrangeLettersIntoWordsClient, { questionData: questionData, onSubmit: (answers) => {
95
- setSelectedAnswer(answers);
96
- onSubmitAnswer?.(answers);
97
- }, isReviewMode: isReviewMode, userAnswer: userAnswer }));
98
- };
99
- const renderReadingPassageFill = () => {
100
- const answer = question.answer;
101
- const { passage, blanks } = answer;
102
- // Split passage by placeholders
103
- const parts = passage.split(/\{(\d+)\}/);
104
- return (_jsx("div", { className: "space-y-4", children: _jsx("div", { className: "prose max-w-none", children: _jsx("div", { className: "text-lg leading-relaxed", children: parts.map((part, idx) => {
105
- const blankIndex = parseInt(part);
106
- if (!isNaN(blankIndex)) {
107
- const blank = blanks.find((b) => b.index === blankIndex);
108
- return (_jsxs("span", { className: "mx-1 inline-flex items-center", children: [_jsx(Input, { value: passageFillAnswers[blankIndex] || '', onChange: (e) => {
109
- const newAnswers = { ...passageFillAnswers, [blankIndex]: e.target.value };
110
- setPassageFillAnswers(newAnswers);
111
- setSelectedAnswer(newAnswers);
112
- }, className: `inline-block h-8 w-32 text-center ${isReviewMode && passageFillAnswers[blankIndex] === blank?.correctAnswer
113
- ? 'border-green-500 bg-green-50'
114
- : ''} ${isReviewMode && passageFillAnswers[blankIndex] !== blank?.correctAnswer
115
- ? 'border-red-500 bg-red-50'
116
- : ''} `, disabled: isReviewMode }), isReviewMode && (_jsxs("span", { className: "ml-2 text-sm text-green-600", children: ["(", blank?.correctAnswer, ")"] }))] }, idx));
117
- }
118
- return _jsx("span", { children: part }, idx);
119
- }) }) }) }));
120
- };
121
- // Basic question type renderers
122
- ;
123
- const renderChooseTheCorrectAnswer = () => {
124
- const answer = question.answer;
125
- const { isGroup, questionGroup } = question;
126
- let questionData = {};
127
- if (question.content && typeof question.content === 'object') {
128
- questionData = {
129
- ...question.content,
130
- correctAnswer: question.correctAnswer?.answer ?? -1,
131
- id: question.id,
132
- isGroup,
133
- groupTitle: questionGroup?.payload?.title,
134
- groupContent: questionGroup?.payload?.content,
135
- groupImageUrl: questionGroup?.payload?.imageUrl,
136
- groupNumber: questionGroup?.groupNumber,
137
- };
138
- }
139
- else {
140
- questionData = {
141
- ...answer,
142
- question: question.content || answer?.question,
143
- id: question.id,
144
- isGroup,
145
- groupTitle: questionGroup?.payload?.title,
146
- groupContent: questionGroup?.payload?.content,
147
- groupImageUrl: questionGroup?.payload?.imageUrl,
148
- groupNumber: questionGroup?.groupNumber,
149
- };
150
- }
151
- return (_jsx(ChooseTheCorrectAnswerClient, { questionData: questionData, questionConfig: question.questionConfig, onSubmit: (ans) => {
152
- setSelectedAnswer(ans);
153
- onSubmitAnswer?.(ans);
154
- }, isReviewMode: isReviewMode, userAnswer: userAnswer }));
155
- };
156
- const renderFillInBlank = () => {
157
- const answer = question.answer;
158
- const { blanks, caseSensitive = false } = answer;
159
- const handleBlankChange = (index, value) => {
160
- const newAnswers = [...fillBlankAnswers];
161
- newAnswers[index] = value;
162
- setFillBlankAnswers(newAnswers);
163
- };
164
- const isCorrect = (index, value) => {
165
- const correctAnswer = blanks[index];
166
- return caseSensitive
167
- ? value === correctAnswer
168
- : value.toLowerCase() === correctAnswer.toLowerCase();
169
- };
170
- return (_jsxs("div", { className: "space-y-4", children: [blanks.map((correctAnswer, index) => {
171
- const userValue = fillBlankAnswers[index] || '';
172
- const isCorrectAnswer = isReviewMode && isCorrect(index, userValue);
173
- return (_jsxs("div", { className: "flex items-center space-x-2", children: [_jsxs("span", { className: "text-sm font-medium", children: ["Ch\u1ED7 tr\u1ED1ng ", index + 1, ":"] }), _jsx(Input, { value: userValue, onChange: (e) => handleBlankChange(index, e.target.value), placeholder: "Nh\u1EADp \u0111\u00E1p \u00E1n...", className: `flex-1 ${isReviewMode
174
- ? isCorrectAnswer
175
- ? 'border-green-500 bg-green-50'
176
- : 'border-red-500 bg-red-50'
177
- : ''}`, disabled: isReviewMode }), isReviewMode && isCorrectAnswer && _jsx(Check, { className: "h-4 w-4 text-green-600" }), isReviewMode && !isCorrectAnswer && userValue && (_jsx(X, { className: "h-4 w-4 text-red-600" }))] }, index));
178
- }), !isReviewMode && (_jsx(Button, { onClick: () => {
179
- if (onSubmitAnswer) {
180
- onSubmitAnswer(fillBlankAnswers);
181
- }
182
- }, disabled: fillBlankAnswers.some((answer) => answer.trim() === ''), children: "N\u1ED9p \u0111\u00E1p \u00E1n" }))] }));
183
- };
184
- const renderTrueFalse = () => {
185
- const answer = question.answer;
186
- const { correctAnswer } = answer;
187
- const options = [
188
- { value: true, label: 'Đúng' },
189
- { value: false, label: 'Sai' },
190
- ];
191
- return (_jsxs("div", { className: "space-y-3", children: [options.map((option) => {
192
- const isSelected = selectedAnswer === option.value;
193
- return (_jsx("div", { className: `flex cursor-pointer items-center rounded-lg border p-3 transition-colors ${isSelected ? 'border-blue-500 bg-blue-50' : 'border-gray-200 hover:border-gray-300'} ${isReviewMode && option.value === correctAnswer
194
- ? 'border-green-500 bg-green-50'
195
- : isReviewMode && isSelected && option.value !== correctAnswer
196
- ? 'border-red-500 bg-red-50'
197
- : ''}`, onClick: () => !isReviewMode && setSelectedAnswer(option.value), children: _jsxs("div", { className: "flex w-full items-center", children: [_jsx("div", { className: `mr-3 h-4 w-4 rounded-full border-2 ${isSelected ? 'border-blue-500 bg-blue-500' : 'border-gray-300'} ${isReviewMode && option.value === correctAnswer
198
- ? 'border-green-500 bg-green-500'
199
- : ''}`, children: isSelected && _jsx("div", { className: "m-auto h-2 w-2 rounded-full bg-white" }) }), _jsx("span", { className: "flex-1", children: option.label }), isReviewMode && option.value === correctAnswer && (_jsx(Check, { className: "ml-2 h-4 w-4 text-green-600" })), isReviewMode && isSelected && option.value !== correctAnswer && (_jsx(X, { className: "ml-2 h-4 w-4 text-red-600" }))] }) }, option.value.toString()));
200
- }), !isReviewMode && (_jsx(Button, { onClick: () => {
201
- if (onSubmitAnswer) {
202
- onSubmitAnswer(selectedAnswer);
203
- }
204
- }, disabled: selectedAnswer === null, children: "N\u1ED9p \u0111\u00E1p \u00E1n" }))] }));
205
- };
206
- const renderEssay = () => {
207
- const answer = question.answer;
208
- const { minWords, maxWords } = answer;
209
- const wordCount = selectedAnswer ? selectedAnswer.trim().split(/\s+/).length : 0;
210
- const isWithinLimit = wordCount >= (minWords || 0) && wordCount <= (maxWords || Infinity);
211
- return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "text-sm text-gray-600", children: [minWords && maxWords
212
- ? `Số từ: ${minWords} - ${maxWords} từ`
213
- : minWords
214
- ? `Tối thiểu: ${minWords} từ`
215
- : maxWords
216
- ? `Tối đa: ${maxWords} từ`
217
- : '', selectedAnswer && (_jsxs("span", { className: `ml-2 ${isWithinLimit ? 'text-green-600' : 'text-red-600'}`, children: ["(", wordCount, " t\u1EEB)"] }))] }), _jsx("textarea", { value: selectedAnswer || '', onChange: (e) => setSelectedAnswer(e.target.value), placeholder: "Vi\u1EBFt c\u00E2u tr\u1EA3 l\u1EDDi c\u1EE7a b\u1EA1n \u1EDF \u0111\u00E2y...", className: "h-32 w-full resize-none rounded-lg border border-gray-300 p-3 focus:border-blue-500 focus:ring-1 focus:ring-blue-500", disabled: isReviewMode }), !isReviewMode && (_jsx(Button, { onClick: () => {
218
- if (onSubmitAnswer) {
219
- onSubmitAnswer(selectedAnswer);
220
- }
221
- }, disabled: !selectedAnswer || selectedAnswer.trim().length < (minWords || 0), children: "N\u1ED9p \u0111\u00E1p \u00E1n" }))] }));
222
- };
223
- ;
224
- ;
225
- ;
226
- ;
227
- ;
228
- const renderWriteShortParagraph = () => {
229
- const answer = question.answer;
230
- const questionData = {
231
- contexts: answer?.contexts || [],
232
- minWords: answer?.minWords || 20,
233
- maxWords: answer?.maxWords,
234
- instruction: answer?.instruction || '',
235
- };
236
- return (_jsx(WriteShortParagraphClient, { questionData: questionData, onSubmit: (answerText) => {
237
- setSelectedAnswer(answerText);
238
- onSubmitAnswer?.(answerText);
239
- }, isReviewMode: isReviewMode, userAnswer: userAnswer }));
240
- };
241
- const renderWriteAShortLetter = () => {
242
- const answer = question.answer;
243
- const questionData = {
244
- contexts: answer?.contexts || [],
245
- informations: answer?.informations || [],
246
- minWords: answer?.minWords || 20,
247
- maxWords: answer?.maxWords,
248
- instruction: answer?.instruction || '',
249
- };
250
- return (_jsx(WriteAShortLetterClient, { questionData: questionData, onSubmit: (answerText) => {
251
- setSelectedAnswer(answerText);
252
- onSubmitAnswer?.(answerText);
253
- }, isReviewMode: isReviewMode, userAnswer: userAnswer }));
254
- };
255
- ;
256
- ;
257
- const renderLabelThePicture = () => {
258
- const questionData = question.answer; // answer contains the full data structure
259
- return (_jsx(LabelThePictureClient, { questionData: questionData, userAnswer: userAnswer, isReviewMode: isReviewMode, onSubmit: (answers) => {
260
- setSelectedAnswer(answers);
261
- onSubmitAnswer?.(answers);
262
- } }));
263
- };
264
- const renderReadAndColorObjects = () => {
265
- const answer = question.answer;
266
- const questionData = {
267
- backgroundImageUrl: answer?.backgroundImageUrl || '',
268
- colorNodes: answer?.colorNodes || [],
269
- regionNodes: answer?.regionNodes || [],
270
- instructions: answer?.instructions || [],
271
- };
272
- return (_jsx(ReadAndColorObjectsClient, { questionData: questionData, onSubmit: (userConnections) => {
273
- setSelectedAnswer(userConnections);
274
- onSubmitAnswer?.(userConnections);
275
- } }));
276
- };
277
- const renderLookPictureFillWordHint = () => {
278
- const answer = question.answer;
279
- const content = question.content;
280
- // Check if question has group info (from API)
281
- const questionGroup = question.questionGroup;
282
- let subQuestions = [];
283
- if (answer?.subQuestions && answer.subQuestions.length > 0) {
284
- subQuestions = answer.subQuestions;
285
- }
286
- else if (content?.imageUrl && content?.word) {
287
- // Single question format - transform to subQuestions
288
- subQuestions = [{
289
- id: question.id,
290
- imageUrl: content.imageUrl,
291
- word: content.word,
292
- correctAnswer: answer?.correctAnswer || '',
293
- groupNumber: questionGroup?.groupNumber || 1,
294
- }];
295
- }
296
- // Get hintLetters from multiple sources (priority order):
297
- // 1. questionGroup.payload.hintLetters (from API group payload)
298
- // 2. answer.hintLetters
299
- // 3. content.hintLetters
300
- const hintLetters = questionGroup?.payload?.hintLetters ||
301
- answer?.hintLetters ||
302
- content?.hintLetters ||
303
- [];
304
- // Build hintLettersByGroup if we have a groupNumber
305
- const groupNumber = questionGroup?.groupNumber || 1;
306
- const hintLettersByGroup = {};
307
- if (hintLetters.length > 0) {
308
- hintLettersByGroup[groupNumber] = hintLetters;
309
- }
310
- const questionData = {
311
- subQuestions,
312
- hintLetters,
313
- hintLettersByGroup,
314
- groups: [{ number: groupNumber, questionCount: subQuestions.length }],
315
- };
316
- return (_jsx(LookPictureFillWordHintClient, { questionData: questionData, userAnswer: userAnswer, isReviewMode: isReviewMode, explanation: question.explanation, onSubmit: (answers) => {
317
- setSelectedAnswer(answers);
318
- onSubmitAnswer?.(answers);
319
- } }));
320
- };
321
- const renderReadPassageAndAnswerQuestions = () => {
322
- const answer = question.answer;
323
- const questionData = {
324
- passage: answer?.passage || '',
325
- passageTitle: answer?.passageTitle || '',
326
- instructions: answer?.instructions || '',
327
- subQuestions: answer?.subQuestions || [],
328
- };
329
- return (_jsx(ReadPassageAndAnswerQuestionsClient, { questionData: questionData, userAnswer: userAnswer, isReviewMode: isReviewMode, onSubmit: (answers) => {
330
- setSelectedAnswer(answers);
331
- onSubmitAnswer?.(answers);
332
- } }));
333
- };
334
- const renderLookPictureChooseCorrectAnswer = () => {
335
- const content = question.content;
336
- const answer = question.answer;
337
- // Merge correct answers into subQuestions
338
- const subQuestions = (content?.subQuestions || answer?.subQuestions || []).map((sq) => {
339
- const correctAnswerId = answer?.answers?.[sq.id] || sq.correctAnswer;
340
- return {
341
- ...sq,
342
- options: (sq.options || []).map((opt) => ({
343
- ...opt,
344
- isCorrect: opt.id === correctAnswerId,
345
- })),
346
- };
347
- });
348
- const questionData = {
349
- subQuestions,
350
- };
351
- return (_jsx(LookPictureChooseCorrectAnswerClient, { questionData: questionData, userAnswer: userAnswer, isReviewMode: isReviewMode, onSubmit: (answers) => {
352
- setSelectedAnswer(answers);
353
- onSubmitAnswer?.(answers);
354
- } }));
355
- };
356
- const renderLookPictureFillBlankChooseAnswer = () => {
357
- const content = question.content;
358
- const answer = question.answer;
359
- // Merge correct answers into subQuestions
360
- const subQuestions = (content?.subQuestions || answer?.subQuestions || []).map((sq) => {
361
- const correctAnswerId = answer?.answers?.[sq.id] || sq.correctAnswer;
362
- return {
363
- ...sq,
364
- options: (sq.options || []).map((opt) => ({
365
- ...opt,
366
- isCorrect: opt.id === correctAnswerId,
367
- })),
368
- };
369
- });
370
- const questionData = {
371
- subQuestions,
372
- };
373
- return (_jsx(LookPictureFillBlankChooseAnswerClient, { questionData: questionData, userAnswer: userAnswer, isReviewMode: isReviewMode, onSubmit: (answers) => {
374
- setSelectedAnswer(answers);
375
- onSubmitAnswer?.(answers);
376
- } }));
377
- };
378
- const renderFillMissingWordsInGrid = () => {
379
- const content = question.content;
380
- const answer = question.answer;
381
- // Build questionData in the expected format: { content: {...}, answer: {...} }
382
- const questionData = {
383
- content: {
384
- grid: content?.grid || [],
385
- rows: content?.rows || 3,
386
- columns: content?.columns || 3,
387
- disabledCells: content?.disabledCells || [],
388
- },
389
- answer: {
390
- answers: answer?.answers || {},
391
- missingLetters: answer?.missingLetters || [],
392
- caseSensitive: answer?.caseSensitive || false,
393
- },
394
- };
395
- return (_jsx(FillMissingWordsInGridClient, { questionData: questionData, userAnswer: userAnswer, isReviewMode: isReviewMode, onSubmit: (answers) => {
396
- setSelectedAnswer(answers);
397
- onSubmitAnswer?.(answers);
398
- } }));
399
- };
400
- const renderQuestion = () => {
401
- switch (question.type) {
402
- case 'CHOOSE_THE_CORRECT_ANSWER':
403
- return renderChooseTheCorrectAnswer();
404
- case 'FILL_IN_BLANK':
405
- return renderFillInBlank();
406
- case 'TRUE_FALSE':
407
- return renderTrueFalse();
408
- case 'ESSAY':
409
- return renderEssay();
410
- case 'LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE':
411
- return renderListeningFillBlank();
412
- case 'TICK_TRUE_OR_FALSE':
413
- case 'TICK_YES_OR_NO':
414
- return renderReadingTickCross();
415
- case 'ARRANGE_LETTERS_INTO_WORDS':
416
- return renderReadingLetterArrange();
417
- case 'READ_PASSAGE_AND_COMPLETE_STORY':
418
- return renderReadingPassageFill();
419
- case 'WRITE_SHORT_PARAGRAPH':
420
- return renderWriteShortParagraph();
421
- case 'WRITE_A_SHORT_LETTER':
422
- return renderWriteAShortLetter();
423
- // Marketing exam questions grade 1-2
424
- case 'LOOK_PICTURE_FILL_WORD_HINT':
425
- return renderLookPictureFillWordHint();
426
- case 'LABEL_THE_PICTURE':
427
- return renderLabelThePicture();
428
- case 'READ_AND_COLOR_OBJECTS':
429
- return renderReadAndColorObjects();
430
- case 'READ_PASSAGE_AND_ANSWER_QUESTIONS':
431
- return renderReadPassageAndAnswerQuestions();
432
- // Additional marketing exam questions
433
- case 'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER':
434
- return renderLookPictureChooseCorrectAnswer();
435
- case 'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER':
436
- return renderLookPictureFillBlankChooseAnswer();
437
- case 'FILL_MISSING_WORDS_IN_GRID':
438
- return renderFillMissingWordsInGrid();
439
- default:
440
- return _jsxs("div", { children: ["Lo\u1EA1i c\u00E2u h\u1ECFi kh\u00F4ng \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3: ", question.type] });
441
- }
442
- };
443
- return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs(CardTitle, { className: "flex items-center justify-between", children: [_jsx("span", { children: typeof question.content === 'string'
444
- ? question.content
445
- : question.content?.question || 'Câu hỏi' }), _jsxs("span", { className: "text-sm font-normal text-gray-500", children: [question.points, " \u0111i\u1EC3m"] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [renderQuestion(), !isReviewMode && (_jsx("div", { className: "flex justify-end border-t pt-4", children: _jsx(Button, { onClick: handleSubmit, className: "bg-blue-500 hover:bg-blue-600", children: "X\u00E1c nh\u1EADn \u0111\u00E1p \u00E1n" }) }))] })] }));
446
- }
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Button as s}from"../../../components/ui/button";import{Card as n,CardContent as t,CardHeader as o,CardTitle as a}from"../../../components/ui/card";import{Input as i}from"../../../components/ui/input";import{Check as c,Volume2 as l,X as d}from"lucide-react";import{useRef as u,useState as m}from"react";import{toast as p}from"sonner";import{t as g}from"../../../shared/lib/i18n";import{ReadAndColorObjectsClient as b}from"../types/read-and-color-objects/ReadAndColorObjectsClient";import{ArrangeLettersIntoWordsClient as h}from"../types/arrange-letters-into-words/ArrangeLettersIntoWordsClient";import{WriteShortParagraphClient as w}from"../types/write-short-paragraph/WriteShortParagraphClient";import{LabelThePictureClient as N}from"../types/label-the-picture/LabelThePictureClient";import{LookPictureFillWordHintClient as C}from"../types/look-picture-fill-word-hint/LookPictureFillWordHintClient";import{ReadPassageAndAnswerQuestionsClient as f}from"../types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient";import{ChooseTheCorrectAnswerClient as x}from"../types/choose-the-correct-answer/ChooseTheCorrectAnswerClient";import{WriteAShortLetterClient as A}from"../types/write-a-short-letter/WriteAShortLetterClient";import{LookPictureChooseCorrectAnswerClient as _}from"../types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient";import{LookPictureFillBlankChooseAnswerClient as v}from"../types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient";import{FillMissingWordsInGridClient as y}from"../types/fill-missing-words-in-grid/FillMissingWordsInGridClient";export function QuestionViewer({question:S,onSubmitAnswer:R,isReviewMode:L=!1,userAnswer:E}){const[T,I]=m(E||null),[O,W]=m(0),k=u(null),[D,q]=m(E||{}),P=S.answer,[Q,U]=m(E||Array(P?.blanks?.length||0).fill("")),[M,G]=m(E||{}),[$,H]=m(E||{}),[j,F]=m(E||{}),B=()=>{const e=S.answer.maxPlayCount||3;O>=e?p.warning(g("common.audio.maxPlayReached",{maxPlay:String(e)})):(k.current?.play(),W(O+1))},K=()=>{const n=S.answer,{blanks:t,caseSensitive:o=!1}=n;return r("div",{className:"space-y-4",children:[t.map((s,n)=>{const a=Q[n]||"",l=L&&((e,r)=>{const s=t[e];return o?r===s:r.toLowerCase()===s.toLowerCase()})(n,a);return r("div",{className:"flex items-center space-x-2",children:[r("span",{className:"text-sm font-medium",children:["Chỗ trống ",n+1,":"]}),e(i,{value:a,onChange:e=>((e,r)=>{const s=[...Q];s[e]=r,U(s)})(n,e.target.value),placeholder:"Nhập đáp án...",className:"flex-1 "+(L?l?"border-green-500 bg-green-50":"border-red-500 bg-red-50":""),disabled:L}),L&&l&&e(c,{className:"h-4 w-4 text-green-600"}),L&&!l&&a&&e(d,{className:"h-4 w-4 text-red-600"})]},n)}),!L&&e(s,{onClick:()=>{R&&R(Q)},disabled:Q.some(e=>""===e.trim()),children:"Nộp đáp án"})]})};return r(n,{children:[e(o,{children:r(a,{className:"flex items-center justify-between",children:[e("span",{children:"string"==typeof S.content?S.content:S.content?.question||"Câu hỏi"}),r("span",{className:"text-sm font-normal text-gray-500",children:[S.points," điểm"]})]})}),r(t,{className:"space-y-4",children:[(()=>{switch(S.type){case"CHOOSE_THE_CORRECT_ANSWER":return(()=>{const r=S.answer,{isGroup:s,questionGroup:n}=S;let t={};return t=S.content&&"object"==typeof S.content?{...S.content,correctAnswer:S.correctAnswer?.answer??-1,id:S.id,isGroup:s,groupTitle:n?.payload?.title,groupContent:n?.payload?.content,groupImageUrl:n?.payload?.imageUrl,groupNumber:n?.groupNumber}:{...r,question:S.content||r?.question,id:S.id,isGroup:s,groupTitle:n?.payload?.title,groupContent:n?.payload?.content,groupImageUrl:n?.payload?.imageUrl,groupNumber:n?.groupNumber},e(x,{questionData:t,questionConfig:S.questionConfig,onSubmit:e=>{I(e),R?.(e)},isReviewMode:L,userAnswer:E})})();case"FILL_IN_BLANK":return K();case"TRUE_FALSE":return(()=>{const n=S.answer,{correctAnswer:t}=n;return r("div",{className:"space-y-3",children:[[{value:!0,label:"Đúng"},{value:!1,label:"Sai"}].map(s=>{const n=T===s.value;return e("div",{className:`flex cursor-pointer items-center rounded-lg border p-3 transition-colors ${n?"border-blue-500 bg-blue-50":"border-gray-200 hover:border-gray-300"} ${L&&s.value===t?"border-green-500 bg-green-50":L&&n&&s.value!==t?"border-red-500 bg-red-50":""}`,onClick:()=>!L&&I(s.value),children:r("div",{className:"flex w-full items-center",children:[e("div",{className:`mr-3 h-4 w-4 rounded-full border-2 ${n?"border-blue-500 bg-blue-500":"border-gray-300"} ${L&&s.value===t?"border-green-500 bg-green-500":""}`,children:n&&e("div",{className:"m-auto h-2 w-2 rounded-full bg-white"})}),e("span",{className:"flex-1",children:s.label}),L&&s.value===t&&e(c,{className:"ml-2 h-4 w-4 text-green-600"}),L&&n&&s.value!==t&&e(d,{className:"ml-2 h-4 w-4 text-red-600"})]})},s.value.toString())}),!L&&e(s,{onClick:()=>{R&&R(T)},disabled:null===T,children:"Nộp đáp án"})]})})();case"ESSAY":return(()=>{const n=S.answer,{minWords:t,maxWords:o}=n,a=T?T.trim().split(/\s+/).length:0;return r("div",{className:"space-y-4",children:[r("div",{className:"text-sm text-gray-600",children:[t&&o?`Số từ: ${t} - ${o} từ`:t?`Tối thiểu: ${t} từ`:o?`Tối đa: ${o} từ`:"",T&&r("span",{className:"ml-2 "+(a>=(t||0)&&a<=(o||1/0)?"text-green-600":"text-red-600"),children:["(",a," từ)"]})]}),e("textarea",{value:T||"",onChange:e=>I(e.target.value),placeholder:"Viết câu trả lời của bạn ở đây...",className:"h-32 w-full resize-none rounded-lg border border-gray-300 p-3 focus:border-blue-500 focus:ring-1 focus:ring-blue-500",disabled:L}),!L&&e(s,{onClick:()=>{R&&R(T)},disabled:!T||T.trim().length<(t||0),children:"Nộp đáp án"})]})})();case"LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE":return(()=>{const n=S.answer,{audioUrl:t,blanks:o}=n;return r("div",{className:"space-y-4",children:[r("div",{className:"flex items-center gap-4 rounded-lg bg-blue-50 p-4",children:[r(s,{onClick:B,variant:"outline",className:"gap-2",children:[e(l,{className:"h-5 w-5"}),"Phát âm thanh (",O,"/",n.maxPlayCount||3,")"]}),e("audio",{ref:k,src:t})]}),e("div",{className:"space-y-3",children:o.map((s,n)=>r("div",{className:"flex items-center gap-3",children:[r("span",{className:"w-20 font-medium",children:["Blank ",n+1,":"]}),e(i,{value:Q[n],onChange:e=>{const r=[...Q];r[n]=e.target.value,U(r),I(r)},placeholder:"Nhập câu trả lời...",disabled:L,className:L?Q[n]?.toLowerCase()===o[n]?.toLowerCase()?"border-green-500 bg-green-50":"border-red-500 bg-red-50":""}),L&&r("span",{className:"text-sm text-gray-600",children:["Đáp án: ",e("strong",{children:o[n]})]})]},n))})]})})();case"TICK_TRUE_OR_FALSE":case"TICK_YES_OR_NO":return(()=>{const s=S.answer,{statements:n}=s;return e("div",{className:"space-y-3",children:n.map(s=>e("div",{className:`rounded-lg border-2 p-4 ${L&&D[s.id]===s.isCorrect?"border-green-500 bg-green-50":""} ${L&&D[s.id]!==s.isCorrect?"border-red-500 bg-red-50":"border-gray-200"} `,children:r("div",{className:"flex items-start gap-4",children:[r("div",{className:"mt-1 flex gap-2",children:[e("button",{onClick:()=>{const e={...D,[s.id]:!0};q(e),I(e)},disabled:L,className:`flex h-8 w-8 items-center justify-center rounded border-2 ${!0===D[s.id]?"border-green-500 bg-green-500 text-white":"border-gray-300"} `,children:e(c,{className:"h-5 w-5"})}),e("button",{onClick:()=>{const e={...D,[s.id]:!1};q(e),I(e)},disabled:L,className:`flex h-8 w-8 items-center justify-center rounded border-2 ${!1===D[s.id]?"border-red-500 bg-red-500 text-white":"border-gray-300"} `,children:e(d,{className:"h-5 w-5"})})]}),e("p",{className:"flex-1",children:s.text}),L&&e("span",{className:"text-sm font-medium",children:s.isCorrect?"✓ Đúng":"✗ Sai"})]})},s.id))})})();case"ARRANGE_LETTERS_INTO_WORDS":return(()=>{const r=S.answer,s=S.correctAnswer||S.correct_answer,n=(r?.subQuestions||[]).map(e=>{const r=s?.answers?.[e.id]||e.correctWord||"";return{...e,correctWord:r}});return e(h,{questionData:{subQuestions:n},onSubmit:e=>{I(e),R?.(e)},isReviewMode:L,userAnswer:E})})();case"READ_PASSAGE_AND_COMPLETE_STORY":return(()=>{const s=S.answer,{passage:n,blanks:t}=s,o=n.split(/\{(\d+)\}/);return e("div",{className:"space-y-4",children:e("div",{className:"prose max-w-none",children:e("div",{className:"text-lg leading-relaxed",children:o.map((s,n)=>{const o=parseInt(s);if(!isNaN(o)){const s=t.find(e=>e.index===o);return r("span",{className:"mx-1 inline-flex items-center",children:[e(i,{value:$[o]||"",onChange:e=>{const r={...$,[o]:e.target.value};H(r),I(r)},className:`inline-block h-8 w-32 text-center ${L&&$[o]===s?.correctAnswer?"border-green-500 bg-green-50":""} ${L&&$[o]!==s?.correctAnswer?"border-red-500 bg-red-50":""} `,disabled:L}),L&&r("span",{className:"ml-2 text-sm text-green-600",children:["(",s?.correctAnswer,")"]})]},n)}return e("span",{children:s},n)})})})})})();case"WRITE_SHORT_PARAGRAPH":return(()=>{const r=S.answer;return e(w,{questionData:{contexts:r?.contexts||[],minWords:r?.minWords||20,maxWords:r?.maxWords,instruction:r?.instruction||""},onSubmit:e=>{I(e),R?.(e)},isReviewMode:L,userAnswer:E})})();case"WRITE_A_SHORT_LETTER":return(()=>{const r=S.answer;return e(A,{questionData:{contexts:r?.contexts||[],informations:r?.informations||[],minWords:r?.minWords||20,maxWords:r?.maxWords,instruction:r?.instruction||""},onSubmit:e=>{I(e),R?.(e)},isReviewMode:L,userAnswer:E})})();case"LOOK_PICTURE_FILL_WORD_HINT":return(()=>{const r=S.answer,s=S.content,n=S.questionGroup;let t=[];r?.subQuestions&&r.subQuestions.length>0?t=r.subQuestions:s?.imageUrl&&s?.word&&(t=[{id:S.id,imageUrl:s.imageUrl,word:s.word,correctAnswer:r?.correctAnswer||"",groupNumber:n?.groupNumber||1}]);const o=n?.payload?.hintLetters||r?.hintLetters||s?.hintLetters||[],a=n?.groupNumber||1,i={};o.length>0&&(i[a]=o);const c={subQuestions:t,hintLetters:o,hintLettersByGroup:i,groups:[{number:a,questionCount:t.length}]};return e(C,{questionData:c,userAnswer:E,isReviewMode:L,explanation:S.explanation,onSubmit:e=>{I(e),R?.(e)}})})();case"LABEL_THE_PICTURE":return(()=>{const r=S.answer;return e(N,{questionData:r,userAnswer:E,isReviewMode:L,onSubmit:e=>{I(e),R?.(e)}})})();case"READ_AND_COLOR_OBJECTS":return(()=>{const r=S.answer;return e(b,{questionData:{backgroundImageUrl:r?.backgroundImageUrl||"",colorNodes:r?.colorNodes||[],regionNodes:r?.regionNodes||[],instructions:r?.instructions||[]},onSubmit:e=>{I(e),R?.(e)}})})();case"READ_PASSAGE_AND_ANSWER_QUESTIONS":return(()=>{const r=S.answer;return e(f,{questionData:{passage:r?.passage||"",passageTitle:r?.passageTitle||"",instructions:r?.instructions||"",subQuestions:r?.subQuestions||[]},userAnswer:E,isReviewMode:L,onSubmit:e=>{I(e),R?.(e)}})})();case"LOOK_PICTURE_CHOOSE_CORRECT_ANSWER":return(()=>{const r=S.content,s=S.answer,n=(r?.subQuestions||s?.subQuestions||[]).map(e=>{const r=s?.answers?.[e.id]||e.correctAnswer;return{...e,options:(e.options||[]).map(e=>({...e,isCorrect:e.id===r}))}});return e(_,{questionData:{subQuestions:n},userAnswer:E,isReviewMode:L,onSubmit:e=>{I(e),R?.(e)}})})();case"LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER":return(()=>{const r=S.content,s=S.answer,n=(r?.subQuestions||s?.subQuestions||[]).map(e=>{const r=s?.answers?.[e.id]||e.correctAnswer;return{...e,options:(e.options||[]).map(e=>({...e,isCorrect:e.id===r}))}});return e(v,{questionData:{subQuestions:n},userAnswer:E,isReviewMode:L,onSubmit:e=>{I(e),R?.(e)}})})();case"FILL_MISSING_WORDS_IN_GRID":return(()=>{const r=S.content,s=S.answer;return e(y,{questionData:{content:{grid:r?.grid||[],rows:r?.rows||3,columns:r?.columns||3,disabledCells:r?.disabledCells||[]},answer:{answers:s?.answers||{},missingLetters:s?.missingLetters||[],caseSensitive:s?.caseSensitive||!1}},userAnswer:E,isReviewMode:L,onSubmit:e=>{I(e),R?.(e)}})})();default:return r("div",{children:["Loại câu hỏi không được hỗ trợ: ",S.type]})}})(),!L&&e("div",{className:"flex justify-end border-t pt-4",children:e(s,{onClick:()=>{R?.(T)},className:"bg-blue-500 hover:bg-blue-600",children:"Xác nhận đáp án"})})]})]})}
@@ -1,2 +1 @@
1
- export { QuestionViewer } from './QuestionViewer';
2
- export { QuestionGroupViewer } from './QuestionGroupViewer';
1
+ export{QuestionViewer}from"./QuestionViewer";export{QuestionGroupViewer}from"./QuestionGroupViewer";
@@ -1,98 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useEffect, useMemo, useState } from 'react';
4
- import { ChevronLeft, Download, Loader2, Sparkles, XCircle } from 'lucide-react';
5
- import { Button } from '../../components/ui/button';
6
- import { Card, CardContent, CardHeader, CardTitle } from '../../components/ui/card';
7
- import { useToast } from '../../shared/lib/hooks/useToast';
8
- import { transformResultAnswersForReview } from './review-data';
9
- import { ReviewQuestionRenderer } from './ReviewQuestionRenderer';
10
- import { unwrap } from './result-review.utils';
11
- export function ResultReviewPageContainer({ attemptId, token, api, onBack, onNavigate, topActionsSlot, beforeReportSlot, afterReportSlot }) {
12
- const { error: errorToast, success } = useToast();
13
- const [detail, setDetail] = useState(null);
14
- const [attempt, setAttempt] = useState(null);
15
- const [isLoading, setIsLoading] = useState(true);
16
- const [isExporting, setIsExporting] = useState(false);
17
- useEffect(() => {
18
- let ignore = false;
19
- setIsLoading(true);
20
- Promise.all([api.getDetailedResult(attemptId, token), api.getAttempt?.(attemptId, token)])
21
- .then(([detailResult, attemptResult]) => {
22
- if (ignore)
23
- return;
24
- setDetail(unwrap(detailResult));
25
- setAttempt(attemptResult ? unwrap(attemptResult) : null);
26
- })
27
- .catch((err) => errorToast({ message: 'Không thể tải kết quả', description: err instanceof Error ? err.message : undefined }))
28
- .finally(() => { if (!ignore)
29
- setIsLoading(false); });
30
- return () => { ignore = true; };
31
- }, [api, attemptId, token, errorToast]);
32
- const reviewData = useMemo(() => transformResultAnswersForReview(detail?.answers ?? [], attempt?.exam?.template?.parts), [detail?.answers, attempt?.exam?.template?.parts]);
33
- const studentName = attempt?.studentName ?? 'Học sinh';
34
- const studentCode = attempt?.studentUsername;
35
- const branchName = attempt?.branchName ?? 'Chưa tham gia chi nhánh';
36
- const examTitle = attempt?.exam?.template?.title ?? detail?.examName ?? 'Kết quả bài thi';
37
- const handleBack = () => {
38
- if (onBack)
39
- onBack();
40
- else if (onNavigate)
41
- onNavigate('/student/results');
42
- else if (typeof window !== 'undefined')
43
- window.history.back();
44
- };
45
- const handleExport = async () => {
46
- if (!detail || !api.exportPdf)
47
- return;
48
- setIsExporting(true);
49
- try {
50
- const { blob, filename } = await api.exportPdf(attemptId, attempt?.exam?.template?.type);
51
- downloadBlob(blob, filename);
52
- success({ message: 'Xuất PDF thành công' });
53
- }
54
- catch (err) {
55
- errorToast({ message: 'Không xuất được PDF', description: err instanceof Error ? err.message : undefined });
56
- }
57
- finally {
58
- setIsExporting(false);
59
- }
60
- };
61
- if (isLoading)
62
- return _jsx(State, { icon: _jsx(Loader2, { className: "h-8 w-8 animate-spin text-[#2874A6]" }), title: "\u0110ang t\u1EA3i k\u1EBFt qu\u1EA3 b\u00E0i thi..." });
63
- if (!detail)
64
- return _jsx(State, { icon: _jsx(XCircle, { className: "h-8 w-8 text-red-400" }), title: "Kh\u00F4ng th\u1EC3 t\u1EA3i k\u1EBFt qu\u1EA3", action: _jsx(Button, { onClick: handleBack, children: "Quay l\u1EA1i" }) });
65
- return (_jsx("div", { className: "min-h-screen bg-[#e0e0e0] py-4 md:py-6", children: _jsxs("div", { className: "mx-auto flex w-full max-w-[1160px] flex-col gap-4 px-3 sm:px-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-3 print:hidden", children: [_jsxs(Button, { variant: "outline", onClick: handleBack, children: [_jsx(ChevronLeft, { className: "mr-2 h-4 w-4" }), "Quay l\u1EA1i"] }), _jsxs("div", { className: "flex items-center gap-2", children: [api.exportPdf && _jsxs(Button, { onClick: () => void handleExport(), disabled: isExporting, className: "bg-[#00AEEF] hover:bg-[#0099D6]", children: [isExporting ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : _jsx(Download, { className: "mr-2 h-4 w-4" }), "Xu\u1EA5t PDF"] }), topActionsSlot] })] }), beforeReportSlot, _jsxs(Card, { className: "overflow-hidden rounded-[28px] border-0 shadow-xl", children: [_jsxs(CardHeader, { className: "bg-gradient-to-r from-[#0054A6] to-[#00AEEF] text-white", children: [_jsx(CardTitle, { className: "text-2xl", children: examTitle }), _jsxs("p", { className: "text-sm text-white/80", children: [studentName, studentCode ? ` • ${studentCode}` : '', " \u2022 ", branchName] })] }), _jsxs(CardContent, { className: "space-y-6 p-5 md:p-8", children: [_jsx(ScoreSummary, { detail: detail }), _jsx(Assessment, { detail: detail }), detail.showDetailedResults !== false && _jsx(AnswerSections, { reviewData: reviewData })] })] }), afterReportSlot] }) }));
66
- }
67
- function ScoreSummary({ detail }) {
68
- return _jsxs("div", { className: "grid gap-3 md:grid-cols-4", children: [_jsx(Metric, { label: "T\u1ED5ng \u0111i\u1EC3m", value: `${formatScore(detail.totalScore)} / ${formatScore(detail.maxScore)}` }), _jsx(Metric, { label: "C\u00E2u \u0111\u00FAng", value: `${detail.correctAnswers} / ${detail.totalQuestions}` }), _jsx(Metric, { label: "X\u1EBFp h\u1EA1ng", value: detail.rank ? `${detail.rank}/${detail.totalStudents ?? '-'}` : '-' }), _jsx(Metric, { label: "Tr\u1EA1ng th\u00E1i", value: detail.isPendingGrading ? 'Đang chấm' : 'Hoàn tất' })] });
69
- }
70
- function Assessment({ detail }) {
71
- if (!detail.llmOverallAssessment && !detail.llmSkillAssessment)
72
- return null;
73
- return _jsxs("section", { className: "rounded-2xl border border-[#00AEEF]/30 bg-[#f0f8ff] p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2 font-bold text-[#0054A6]", children: [_jsx(Sparkles, { className: "h-5 w-5" }), "Ph\u00E2n t\u00EDch t\u1ED5ng quan"] }), detail.llmOverallAssessment && _jsx("p", { className: "whitespace-pre-wrap text-sm text-slate-700", children: detail.llmOverallAssessment }), detail.llmSkillAssessment && _jsx("div", { className: "mt-3 grid gap-2 md:grid-cols-2", children: Object.entries(detail.llmSkillAssessment).map(([key, value]) => value ? _jsxs("div", { className: "rounded-xl bg-white p-3 text-sm", children: [_jsx("strong", { children: value.label }), _jsx("p", { className: "mt-1 text-slate-600", children: value.feedback })] }, key) : null) })] });
74
- }
75
- function AnswerSections({ reviewData }) {
76
- return _jsx("div", { className: "space-y-6", children: reviewData.parts.map((part, index) => _jsxs("section", { className: "rounded-2xl border border-[#00AEEF] bg-white p-5", children: [_jsxs("div", { className: "mb-4 flex items-center justify-between", children: [_jsx("h3", { className: "text-lg font-bold text-[#0054A6]", children: part.name || `Phần ${part.partNo}` }), reviewData.partScoresMap[index] && _jsxs("span", { className: "rounded-full bg-[#0054A6] px-3 py-1 text-sm font-bold text-white", children: [formatScore(reviewData.partScoresMap[index].score), " / ", formatScore(reviewData.partScoresMap[index].maxScore)] })] }), part.instructions && _jsx("p", { className: "mb-4 font-semibold text-slate-700", children: part.instructions }), _jsx(ReviewQuestionRenderer, { part: part, reviewData: reviewData })] }, `${part.partId}-${index}`)) });
77
- }
78
- function Metric({ label, value }) {
79
- return _jsxs("div", { className: "rounded-2xl bg-slate-50 p-4", children: [_jsx("p", { className: "text-xs font-semibold uppercase text-slate-500", children: label }), _jsx("p", { className: "mt-1 text-xl font-black text-[#0054A6]", children: value })] });
80
- }
81
- function State({ icon, title, action }) {
82
- return _jsx("div", { className: "flex min-h-screen items-center justify-center bg-[#e0e0e0]", children: _jsxs("div", { className: "rounded-[28px] bg-white p-8 text-center shadow-xl", children: [_jsx("div", { className: "mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-slate-50", children: icon }), _jsx("p", { className: "font-semibold text-slate-700", children: title }), action && _jsx("div", { className: "mt-5", children: action })] }) });
83
- }
84
- function formatScore(score) {
85
- if (score == null || !Number.isFinite(score))
86
- return '0';
87
- return score < 10 ? score.toFixed(1) : String(Math.round(score));
88
- }
89
- function downloadBlob(blob, filename) {
90
- const url = URL.createObjectURL(blob);
91
- const link = document.createElement('a');
92
- link.href = url;
93
- link.download = filename;
94
- document.body.appendChild(link);
95
- link.click();
96
- document.body.removeChild(link);
97
- URL.revokeObjectURL(url);
98
- }
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useEffect as a,useMemo as s,useState as l}from"react";import{ChevronLeft as n,Download as r,Loader2 as i,Sparkles as c,XCircle as o}from"lucide-react";import{Button as m}from"../../components/ui/button";import{Card as d,CardContent as h,CardHeader as p,CardTitle as u}from"../../components/ui/card";import{useToast as x}from"../../shared/lib/hooks/useToast";import{transformResultAnswersForReview as f}from"./review-data";import{ReviewQuestionRenderer as b}from"./ReviewQuestionRenderer";import{unwrap as g}from"./result-review.utils";export function ResultReviewPageContainer({attemptId:c,token:b,api:y,onBack:A,onNavigate:S,topActionsSlot:R,beforeReportSlot:E,afterReportSlot:P}){const{error:j,success:$}=x(),[D,F]=l(null),[C,O]=l(null),[q,U]=l(!0),[K,L]=l(!1);a(()=>{let e=!1;return U(!0),Promise.all([y.getDetailedResult(c,b),y.getAttempt?.(c,b)]).then(([t,a])=>{e||(F(g(t)),O(a?g(a):null))}).catch(e=>j({message:"Không thể tải kết quả",description:e instanceof Error?e.message:void 0})).finally(()=>{e||U(!1)}),()=>{e=!0}},[y,c,b,j]);const M=s(()=>f(D?.answers??[],C?.exam?.template?.parts),[D?.answers,C?.exam?.template?.parts]),Q=C?.studentName??"Học sinh",T=C?.studentUsername,X=C?.branchName??"Chưa tham gia chi nhánh",H=C?.exam?.template?.title??D?.examName??"Kết quả bài thi",I=()=>{A?A():S?S("/student/results"):"undefined"!=typeof window&&window.history.back()},B=async()=>{if(D&&y.exportPdf){L(!0);try{const{blob:e,filename:t}=await y.exportPdf(c,C?.exam?.template?.type);!function(e,t){const a=URL.createObjectURL(e),s=document.createElement("a");s.href=a,s.download=t,document.body.appendChild(s),s.click(),document.body.removeChild(s),URL.revokeObjectURL(a)}(e,t),$({message:"Xuất PDF thành công"})}catch(e){j({message:"Không xuất được PDF",description:e instanceof Error?e.message:void 0})}finally{L(!1)}}};return q?e(k,{icon:e(i,{className:"h-8 w-8 animate-spin text-[#2874A6]"}),title:"Đang tải kết quả bài thi..."}):D?e("div",{className:"min-h-screen bg-[#e0e0e0] py-4 md:py-6",children:t("div",{className:"mx-auto flex w-full max-w-[1160px] flex-col gap-4 px-3 sm:px-4",children:[t("div",{className:"flex items-center justify-between gap-3 print:hidden",children:[t(m,{variant:"outline",onClick:I,children:[e(n,{className:"mr-2 h-4 w-4"}),"Quay lại"]}),t("div",{className:"flex items-center gap-2",children:[y.exportPdf&&t(m,{onClick:()=>{B()},disabled:K,className:"bg-[#00AEEF] hover:bg-[#0099D6]",children:[K?e(i,{className:"mr-2 h-4 w-4 animate-spin"}):e(r,{className:"mr-2 h-4 w-4"}),"Xuất PDF"]}),R]})]}),E,t(d,{className:"overflow-hidden rounded-[28px] border-0 shadow-xl",children:[t(p,{className:"bg-gradient-to-r from-[#0054A6] to-[#00AEEF] text-white",children:[e(u,{className:"text-2xl",children:H}),t("p",{className:"text-sm text-white/80",children:[Q,T?` • ${T}`:""," • ",X]})]}),t(h,{className:"space-y-6 p-5 md:p-8",children:[e(N,{detail:D}),e(w,{detail:D}),!1!==D.showDetailedResults&&e(v,{reviewData:M})]})]}),P]})}):e(k,{icon:e(o,{className:"h-8 w-8 text-red-400"}),title:"Không thể tải kết quả",action:e(m,{onClick:I,children:"Quay lại"})})}function N({detail:a}){return t("div",{className:"grid gap-3 md:grid-cols-4",children:[e(y,{label:"Tổng điểm",value:`${A(a.totalScore)} / ${A(a.maxScore)}`}),e(y,{label:"Câu đúng",value:`${a.correctAnswers} / ${a.totalQuestions}`}),e(y,{label:"Xếp hạng",value:a.rank?`${a.rank}/${a.totalStudents??"-"}`:"-"}),e(y,{label:"Trạng thái",value:a.isPendingGrading?"Đang chấm":"Hoàn tất"})]})}function w({detail:a}){return a.llmOverallAssessment||a.llmSkillAssessment?t("section",{className:"rounded-2xl border border-[#00AEEF]/30 bg-[#f0f8ff] p-4",children:[t("div",{className:"mb-2 flex items-center gap-2 font-bold text-[#0054A6]",children:[e(c,{className:"h-5 w-5"}),"Phân tích tổng quan"]}),a.llmOverallAssessment&&e("p",{className:"whitespace-pre-wrap text-sm text-slate-700",children:a.llmOverallAssessment}),a.llmSkillAssessment&&e("div",{className:"mt-3 grid gap-2 md:grid-cols-2",children:Object.entries(a.llmSkillAssessment).map(([a,s])=>s?t("div",{className:"rounded-xl bg-white p-3 text-sm",children:[e("strong",{children:s.label}),e("p",{className:"mt-1 text-slate-600",children:s.feedback})]},a):null)})]}):null}function v({reviewData:a}){return e("div",{className:"space-y-6",children:a.parts.map((s,l)=>t("section",{className:"rounded-2xl border border-[#00AEEF] bg-white p-5",children:[t("div",{className:"mb-4 flex items-center justify-between",children:[e("h3",{className:"text-lg font-bold text-[#0054A6]",children:s.name||`Phần ${s.partNo}`}),a.partScoresMap[l]&&t("span",{className:"rounded-full bg-[#0054A6] px-3 py-1 text-sm font-bold text-white",children:[A(a.partScoresMap[l].score)," / ",A(a.partScoresMap[l].maxScore)]})]}),s.instructions&&e("p",{className:"mb-4 font-semibold text-slate-700",children:s.instructions}),e(b,{part:s,reviewData:a})]},`${s.partId}-${l}`))})}function y({label:a,value:s}){return t("div",{className:"rounded-2xl bg-slate-50 p-4",children:[e("p",{className:"text-xs font-semibold uppercase text-slate-500",children:a}),e("p",{className:"mt-1 text-xl font-black text-[#0054A6]",children:s})]})}function k({icon:a,title:s,action:l}){return e("div",{className:"flex min-h-screen items-center justify-center bg-[#e0e0e0]",children:t("div",{className:"rounded-[28px] bg-white p-8 text-center shadow-xl",children:[e("div",{className:"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-slate-50",children:a}),e("p",{className:"font-semibold text-slate-700",children:s}),l&&e("div",{className:"mt-5",children:l})]})})}function A(e){return null!=e&&Number.isFinite(e)?e<10?e.toFixed(1):String(Math.round(e)):"0"}