@tinyweb_dev/oe-exam-sdk 0.1.1 → 0.1.2

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 (485) 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.js +1 -98
  4. package/dist/api/exam-questions.types.js +1 -1
  5. package/dist/api/exam-taking.js +1 -73
  6. package/dist/api/exam-taking.types.js +1 -1
  7. package/dist/api/index.js +1 -6
  8. package/dist/api/result-review.js +1 -50
  9. package/dist/api/result-review.types.js +1 -1
  10. package/dist/components/CueCardEditor.js +1 -58
  11. package/dist/components/common/ResolvedImage.js +1 -15
  12. package/dist/components/exams/CreateExamInfoForm.js +1 -35
  13. package/dist/components/exams/CreateExamPageContainer.js +1 -103
  14. package/dist/components/exams/ExamCreator.js +1 -89
  15. package/dist/components/exams/ExamPartTabs.js +1 -40
  16. package/dist/components/exams/ExamQuestionGrid.js +1 -18
  17. package/dist/components/exams/ExamQuestionsPageContainer.js +1 -90
  18. package/dist/components/exams/PartAudioUploader.js +1 -9
  19. package/dist/components/exams/TemplateSelector.js +1 -66
  20. package/dist/components/exams/api.js +1 -3
  21. package/dist/components/exams/create-exam.validation.js +1 -18
  22. package/dist/components/exams/exam-template.utils.js +1 -18
  23. package/dist/components/exams/index.js +1 -10
  24. package/dist/components/exams/take/ExamTakingPageContainer.js +1 -153
  25. package/dist/components/exams/take/GenericQuestionRenderer.js +1 -14
  26. package/dist/components/exams/take/exam-taking.utils.js +1 -88
  27. package/dist/components/exams/take/hooks/useSpeakingRecorder.js +1 -24
  28. package/dist/components/exams/take/index.js +1 -2
  29. package/dist/components/exams/take/types.js +1 -1
  30. package/dist/components/index.js +1 -42
  31. package/dist/components/questions/_shared/config/question-types.config.js +1 -333
  32. package/dist/components/questions/_shared/constants/color-palette.js +1 -21
  33. package/dist/components/questions/_shared/hooks/useAnswerGroupQuestionGroup.js +1 -105
  34. package/dist/components/questions/_shared/hooks/useBasicQuestionGroup.js +1 -222
  35. package/dist/components/questions/_shared/hooks/useCompareImagesGroup.js +1 -140
  36. package/dist/components/questions/_shared/hooks/useImageQuestionGroup.js +1 -123
  37. package/dist/components/questions/_shared/hooks/useInfoExchangeGroup.js +1 -50
  38. package/dist/components/questions/_shared/hooks/useQuestionFormState.js +1 -54
  39. package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +1 -123
  40. package/dist/components/questions/_shared/hooks/useQuestionListGroup.js +1 -121
  41. package/dist/components/questions/_shared/hooks/useQuestionOnChange.js +1 -70
  42. package/dist/components/questions/_shared/hooks/useSceneQuestionGroup.js +1 -194
  43. package/dist/components/questions/_shared/index.js +1 -7
  44. package/dist/components/questions/_shared/types/answer-the-question.type.js +1 -1
  45. package/dist/components/questions/_shared/types/arrange-letters-into-words.type.js +1 -1
  46. package/dist/components/questions/_shared/types/choose-correct-adjective.type.js +1 -3
  47. package/dist/components/questions/_shared/types/color-region.config.js +1 -1
  48. package/dist/components/questions/_shared/types/fill-in-blank.type.js +1 -1
  49. package/dist/components/questions/_shared/types/fill-missing-words-in-grid.type.js +1 -1
  50. package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +1 -1
  51. package/dist/components/questions/_shared/types/index.js +1 -12
  52. package/dist/components/questions/_shared/types/label-the-picture.type.js +1 -1
  53. package/dist/components/questions/_shared/types/listen-and-choose-from-answer-group.type.js +1 -1
  54. package/dist/components/questions/_shared/types/listen-and-choose-objects-in-scene.type.js +1 -1
  55. package/dist/components/questions/_shared/types/listen-and-drag-objects-into-scene.type.js +1 -1
  56. package/dist/components/questions/_shared/types/listen-and-speak-answer.type.js +1 -1
  57. package/dist/components/questions/_shared/types/listen-and-speak-compare-images.type.js +1 -1
  58. package/dist/components/questions/_shared/types/listen-and-speak-image-group.type.js +1 -1
  59. package/dist/components/questions/_shared/types/listen-and-speak-info-exchange.type.js +1 -1
  60. package/dist/components/questions/_shared/types/listen-and-speak-odd-one-out.type.js +1 -3
  61. package/dist/components/questions/_shared/types/listen-and-speak-question-list.type.js +1 -1
  62. package/dist/components/questions/_shared/types/listen-and-speak-with-story-images.type.js +1 -3
  63. package/dist/components/questions/_shared/types/listen-and-tick-answer.type.js +1 -1
  64. package/dist/components/questions/_shared/types/listening-drawline.type.js +1 -1
  65. package/dist/components/questions/_shared/types/look-and-choose-answer.type.js +1 -1
  66. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +1 -1
  67. package/dist/components/questions/_shared/types/match-by-writing-answer.type.js +1 -1
  68. package/dist/components/questions/_shared/types/multiple-choice.type.js +1 -1
  69. package/dist/components/questions/_shared/types/question-group.type.js +1 -11
  70. package/dist/components/questions/_shared/types/read-and-color-objects.type.js +1 -1
  71. package/dist/components/questions/_shared/types/read-displayed-content.type.js +1 -1
  72. package/dist/components/questions/_shared/types/read-passage-and-answer-questions.type.js +1 -1
  73. package/dist/components/questions/_shared/types/speaking-describe-image.type.js +1 -1
  74. package/dist/components/questions/_shared/types/spontaneous-qa.type.js +1 -1
  75. package/dist/components/questions/_shared/types/word-fill-paragraph-with-options.type.js +1 -1
  76. package/dist/components/questions/_shared/types/word-fill-paragraph.type.js +1 -1
  77. package/dist/components/questions/_shared/types/word-fill-structured-form.type.js +1 -1
  78. package/dist/components/questions/_shared/types/word-ordering.type.js +1 -1
  79. package/dist/components/questions/_shared/types/write-a-short-letter.type.js +1 -1
  80. package/dist/components/questions/_shared/types/write-correct-verb-form.type.js +1 -3
  81. package/dist/components/questions/_shared/types/write-sentences.type.js +1 -1
  82. package/dist/components/questions/_shared/types/write-short-paragraph.type.js +1 -1
  83. package/dist/components/questions/_shared/utils/answer-builder.js +1 -79
  84. package/dist/components/questions/_shared/utils/createColorRegionRegistration.js +1 -39
  85. package/dist/components/questions/_shared/utils/id-generator.js +1 -3
  86. package/dist/components/questions/_shared/utils/question-validation.js +1 -21
  87. package/dist/components/questions/components/ColorNode.js +1 -85
  88. package/dist/components/questions/components/ColorRegionClient.js +1 -545
  89. package/dist/components/questions/components/ColorRegionCreator.js +1 -420
  90. package/dist/components/questions/components/ColorRegionNode.js +1 -59
  91. package/dist/components/questions/components/CreatorGuide.js +1 -103
  92. package/dist/components/questions/components/NameNode.js +1 -26
  93. package/dist/components/questions/components/QuestionBankOption.js +1 -46
  94. package/dist/components/questions/components/QuestionBasicInfoForm.js +1 -9
  95. package/dist/components/questions/components/QuestionCommonFields.js +1 -33
  96. package/dist/components/questions/components/QuestionSearchDropdown.js +1 -258
  97. package/dist/components/questions/components/QuestionTypeSelector.js +1 -32
  98. package/dist/components/questions/components/QuestionTypeSpecificForms.js +1 -6
  99. package/dist/components/questions/components/RegionNode.js +1 -94
  100. package/dist/components/questions/components/index.js +1 -14
  101. package/dist/components/questions/creator/QuestionCreator.js +1 -90
  102. package/dist/components/questions/creator/QuestionGroupCreator.js +1 -119
  103. package/dist/components/questions/creator/dedicated-component-router.js +1 -79
  104. package/dist/components/questions/creator/index.js +1 -3
  105. package/dist/components/questions/creator/question-type-registry.js +1 -80
  106. package/dist/components/questions/groups/ArticlesGroupCard.js +1 -121
  107. package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -50
  108. package/dist/components/questions/groups/DocumentGroupCard.js +1 -114
  109. package/dist/components/questions/groups/MatchAnswerGroupCard.js +1 -25
  110. package/dist/components/questions/index.js +1 -10
  111. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +1 -37
  112. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +1 -351
  113. package/dist/components/questions/types/answer-the-question/register.js +1 -67
  114. package/dist/components/questions/types/answer-the-question/transform.js +1 -36
  115. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +1 -141
  116. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +1 -118
  117. package/dist/components/questions/types/arrange-letters-into-words/index.js +1 -2
  118. package/dist/components/questions/types/arrange-letters-into-words/register.js +1 -4
  119. package/dist/components/questions/types/arrange-letters-into-words/transform.js +1 -24
  120. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -64
  121. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +1 -169
  122. package/dist/components/questions/types/choose-correct-adjective/index.js +1 -2
  123. package/dist/components/questions/types/choose-correct-adjective/register.js +1 -55
  124. package/dist/components/questions/types/choose-correct-adjective/transform.js +1 -16
  125. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +1 -131
  126. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +1 -581
  127. package/dist/components/questions/types/choose-the-correct-answer/index.js +1 -2
  128. package/dist/components/questions/types/choose-the-correct-answer/register.js +1 -101
  129. package/dist/components/questions/types/choose-the-correct-answer/transform.js +1 -43
  130. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +1 -275
  131. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +1 -448
  132. package/dist/components/questions/types/fill-in-blank/index.js +1 -2
  133. package/dist/components/questions/types/fill-in-blank/register.js +1 -138
  134. package/dist/components/questions/types/fill-in-blank/transform.js +1 -59
  135. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +1 -117
  136. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +1 -671
  137. package/dist/components/questions/types/fill-missing-words-in-grid/index.js +1 -2
  138. package/dist/components/questions/types/fill-missing-words-in-grid/register.js +1 -37
  139. package/dist/components/questions/types/fill-missing-words-in-grid/transform.js +1 -15
  140. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +1 -262
  141. package/dist/components/questions/types/gn-speaking-interview/register.js +1 -121
  142. package/dist/components/questions/types/gn-speaking-interview/transform.js +1 -48
  143. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingClient.js +1 -16
  144. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingCreator.js +1 -27
  145. package/dist/components/questions/types/image-object-matching/index.js +1 -2
  146. package/dist/components/questions/types/image-object-matching/register.js +1 -3
  147. package/dist/components/questions/types/image-object-matching/transform.js +1 -2
  148. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +1 -102
  149. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +1 -344
  150. package/dist/components/questions/types/label-the-picture/index.js +1 -2
  151. package/dist/components/questions/types/label-the-picture/register.js +1 -54
  152. package/dist/components/questions/types/label-the-picture/transform.js +1 -28
  153. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +1 -78
  154. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +1 -232
  155. package/dist/components/questions/types/listen-and-choose-from-answer-group/index.js +1 -2
  156. package/dist/components/questions/types/listen-and-choose-from-answer-group/register.js +1 -82
  157. package/dist/components/questions/types/listen-and-choose-from-answer-group/transform.js +1 -27
  158. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +1 -70
  159. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +1 -386
  160. package/dist/components/questions/types/listen-and-choose-objects-in-scene/index.js +1 -2
  161. package/dist/components/questions/types/listen-and-choose-objects-in-scene/register.js +1 -100
  162. package/dist/components/questions/types/listen-and-choose-objects-in-scene/transform.js +1 -33
  163. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +1 -106
  164. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +1 -457
  165. package/dist/components/questions/types/listen-and-drag-objects-into-scene/index.js +1 -2
  166. package/dist/components/questions/types/listen-and-drag-objects-into-scene/register.js +1 -110
  167. package/dist/components/questions/types/listen-and-drag-objects-into-scene/transform.js +1 -35
  168. package/dist/components/questions/types/listen-and-speak-answer/ArrowIndicator.js +1 -8
  169. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -22
  170. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +1 -291
  171. package/dist/components/questions/types/listen-and-speak-answer/index.js +1 -2
  172. package/dist/components/questions/types/listen-and-speak-answer/register.js +1 -84
  173. package/dist/components/questions/types/listen-and-speak-answer/transform.js +1 -27
  174. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -32
  175. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +1 -295
  176. package/dist/components/questions/types/listen-and-speak-compare-images/register.js +1 -79
  177. package/dist/components/questions/types/listen-and-speak-compare-images/transform.js +1 -27
  178. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -34
  179. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +1 -285
  180. package/dist/components/questions/types/listen-and-speak-image-group/register.js +1 -81
  181. package/dist/components/questions/types/listen-and-speak-image-group/transform.js +1 -26
  182. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -38
  183. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +1 -385
  184. package/dist/components/questions/types/listen-and-speak-info-exchange/register.js +1 -87
  185. package/dist/components/questions/types/listen-and-speak-info-exchange/transform.js +1 -32
  186. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -26
  187. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +1 -247
  188. package/dist/components/questions/types/listen-and-speak-odd-one-out/register.js +1 -59
  189. package/dist/components/questions/types/listen-and-speak-odd-one-out/transform.js +1 -18
  190. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -26
  191. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +1 -256
  192. package/dist/components/questions/types/listen-and-speak-question-list/register.js +1 -81
  193. package/dist/components/questions/types/listen-and-speak-question-list/transform.js +1 -26
  194. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -26
  195. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +1 -230
  196. package/dist/components/questions/types/listen-and-speak-with-story-images/register.js +1 -56
  197. package/dist/components/questions/types/listen-and-speak-with-story-images/transform.js +1 -16
  198. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +1 -72
  199. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +1 -230
  200. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +1 -1
  201. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +1 -66
  202. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +1 -42
  203. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +1 -96
  204. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +1 -238
  205. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +1 -2
  206. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +1 -76
  207. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +1 -19
  208. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +1 -134
  209. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +1 -617
  210. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +1 -2
  211. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +1 -82
  212. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +1 -29
  213. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +1 -38
  214. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +1 -359
  215. package/dist/components/questions/types/match-by-writing-answer/index.js +1 -3
  216. package/dist/components/questions/types/match-by-writing-answer/register.js +1 -58
  217. package/dist/components/questions/types/match-by-writing-answer/transform.js +1 -61
  218. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsClient.js +1 -16
  219. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsCreator.js +1 -23
  220. package/dist/components/questions/types/read-and-color-objects/index.js +1 -2
  221. package/dist/components/questions/types/read-and-color-objects/register.js +1 -3
  222. package/dist/components/questions/types/read-and-color-objects/transform.js +1 -36
  223. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +1 -125
  224. package/dist/components/questions/types/read-displayed-content/register.js +1 -64
  225. package/dist/components/questions/types/read-displayed-content/transform.js +1 -12
  226. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -64
  227. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +1 -511
  228. package/dist/components/questions/types/read-passage-and-answer-questions/index.js +1 -2
  229. package/dist/components/questions/types/read-passage-and-answer-questions/register.js +1 -58
  230. package/dist/components/questions/types/read-passage-and-answer-questions/transform.js +1 -28
  231. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +1 -373
  232. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +1 -41
  233. package/dist/components/questions/types/speaking-conversation/register.js +1 -110
  234. package/dist/components/questions/types/speaking-conversation/transform.js +1 -34
  235. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +1 -74
  236. package/dist/components/questions/types/speaking-cue-card/register.js +1 -52
  237. package/dist/components/questions/types/speaking-cue-card/transform.js +1 -12
  238. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -10
  239. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +1 -161
  240. package/dist/components/questions/types/speaking-describe-image/index.js +1 -2
  241. package/dist/components/questions/types/speaking-describe-image/register.js +1 -69
  242. package/dist/components/questions/types/speaking-describe-image/transform.js +1 -16
  243. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +1 -234
  244. package/dist/components/questions/types/spontaneous-qa/register.js +1 -93
  245. package/dist/components/questions/types/spontaneous-qa/transform.js +1 -34
  246. package/dist/components/questions/types/true-false/TrueFalseClient.js +1 -75
  247. package/dist/components/questions/types/true-false/TrueFalseCreator.js +1 -244
  248. package/dist/components/questions/types/true-false/index.js +1 -2
  249. package/dist/components/questions/types/true-false/register.js +1 -77
  250. package/dist/components/questions/types/true-false/transform.js +1 -32
  251. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +1 -233
  252. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +1 -631
  253. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphWrapper.js +1 -28
  254. package/dist/components/questions/types/word-fill-paragraph/index.js +1 -2
  255. package/dist/components/questions/types/word-fill-paragraph/register.js +1 -136
  256. package/dist/components/questions/types/word-fill-paragraph/transform.js +1 -56
  257. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -138
  258. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +1 -261
  259. package/dist/components/questions/types/word-fill-structured-form/index.js +1 -2
  260. package/dist/components/questions/types/word-fill-structured-form/register.js +1 -67
  261. package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -40
  262. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -84
  263. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +1 -192
  264. package/dist/components/questions/types/word-ordering/index.js +1 -2
  265. package/dist/components/questions/types/word-ordering/register.js +1 -46
  266. package/dist/components/questions/types/word-ordering/transform.js +1 -24
  267. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +1 -98
  268. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +1 -320
  269. package/dist/components/questions/types/write-a-short-letter/index.js +1 -2
  270. package/dist/components/questions/types/write-a-short-letter/register.js +1 -68
  271. package/dist/components/questions/types/write-a-short-letter/transform.js +1 -25
  272. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +1 -61
  273. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +1 -196
  274. package/dist/components/questions/types/write-correct-verb-form/index.js +1 -2
  275. package/dist/components/questions/types/write-correct-verb-form/register.js +1 -62
  276. package/dist/components/questions/types/write-correct-verb-form/transform.js +1 -17
  277. package/dist/components/questions/types/write-sentences/WriteSentencesClient.js +1 -22
  278. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +1 -174
  279. package/dist/components/questions/types/write-sentences/index.js +1 -2
  280. package/dist/components/questions/types/write-sentences/register.js +1 -58
  281. package/dist/components/questions/types/write-sentences/transform.js +1 -28
  282. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -96
  283. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +1 -161
  284. package/dist/components/questions/types/write-short-paragraph/index.js +1 -2
  285. package/dist/components/questions/types/write-short-paragraph/register.js +1 -4
  286. package/dist/components/questions/types/write-short-paragraph/transform.js +1 -21
  287. package/dist/components/questions/viewer/QuestionGroupViewer.js +1 -36
  288. package/dist/components/questions/viewer/QuestionViewer.js +1 -446
  289. package/dist/components/questions/viewer/index.js +1 -2
  290. package/dist/components/results/ResultReviewPageContainer.js +1 -98
  291. package/dist/components/results/ReviewQuestionRenderer.js +1 -72
  292. package/dist/components/results/index.js +1 -2
  293. package/dist/components/results/result-review.utils.js +1 -6
  294. package/dist/components/results/review-data.js +1 -150
  295. package/dist/components/results/review-renderer.utils.js +1 -97
  296. package/dist/components/results/review-types.js +1 -1
  297. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +1 -138
  298. package/dist/components/themes/cambridge-yle/CambridgeYleBottomNav.js +1 -12
  299. package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.js +1 -18
  300. package/dist/components/themes/cambridge-yle/CambridgeYleExampleText.js +1 -11
  301. package/dist/components/themes/cambridge-yle/CambridgeYleHeader.js +1 -11
  302. package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.js +1 -9
  303. package/dist/components/themes/cambridge-yle/CambridgeYleNavButton.js +1 -13
  304. package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.js +1 -8
  305. package/dist/components/themes/cambridge-yle/CambridgeYleProgressBar.js +1 -6
  306. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCard.js +1 -5
  307. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCardFullWidth.js +1 -9
  308. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.js +1 -41
  309. package/dist/components/themes/cambridge-yle/CambridgeYleTimer.js +1 -25
  310. package/dist/components/themes/cambridge-yle/index.js +1 -13
  311. package/dist/components/themes/english-certification/EcAnswerTheQuestion.js +1 -24
  312. package/dist/components/themes/english-certification/EcMatchByWritingAnswer.js +1 -88
  313. package/dist/components/themes/english-certification/EcWordFillParagraph.js +1 -55
  314. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -156
  315. package/dist/components/themes/english-certification/EcWritingTask.js +1 -34
  316. package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +1 -114
  317. package/dist/components/themes/english-certification/EnglishCertificationBrand.js +1 -11
  318. package/dist/components/themes/english-certification/EnglishCertificationExamLayout.js +1 -21
  319. package/dist/components/themes/english-certification/EnglishCertificationFlagButton.js +1 -15
  320. package/dist/components/themes/english-certification/EnglishCertificationFooterBar.js +1 -19
  321. package/dist/components/themes/english-certification/EnglishCertificationGroupedQuestion.js +1 -148
  322. package/dist/components/themes/english-certification/EnglishCertificationHeader.js +1 -31
  323. package/dist/components/themes/english-certification/EnglishCertificationNavigationPanel.js +1 -71
  324. package/dist/components/themes/english-certification/EnglishCertificationOptionSelector.js +1 -33
  325. package/dist/components/themes/english-certification/EnglishCertificationPassagePanel.js +1 -16
  326. package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.js +1 -22
  327. package/dist/components/themes/english-certification/EnglishCertificationQuestionItem.js +1 -37
  328. package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +1 -72
  329. package/dist/components/themes/english-certification/EnglishCertificationReadingSection.js +1 -90
  330. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestion.js +1 -18
  331. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js +1 -29
  332. package/dist/components/themes/english-certification/english-certification-theme.js +1 -58
  333. package/dist/components/themes/english-certification/grouped-question-utils.js +1 -218
  334. package/dist/components/themes/english-certification/index.js +1 -21
  335. package/dist/components/themes/index.js +1 -6
  336. package/dist/components/themes/summer-sky/SummerSkyAudioPlayer.js +1 -170
  337. package/dist/components/themes/summer-sky/SummerSkyLayout.js +1 -221
  338. package/dist/components/themes/summer-sky/SummerSkySubmitModal.js +1 -18
  339. package/dist/components/themes/summer-sky/SummerSkyWaitingRoom.js +1 -194
  340. package/dist/components/themes/types.js +1 -1
  341. package/dist/components/ui/alert-dialog.js +1 -26
  342. package/dist/components/ui/alert.js +1 -22
  343. package/dist/components/ui/avatar.js +1 -12
  344. package/dist/components/ui/badge.js +1 -38
  345. package/dist/components/ui/button.js +1 -35
  346. package/dist/components/ui/calendar.js +1 -76
  347. package/dist/components/ui/card.js +1 -16
  348. package/dist/components/ui/carousel.js +1 -91
  349. package/dist/components/ui/checkbox.js +1 -9
  350. package/dist/components/ui/collapsible.js +1 -13
  351. package/dist/components/ui/command.js +1 -34
  352. package/dist/components/ui/confirm-dialog.js +1 -49
  353. package/dist/components/ui/dialog.js +1 -23
  354. package/dist/components/ui/drawer.js +1 -35
  355. package/dist/components/ui/dropdown-menu.js +1 -33
  356. package/dist/components/ui/file-upload.js +1 -155
  357. package/dist/components/ui/form.js +1 -62
  358. package/dist/components/ui/hover-card.js +1 -10
  359. package/dist/components/ui/image-preview.js +1 -29
  360. package/dist/components/ui/input.js +1 -8
  361. package/dist/components/ui/label.js +1 -9
  362. package/dist/components/ui/language-switcher.js +1 -15
  363. package/dist/components/ui/multi-select.js +1 -90
  364. package/dist/components/ui/points-input.js +1 -33
  365. package/dist/components/ui/popover.js +1 -26
  366. package/dist/components/ui/progress.js +1 -16
  367. package/dist/components/ui/radio-group.js +1 -12
  368. package/dist/components/ui/scroll-area.js +1 -11
  369. package/dist/components/ui/select.js +1 -38
  370. package/dist/components/ui/separator.js +1 -8
  371. package/dist/components/ui/sheet.js +1 -40
  372. package/dist/components/ui/single-select.js +1 -76
  373. package/dist/components/ui/skeleton.js +1 -6
  374. package/dist/components/ui/switch.js +1 -8
  375. package/dist/components/ui/table.js +1 -20
  376. package/dist/components/ui/tabs.js +1 -17
  377. package/dist/components/ui/textarea.js +1 -8
  378. package/dist/components/ui/tooltip.js +1 -11
  379. package/dist/index.js +1 -19
  380. package/dist/mcp/config.js +1 -32
  381. package/dist/mcp/http-client.js +1 -48
  382. package/dist/mcp/index.js +1 -29
  383. package/dist/mcp/tools/_helpers.js +1 -25
  384. package/dist/mcp/tools/exams.js +1 -439
  385. package/dist/mcp/tools/index.js +1 -6
  386. package/dist/mcp/tools/results.js +1 -19
  387. package/dist/shared/constants/ApiConstant.js +1 -276
  388. package/dist/shared/constants/QueryKeyConstant.js +1 -112
  389. package/dist/shared/constants/exam-level.enum.js +1 -41
  390. package/dist/shared/constants/landings/placement-test.constants.js +1 -431
  391. package/dist/shared/constants/question-skills.js +1 -267
  392. package/dist/shared/constants/routes.js +1 -81
  393. package/dist/shared/index.js +1 -7
  394. package/dist/shared/lib/hooks/index.js +1 -10
  395. package/dist/shared/lib/hooks/useAlertDialog.js +1 -57
  396. package/dist/shared/lib/hooks/useAudioPlayCounter.js +1 -30
  397. package/dist/shared/lib/hooks/useAudioPreview.js +1 -81
  398. package/dist/shared/lib/hooks/useAudioUpload.js +1 -24
  399. package/dist/shared/lib/hooks/useConfirmDialog.js +1 -89
  400. package/dist/shared/lib/hooks/useDebouncedCallback.js +1 -32
  401. package/dist/shared/lib/hooks/useDelayedAutoPlay.js +1 -12
  402. package/dist/shared/lib/hooks/useFileUpload.js +1 -24
  403. package/dist/shared/lib/hooks/useImageUrl.js +1 -5
  404. package/dist/shared/lib/hooks/usePresignedFileUrl.js +1 -10
  405. package/dist/shared/lib/hooks/useReactHookForm.js +1 -31
  406. package/dist/shared/lib/hooks/useToast.js +1 -62
  407. package/dist/shared/lib/i18n/index.js +1 -54
  408. package/dist/shared/lib/i18n/messages/en/admin.js +1 -2104
  409. package/dist/shared/lib/i18n/messages/en/auth.js +1 -86
  410. package/dist/shared/lib/i18n/messages/en/common.js +1 -140
  411. package/dist/shared/lib/i18n/messages/en/file-upload.js +1 -18
  412. package/dist/shared/lib/i18n/messages/en/student.js +1 -133
  413. package/dist/shared/lib/i18n/messages/en/terms.js +1 -37
  414. package/dist/shared/lib/i18n/messages/en.js +1 -15
  415. package/dist/shared/lib/i18n/messages/vi/admin.js +1 -2104
  416. package/dist/shared/lib/i18n/messages/vi/auth.js +1 -86
  417. package/dist/shared/lib/i18n/messages/vi/common.js +1 -140
  418. package/dist/shared/lib/i18n/messages/vi/file-upload.js +1 -18
  419. package/dist/shared/lib/i18n/messages/vi/student.js +1 -133
  420. package/dist/shared/lib/i18n/messages/vi/terms.js +1 -37
  421. package/dist/shared/lib/i18n/messages/vi.js +1 -15
  422. package/dist/shared/lib/i18n/types.js +1 -1
  423. package/dist/shared/lib/stores/examQuestionStore.js +1 -212
  424. package/dist/shared/lib/stores/localeStore.js +1 -10
  425. package/dist/shared/lib/utils/public-page.js +1 -44
  426. package/dist/shared/lib/utils/question-transform-types.js +1 -1
  427. package/dist/shared/lib/utils/question-transform.js +1 -174
  428. package/dist/shared/lib/utils.js +1 -168
  429. package/dist/shared/lib/validation/choose-correct-answer.validation.js +1 -111
  430. package/dist/shared/lib/validation/label-the-picture.validation.js +1 -19
  431. package/dist/shared/lib/validation/listen-and-choose-from-answer-group.validation.js +1 -46
  432. package/dist/shared/lib/validation/listen-and-choose-objects-in-scene.validation.js +1 -21
  433. package/dist/shared/lib/validation/listen-and-drag-objects-into-scene.validation.js +1 -34
  434. package/dist/shared/lib/validation/listen-and-speak-answer.validation.js +1 -40
  435. package/dist/shared/lib/validation/listen-and-speak-compare-images.validation.js +1 -45
  436. package/dist/shared/lib/validation/listen-and-speak-image-group.validation.js +1 -19
  437. package/dist/shared/lib/validation/listen-and-speak-info-exchange.validation.js +1 -39
  438. package/dist/shared/lib/validation/listen-and-speak-odd-one-out.validation.js +1 -32
  439. package/dist/shared/lib/validation/listen-and-speak-question-list.validation.js +1 -19
  440. package/dist/shared/lib/validation/listen-and-speak-with-story-images.validation.js +1 -28
  441. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +1 -12
  442. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +1 -47
  443. package/dist/shared/lib/validation/read-and-color-objects.validation.js +1 -29
  444. package/dist/shared/lib/validation/read-displayed-content.validation.js +1 -28
  445. package/dist/shared/lib/validation/speaking-conversation.validation.js +1 -75
  446. package/dist/shared/lib/validation/speaking-describe-image.validation.js +1 -31
  447. package/dist/shared/lib/validation/spontaneous-qa.validation.js +1 -48
  448. package/dist/shared/lib/validation/word-fill-paragraph-default.validation.js +1 -38
  449. package/dist/shared/lib/validation/word-fill-paragraph-with-options.validation.js +1 -39
  450. package/dist/shared/lib/validation/word-fill-paragraph.validation.js +1 -43
  451. package/dist/shared/lib/validation/word-ordering.validation.js +1 -33
  452. package/dist/shared/types/branch.types.js +1 -6
  453. package/dist/shared/types/common.types.js +1 -50
  454. package/dist/shared/types/entities/exam-schedule.types.js +1 -5
  455. package/dist/shared/types/entities/exam.types.js +1 -1
  456. package/dist/shared/types/entities/question.types.js +1 -4
  457. package/dist/shared/types/entities/result.types.js +1 -4
  458. package/dist/shared/types/entities/user.types.js +1 -4
  459. package/dist/shared/types/exam-taking.types.js +1 -2
  460. package/dist/shared/types/history.types.js +1 -34
  461. package/dist/shared/types/index.js +1 -1
  462. package/dist/shared/types/questions/choose-correct-adjective.js +1 -4
  463. package/dist/shared/types/questions/choose-the-correct-answer.js +1 -9
  464. package/dist/shared/types/questions/fill-missing-words-in-grid.js +1 -11
  465. package/dist/shared/types/questions/index.js +1 -40
  466. package/dist/shared/types/questions/listen-and-speak-answer.js +1 -1
  467. package/dist/shared/types/questions/listen-and-speak-compare-images.js +1 -1
  468. package/dist/shared/types/questions/listen-and-speak-image-group.js +1 -1
  469. package/dist/shared/types/questions/listen-and-speak-info-exchange.js +1 -3
  470. package/dist/shared/types/questions/listen-and-speak-odd-one-out.js +1 -3
  471. package/dist/shared/types/questions/listen-and-speak-question-list.js +1 -1
  472. package/dist/shared/types/questions/listen-and-speak-with-story-images.js +1 -3
  473. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +1 -5
  474. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +1 -9
  475. package/dist/shared/types/questions/look-picture-fill-word-hint.js +1 -7
  476. package/dist/shared/types/questions/read-displayed-content.js +1 -1
  477. package/dist/shared/types/questions/speaking-conversation.js +1 -7
  478. package/dist/shared/types/questions/speaking-describe-image.js +1 -1
  479. package/dist/shared/types/questions/spontaneous-qa.js +1 -1
  480. package/dist/shared/types/questions/write-correct-verb-form.js +1 -12
  481. package/dist/shared/types/role.types.js +1 -1
  482. package/dist/shared/types/structure-management.js +1 -28
  483. package/dist/shared/types/student.types.js +1 -54
  484. package/dist/shared/types/teacher-dashboard-rooms.js +1 -2
  485. package/package.json +4 -2
@@ -1,2 +1 @@
1
- export * from './LookPictureFillWordHintCreator';
2
- export * from './LookPictureFillWordHintClient';
1
+ export*from"./LookPictureFillWordHintCreator";export*from"./LookPictureFillWordHintClient";
@@ -1,82 +1 @@
1
- import { LookPictureFillWordHint } from './LookPictureFillWordHintCreator';
2
- export const lookPictureFillWordHintRegistration = {
3
- component: LookPictureFillWordHint,
4
- transformInitialData: (initialData) => {
5
- const content = initialData?.content;
6
- const answer = initialData?.answer;
7
- // Get hintLetters from various sources
8
- const hintLettersFromGroup = initialData?.questionGroup?.payload?.hintLetters;
9
- const hintLettersFromAnswer = answer?.hintLetters;
10
- const hintLettersFallback = content?.hintLetters;
11
- const hintLetters = hintLettersFromGroup || hintLettersFromAnswer || hintLettersFallback || [];
12
- const apiPoints = initialData?.total_points
13
- ? parseFloat(initialData.total_points)
14
- : (initialData?.points || 1);
15
- // PRIORITY 1: Store format - answer has subQuestions
16
- if (answer?.subQuestions && answer.subQuestions.length > 0) {
17
- return {
18
- subQuestions: answer.subQuestions,
19
- hintLetters: hintLetters.length > 0 ? hintLetters : answer.hintLetters || [],
20
- caseSensitive: answer?.caseSensitive ?? false,
21
- points: apiPoints,
22
- explanation: answer?.explanation || initialData?.explanation || '',
23
- };
24
- }
25
- // PRIORITY 2: API format - content has imageUrl/word
26
- else if (content && (content.imageUrl !== undefined || content.word !== undefined)) {
27
- return {
28
- content: {
29
- imageUrl: content.imageUrl || '',
30
- word: content.word || '',
31
- },
32
- answer: {
33
- correctAnswer: answer?.correctAnswer || '',
34
- caseSensitive: answer?.caseSensitive ?? false,
35
- hintLetters,
36
- explanation: answer?.explanation || initialData?.explanation || '',
37
- },
38
- hintLetters,
39
- caseSensitive: answer?.caseSensitive ?? false,
40
- points: apiPoints,
41
- explanation: answer?.explanation || initialData?.explanation || '',
42
- };
43
- }
44
- // PRIORITY 3: Fallback
45
- else {
46
- return {
47
- ...answer,
48
- hintLetters,
49
- caseSensitive: answer?.caseSensitive ?? false,
50
- points: apiPoints,
51
- explanation: answer?.explanation || initialData?.explanation || '',
52
- };
53
- }
54
- },
55
- getExtraProps: (ctx) => ({
56
- groups: ctx.groups,
57
- questionIndexInPart: ctx.questionIndexInPart,
58
- partId: ctx.partId,
59
- onUnsavedChangesChange: ctx.onUnsavedChangesChange,
60
- validationRef: ctx.validationRef,
61
- }),
62
- wrapOnSave: (data, ctx) => ({
63
- type: ctx.questionType,
64
- content: ctx.state.content,
65
- points: data?.points ?? ctx.state.points ?? 1,
66
- level: ctx.state.level,
67
- difficulty: ctx.state.difficulty,
68
- skill: ctx.state.skill,
69
- answer: data,
70
- explanation: data?.explanation || '',
71
- }),
72
- wrapOnChange: (data, ctx) => ({
73
- type: ctx.questionType,
74
- content: ctx.state.content,
75
- points: data?.points ?? ctx.state.points ?? 1,
76
- level: ctx.state.level,
77
- difficulty: ctx.state.difficulty,
78
- skill: ctx.state.skill,
79
- answer: data,
80
- explanation: data?.explanation || '',
81
- }),
82
- };
1
+ import{LookPictureFillWordHint as t}from"./LookPictureFillWordHintCreator";export const lookPictureFillWordHintRegistration={component:t,transformInitialData:t=>{const e=t?.content,n=t?.answer,i=t?.questionGroup?.payload?.hintLetters,a=n?.hintLetters,s=e?.hintLetters,o=i||a||s||[],l=t?.total_points?parseFloat(t.total_points):t?.points||1;return n?.subQuestions&&n.subQuestions.length>0?{subQuestions:n.subQuestions,hintLetters:o.length>0?o:n.hintLetters||[],caseSensitive:n?.caseSensitive??!1,points:l,explanation:n?.explanation||t?.explanation||""}:!e||void 0===e.imageUrl&&void 0===e.word?{...n,hintLetters:o,caseSensitive:n?.caseSensitive??!1,points:l,explanation:n?.explanation||t?.explanation||""}:{content:{imageUrl:e.imageUrl||"",word:e.word||""},answer:{correctAnswer:n?.correctAnswer||"",caseSensitive:n?.caseSensitive??!1,hintLetters:o,explanation:n?.explanation||t?.explanation||""},hintLetters:o,caseSensitive:n?.caseSensitive??!1,points:l,explanation:n?.explanation||t?.explanation||""}},getExtraProps:t=>({groups:t.groups,questionIndexInPart:t.questionIndexInPart,partId:t.partId,onUnsavedChangesChange:t.onUnsavedChangesChange,validationRef:t.validationRef}),wrapOnSave:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t,explanation:t?.explanation||""}),wrapOnChange:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t,explanation:t?.explanation||""})};
@@ -1,29 +1 @@
1
- export const transformLookPictureFillWordHint = (question) => {
2
- const answerData = question.answer;
3
- if (!answerData?.subQuestions || answerData.subQuestions.length === 0) {
4
- return { apiContent: {}, apiCorrectAnswer: {} };
5
- }
6
- const sq = answerData.subQuestions[0];
7
- const apiContent = {
8
- imageUrl: sq.imageUrl,
9
- word: sq.word,
10
- };
11
- const groupNumber = sq.groupNumber || 1;
12
- const hintLettersByGroup = answerData.hintLettersByGroup;
13
- let hintLetters = [];
14
- if (hintLettersByGroup && hintLettersByGroup[groupNumber]) {
15
- hintLetters = hintLettersByGroup[groupNumber];
16
- }
17
- else if (answerData.hintLetters) {
18
- hintLetters = answerData.hintLetters;
19
- }
20
- return {
21
- apiContent,
22
- apiCorrectAnswer: {
23
- correctAnswer: sq.correctAnswer || '',
24
- caseSensitive: answerData.caseSensitive || false,
25
- },
26
- hintLetters,
27
- groupNumber,
28
- };
29
- };
1
+ export const transformLookPictureFillWordHint=e=>{const t=e.answer;if(!t?.subQuestions||0===t.subQuestions.length)return{apiContent:{},apiCorrectAnswer:{}};const r=t.subQuestions[0],n={imageUrl:r.imageUrl,word:r.word},s=r.groupNumber||1,o=t.hintLettersByGroup;let i=[];return o&&o[s]?i=o[s]:t.hintLetters&&(i=t.hintLetters),{apiContent:n,apiCorrectAnswer:{correctAnswer:r.correctAnswer||"",caseSensitive:t.caseSensitive||!1},hintLetters:i,groupNumber:s}};
@@ -1,38 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Card, CardContent } from '../../../../components/ui/card';
4
- import { Input } from '../../../../components/ui/input';
5
- import { Label } from '../../../../components/ui/label';
6
- import { CheckCircle2, XCircle, BookOpen } from 'lucide-react';
7
- import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
8
- import { useState } from 'react';
9
- function OptionCard({ option }) {
10
- const { previewUrl } = usePresignedFileUrl(option.imageUrl || '');
11
- const displayUrl = previewUrl || option.imageUrl;
12
- if (displayUrl) {
13
- return (_jsxs("div", { className: "flex flex-col items-center gap-2 rounded-xl border border-gray-200 bg-white p-3 shadow-sm", children: [_jsx("div", { className: "relative h-24 w-24 overflow-hidden rounded-lg bg-gray-50", children: _jsx("img", { src: displayUrl, alt: option.answer, className: "h-full w-full object-contain" }) }), _jsx("span", { className: "text-sm font-semibold text-gray-700", children: option.answer }), option.content && (_jsx("span", { className: "text-xs text-gray-500 text-center line-clamp-2", children: option.content }))] }));
14
- }
15
- return (_jsxs("div", { className: "flex items-center gap-2.5 rounded-xl border border-gray-200 bg-white px-4 py-3 shadow-sm", children: [_jsx("div", { className: "flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-teal-500 to-cyan-600 text-sm font-bold text-white shadow-sm", children: option.answer?.[0] ?? '?' }), _jsxs("div", { className: "min-w-0", children: [_jsx("span", { className: "block text-sm font-semibold text-gray-800", children: option.answer }), option.content && (_jsx("span", { className: "block truncate text-xs text-gray-500", children: option.content }))] })] }));
16
- }
17
- export function MatchByWritingAnswerClient({ questionData, onSubmit, isReviewMode = false, userAnswer, autoFillCorrectAnswers = false, explanation, }) {
18
- const [answer, setAnswer] = useState(userAnswer || '');
19
- const { previewUrl: questionImageUrl } = usePresignedFileUrl(questionData.imageUrl || '');
20
- if (!questionData.question) {
21
- return (_jsx(Card, { children: _jsx(CardContent, { className: "p-6", children: _jsx("p", { className: "text-gray-500", children: "Kh\u00F4ng c\u00F3 d\u1EEF li\u1EC7u c\u00E2u h\u1ECFi" }) }) }));
22
- }
23
- const isExample = questionData.isExample || false;
24
- const displayedAnswer = autoFillCorrectAnswers || isExample
25
- ? questionData.correctAnswer
26
- : answer;
27
- const isCorrect = isReviewMode && userAnswer !== undefined
28
- ? userAnswer.trim().toLowerCase() === questionData.correctAnswer.trim().toLowerCase()
29
- : undefined;
30
- return (_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-teal-400 to-cyan-500" }), _jsxs(CardContent, { className: "p-6 space-y-6", children: [questionData.options && questionData.options.length > 0 && (_jsxs("div", { className: "rounded-xl border border-purple-200 bg-gradient-to-r from-purple-50 to-violet-50 p-4", children: [_jsx("p", { className: "mb-3 text-sm font-semibold text-purple-700", children: "C\u00E1c l\u1EF1a ch\u1ECDn:" }), _jsx("div", { className: `grid gap-3 ${questionData.options.some((o) => o.imageUrl) ? 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4' : 'grid-cols-1 sm:grid-cols-2'}`, children: questionData.options.map((option, index) => (_jsx(OptionCard, { option: option }, index))) })] })), _jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-5", children: [isExample && (_jsxs("div", { className: "mb-2 inline-flex items-center gap-1.5 rounded-full bg-amber-100 border border-amber-300 px-3 py-1", children: [_jsx(BookOpen, { className: "h-3.5 w-3.5 text-amber-600" }), _jsx("span", { className: "text-xs font-semibold text-amber-700 uppercase tracking-wide", children: "Example" })] })), _jsxs("div", { className: "flex items-center gap-4", children: [(questionImageUrl || questionData.imageUrl) ? (_jsx("div", { className: "relative h-20 w-20 flex-shrink-0 overflow-hidden rounded-lg border border-gray-200 bg-gray-50 shadow-sm", children: _jsx("img", { src: questionImageUrl || questionData.imageUrl, alt: questionData.question, className: "h-full w-full object-contain" }) })) : null, _jsxs("div", { className: "flex-1 space-y-2", children: [_jsx(Label, { className: "text-base font-semibold text-gray-800", children: questionData.question }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Input, { value: displayedAnswer, onChange: (e) => {
31
- setAnswer(e.target.value);
32
- onSubmit?.(e.target.value);
33
- }, placeholder: "Vi\u1EBFt \u0111\u00E1p \u00E1n...", disabled: isReviewMode || isExample, className: `max-w-xs border-2 text-lg font-medium ${isReviewMode
34
- ? isCorrect
35
- ? 'border-green-400 bg-green-50 text-green-700'
36
- : 'border-red-400 bg-red-50 text-red-700'
37
- : 'border-teal-300 focus:border-teal-500 focus:ring-teal-200'}` }), isReviewMode && isCorrect !== undefined && (_jsx("span", { className: "flex items-center gap-1", children: isCorrect ? (_jsx(CheckCircle2, { className: "h-5 w-5 text-green-500" })) : (_jsx(XCircle, { className: "h-5 w-5 text-red-500" })) }))] })] })] }), isReviewMode && isCorrect === false && (_jsxs("div", { className: "mt-3 flex items-center gap-2 rounded-lg bg-green-50 border border-green-200 px-4 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), _jsxs("span", { className: "text-sm text-green-700", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng: ", _jsx("strong", { children: questionData.correctAnswer })] })] })), autoFillCorrectAnswers && (_jsxs("div", { className: "mt-3 flex items-center gap-2 rounded-lg bg-teal-50 border border-teal-200 px-4 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-teal-600" }), _jsxs("span", { className: "text-sm text-teal-700", children: ["\u0110\u00E1p \u00E1n: ", _jsx("strong", { children: questionData.correctAnswer })] })] }))] }), explanation && isReviewMode && (_jsxs("div", { className: "rounded-xl border border-blue-200 bg-blue-50 p-4", children: [_jsx("p", { className: "text-sm font-semibold text-blue-800 mb-1", children: "Gi\u1EA3i th\u00EDch:" }), _jsx("p", { className: "text-sm text-blue-700", children: explanation })] }))] })] }));
38
- }
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Card as t,CardContent as a}from"../../../../components/ui/card";import{Input as s}from"../../../../components/ui/input";import{Label as l}from"../../../../components/ui/label";import{CheckCircle2 as n,XCircle as i,BookOpen as o}from"lucide-react";import{usePresignedFileUrl as c}from"../../../../shared/lib/hooks";import{useState as d}from"react";function m({option:t}){const{previewUrl:a}=c(t.imageUrl||""),s=a||t.imageUrl;return r("div",s?{className:"flex flex-col items-center gap-2 rounded-xl border border-gray-200 bg-white p-3 shadow-sm",children:[e("div",{className:"relative h-24 w-24 overflow-hidden rounded-lg bg-gray-50",children:e("img",{src:s,alt:t.answer,className:"h-full w-full object-contain"})}),e("span",{className:"text-sm font-semibold text-gray-700",children:t.answer}),t.content&&e("span",{className:"text-xs text-gray-500 text-center line-clamp-2",children:t.content})]}:{className:"flex items-center gap-2.5 rounded-xl border border-gray-200 bg-white px-4 py-3 shadow-sm",children:[e("div",{className:"flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-teal-500 to-cyan-600 text-sm font-bold text-white shadow-sm",children:t.answer?.[0]??"?"}),r("div",{className:"min-w-0",children:[e("span",{className:"block text-sm font-semibold text-gray-800",children:t.answer}),t.content&&e("span",{className:"block truncate text-xs text-gray-500",children:t.content})]})]})}export function MatchByWritingAnswerClient({questionData:g,onSubmit:p,isReviewMode:h=!1,userAnswer:b,autoFillCorrectAnswers:x=!1,explanation:u}){const[f,w]=d(b||""),{previewUrl:N}=c(g.imageUrl||"");if(!g.question)return e(t,{children:e(a,{className:"p-6",children:e("p",{className:"text-gray-500",children:"Không có dữ liệu câu hỏi"})})});const v=g.isExample||!1,y=x||v?g.correctAnswer:f,U=h&&void 0!==b?b.trim().toLowerCase()===g.correctAnswer.trim().toLowerCase():void 0;return r(t,{className:"overflow-hidden border-0 bg-white shadow-lg",children:[e("div",{className:"h-1 bg-gradient-to-r from-teal-400 to-cyan-500"}),r(a,{className:"p-6 space-y-6",children:[g.options&&g.options.length>0&&r("div",{className:"rounded-xl border border-purple-200 bg-gradient-to-r from-purple-50 to-violet-50 p-4",children:[e("p",{className:"mb-3 text-sm font-semibold text-purple-700",children:"Các lựa chọn:"}),e("div",{className:"grid gap-3 "+(g.options.some(e=>e.imageUrl)?"grid-cols-2 sm:grid-cols-3 md:grid-cols-4":"grid-cols-1 sm:grid-cols-2"),children:g.options.map((r,t)=>e(m,{option:r},t))})]}),r("div",{className:"rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-5",children:[v&&r("div",{className:"mb-2 inline-flex items-center gap-1.5 rounded-full bg-amber-100 border border-amber-300 px-3 py-1",children:[e(o,{className:"h-3.5 w-3.5 text-amber-600"}),e("span",{className:"text-xs font-semibold text-amber-700 uppercase tracking-wide",children:"Example"})]}),r("div",{className:"flex items-center gap-4",children:[N||g.imageUrl?e("div",{className:"relative h-20 w-20 flex-shrink-0 overflow-hidden rounded-lg border border-gray-200 bg-gray-50 shadow-sm",children:e("img",{src:N||g.imageUrl,alt:g.question,className:"h-full w-full object-contain"})}):null,r("div",{className:"flex-1 space-y-2",children:[e(l,{className:"text-base font-semibold text-gray-800",children:g.question}),r("div",{className:"flex items-center gap-3",children:[e(s,{value:y,onChange:e=>{w(e.target.value),p?.(e.target.value)},placeholder:"Viết đáp án...",disabled:h||v,className:"max-w-xs border-2 text-lg font-medium "+(h?U?"border-green-400 bg-green-50 text-green-700":"border-red-400 bg-red-50 text-red-700":"border-teal-300 focus:border-teal-500 focus:ring-teal-200")}),h&&void 0!==U&&e("span",{className:"flex items-center gap-1",children:U?e(n,{className:"h-5 w-5 text-green-500"}):e(i,{className:"h-5 w-5 text-red-500"})})]})]})]}),h&&!1===U&&r("div",{className:"mt-3 flex items-center gap-2 rounded-lg bg-green-50 border border-green-200 px-4 py-2",children:[e(n,{className:"h-4 w-4 text-green-600"}),r("span",{className:"text-sm text-green-700",children:["Đáp án đúng: ",e("strong",{children:g.correctAnswer})]})]}),x&&r("div",{className:"mt-3 flex items-center gap-2 rounded-lg bg-teal-50 border border-teal-200 px-4 py-2",children:[e(n,{className:"h-4 w-4 text-teal-600"}),r("span",{className:"text-sm text-teal-700",children:["Đáp án: ",e("strong",{children:g.correctAnswer})]})]})]}),u&&h&&r("div",{className:"rounded-xl border border-blue-200 bg-blue-50 p-4",children:[e("p",{className:"text-sm font-semibold text-blue-800 mb-1",children:"Giải thích:"}),e("p",{className:"text-sm text-blue-700",children:u})]})]})]})}
@@ -1,359 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { ImagePreview } from '../../../../components/ui/image-preview';
4
- import { useState, useRef, useEffect } from 'react';
5
- import { Button } from '../../../../components/ui/button';
6
- import { Label } from '../../../../components/ui/label';
7
- import { Input } from '../../../../components/ui/input';
8
- import { Textarea } from '../../../../components/ui/textarea';
9
- import { PointsInput } from '../../../../components/ui/points-input';
10
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
11
- import { Pencil, Eye, ImageIcon, MessageSquare, HelpCircle, BookOpen, Grid3X3, Users } from 'lucide-react';
12
- import { FileUpload } from '../../../../components/ui/file-upload';
13
- import { Checkbox } from '../../../../components/ui/checkbox';
14
- import { MatchByWritingAnswerClient } from './MatchByWritingAnswerClient';
15
- import { MatchAnswerGroupCard } from '../../groups/MatchAnswerGroupCard';
16
- import { useDebouncedCallback, usePresignedFileUrl } from '../../../../shared/lib/hooks';
17
- import { useExamQuestionStore } from '../../../../shared/lib/stores/examQuestionStore';
18
- // Helper: Calculate group number for a question index based on groups config
19
- function getGroupNumberForIndex(questionIndex, groups) {
20
- if (!groups || groups.length === 0) {
21
- return 1;
22
- }
23
- let currentIndex = 0;
24
- for (const group of groups) {
25
- if (questionIndex < currentIndex + group.questionCount) {
26
- return group.number;
27
- }
28
- currentIndex += group.questionCount;
29
- }
30
- return groups[groups.length - 1]?.number || 1;
31
- }
32
- function MatchByWritingAnswerContent({ initialData, onSave, onSaveToQuestionBank, onCancel, onChange, onUnsavedChangesChange, externalErrors, groups, groupConfig, questionConfig, viewMode, questionIndexInPart = 0, partId = '', validationRef, headerExtra, }) {
33
- // Access exam question store for shared data
34
- const setSharedPassageData = useExamQuestionStore((state) => state.setSharedPassageData);
35
- const getSharedPassageData = useExamQuestionStore((state) => state.getSharedPassageData);
36
- // Get current group number
37
- const initialGroupNum = getGroupNumberForIndex(questionIndexInPart, groups);
38
- // Per-question state
39
- const [question, setQuestion] = useState(initialData?.question || '');
40
- const [imageUrl, setImageUrl] = useState(initialData?.imageUrl || '');
41
- const [correctAnswer, setCorrectAnswer] = useState(initialData?.correctAnswer || '');
42
- const [points, setPoints] = useState(initialData?.points || 1);
43
- const [isExample, setIsExample] = useState(initialData?.isExample || false);
44
- const [groupNumber, setGroupNumber] = useState(initialData?.groupNumber || initialGroupNum);
45
- const [explanation, setExplanation] = useState(initialData?.explanation || '');
46
- const [askerName, setAskerName] = useState(initialData?.askerName || '');
47
- const [answererName, setAnswererName] = useState(initialData?.answererName || '');
48
- // Shared options state (stored via exam question store)
49
- const [options, setOptions] = useState(() => {
50
- // Priority 1: Check shared data from store
51
- if (partId) {
52
- const storedData = getSharedPassageData(partId, initialGroupNum);
53
- if (storedData?.options && Array.isArray(storedData.options)) {
54
- return storedData.options;
55
- }
56
- }
57
- // Priority 2: From initialData
58
- return initialData?.options || [];
59
- });
60
- // Group image URL state (shared across all questions in the group)
61
- const [groupImageUrl, setGroupImageUrl] = useState(() => {
62
- if (partId) {
63
- const storedData = getSharedPassageData(partId, initialGroupNum);
64
- if (storedData?.groupImageUrl) {
65
- return storedData.groupImageUrl;
66
- }
67
- }
68
- return initialData?.groupImageUrl || '';
69
- });
70
- const [groupImagePreviewUrl, setGroupImagePreviewUrl] = useState('');
71
- // Group title state (shared across all questions in the group)
72
- const [groupTitle, setGroupTitle] = useState(() => {
73
- if (partId) {
74
- const storedData = getSharedPassageData(partId, initialGroupNum);
75
- if (storedData?.groupTitle) {
76
- return storedData.groupTitle;
77
- }
78
- }
79
- return initialData?.groupTitle || '';
80
- });
81
- // Whether to show shared group fields
82
- const showGroupImageUrl = groupConfig?.includes('groupImageUrl') ?? false;
83
- const showGroupTitle = groupConfig?.includes('groupTitle') ?? false;
84
- // UI State
85
- const [isClientMode, setIsClientMode] = useState(false);
86
- const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
87
- const [imagePreviewUrl, setImagePreviewUrl] = useState('');
88
- // Inline validation errors
89
- const [validationErrors, setValidationErrors] = useState({});
90
- // Refs
91
- const savedStateRef = useRef('');
92
- const previousInitialDataRef = useRef(initialData);
93
- const previousQuestionIndexRef = useRef(questionIndexInPart);
94
- const isInitializingRef = useRef(false);
95
- const hasInitializedRef = useRef(false);
96
- const onChangeRef = useRef(onChange);
97
- const previousPayloadRef = useRef('');
98
- const onUnsavedChangesChangeRef = useRef(onUnsavedChangesChange);
99
- const currentGroupNumber = groupNumber || 1;
100
- // Determine which per-question fields to show based on questionConfig
101
- // Default: show all fields if questionConfig is not provided
102
- const showQuestionField = !questionConfig || questionConfig.includes('question');
103
- const showImageUrlField = !questionConfig || questionConfig.includes('imageUrl');
104
- const showAnswerField = !questionConfig || questionConfig.includes('answer');
105
- // Presigned URL for per-question image preview
106
- const { previewUrl: generatedPreviewUrl } = usePresignedFileUrl(imageUrl);
107
- const displayPreviewUrl = imagePreviewUrl || generatedPreviewUrl || '';
108
- useEffect(() => { onUnsavedChangesChangeRef.current = onUnsavedChangesChange; }, [onUnsavedChangesChange]);
109
- useEffect(() => { onChangeRef.current = onChange; }, [onChange]);
110
- const debouncedOnChange = useDebouncedCallback((data) => {
111
- onChangeRef.current?.(data);
112
- }, 300);
113
- // Helper to get current state string
114
- const getCurrentStateString = () => {
115
- return JSON.stringify({
116
- question,
117
- imageUrl,
118
- correctAnswer,
119
- points,
120
- isExample,
121
- askerName,
122
- answererName,
123
- options,
124
- groupImageUrl,
125
- groupTitle,
126
- });
127
- };
128
- // Initialize saved state when questionIndexInPart changes
129
- useEffect(() => {
130
- if (isInitializingRef.current)
131
- return;
132
- const timer = setTimeout(() => {
133
- savedStateRef.current = getCurrentStateString();
134
- setHasUnsavedChanges(false);
135
- }, 150);
136
- return () => clearTimeout(timer);
137
- }, [questionIndexInPart]);
138
- // Detect unsaved changes
139
- useEffect(() => {
140
- if (isInitializingRef.current)
141
- return;
142
- if (!savedStateRef.current)
143
- return;
144
- const currentState = getCurrentStateString();
145
- const hasChanges = currentState !== savedStateRef.current;
146
- if (hasChanges !== hasUnsavedChanges) {
147
- setHasUnsavedChanges(hasChanges);
148
- onUnsavedChangesChangeRef.current?.(hasChanges);
149
- }
150
- }, [question, imageUrl, correctAnswer, points, isExample, askerName, answererName, options, groupImageUrl, groupTitle, hasUnsavedChanges]);
151
- // Expose validation function via ref
152
- useEffect(() => {
153
- if (validationRef) {
154
- validationRef.current = {
155
- triggerValidation: async () => {
156
- const errors = {};
157
- let isValid = true;
158
- if (showQuestionField && (!question || question.trim() === '')) {
159
- errors.question = 'Vui lòng nhập tên/câu hỏi';
160
- isValid = false;
161
- }
162
- if (showAnswerField && (!correctAnswer || correctAnswer.trim() === '')) {
163
- errors.correctAnswer = 'Vui lòng nhập đáp án';
164
- isValid = false;
165
- }
166
- setValidationErrors(errors);
167
- return isValid;
168
- },
169
- getFormData: () => ({
170
- question,
171
- imageUrl,
172
- correctAnswer,
173
- points,
174
- groupNumber: currentGroupNumber,
175
- isExample,
176
- askerName,
177
- answererName,
178
- options,
179
- groupImageUrl,
180
- groupTitle,
181
- explanation,
182
- groups,
183
- }),
184
- };
185
- }
186
- }, [validationRef, question, imageUrl, correctAnswer, points, currentGroupNumber, isExample, askerName, answererName, options, groupImageUrl, groupTitle, explanation, groups]);
187
- // Sync from initialData when navigation or first init
188
- useEffect(() => {
189
- const questionIndexChanged = questionIndexInPart !== previousQuestionIndexRef.current;
190
- const hasValidInitialData = !!initialData?.question;
191
- const shouldSyncForFirstInit = !hasInitializedRef.current && hasValidInitialData;
192
- const shouldSyncForNavigation = questionIndexChanged;
193
- if (!shouldSyncForFirstInit && !shouldSyncForNavigation)
194
- return;
195
- isInitializingRef.current = true;
196
- previousInitialDataRef.current = initialData;
197
- previousQuestionIndexRef.current = questionIndexInPart;
198
- hasInitializedRef.current = true;
199
- const groupNum = getGroupNumberForIndex(questionIndexInPart, groups);
200
- // Set per-question state
201
- setQuestion(initialData?.question || '');
202
- setImageUrl(initialData?.imageUrl || '');
203
- setCorrectAnswer(initialData?.correctAnswer || '');
204
- setPoints(initialData?.points || 1);
205
- setIsExample(initialData?.isExample || false);
206
- setGroupNumber(groupNum);
207
- setExplanation(initialData?.explanation || '');
208
- setAskerName(initialData?.askerName || '');
209
- setAnswererName(initialData?.answererName || '');
210
- // Handle shared options, groupImageUrl & groupTitle
211
- let sharedOptions = [];
212
- let sharedGroupImageUrl = '';
213
- let sharedGroupTitle = '';
214
- if (partId) {
215
- const storedData = getSharedPassageData(partId, groupNum);
216
- if (storedData?.options && Array.isArray(storedData.options)) {
217
- sharedOptions = storedData.options;
218
- sharedGroupImageUrl = storedData?.groupImageUrl || '';
219
- sharedGroupTitle = storedData?.groupTitle || '';
220
- }
221
- else {
222
- sharedOptions = initialData?.options || [];
223
- sharedGroupImageUrl = initialData?.groupImageUrl || '';
224
- sharedGroupTitle = initialData?.groupTitle || '';
225
- if (sharedOptions.length > 0 || sharedGroupImageUrl || sharedGroupTitle) {
226
- setSharedPassageData(partId, groupNum, { options: sharedOptions, groupImageUrl: sharedGroupImageUrl, groupTitle: sharedGroupTitle });
227
- }
228
- }
229
- }
230
- else {
231
- sharedOptions = initialData?.options || [];
232
- sharedGroupImageUrl = initialData?.groupImageUrl || '';
233
- sharedGroupTitle = initialData?.groupTitle || '';
234
- }
235
- setOptions(sharedOptions);
236
- setGroupImageUrl(sharedGroupImageUrl);
237
- setGroupTitle(sharedGroupTitle);
238
- setTimeout(() => { isInitializingRef.current = false; }, 100);
239
- }, [initialData, groups, questionIndexInPart, partId, getSharedPassageData, setSharedPassageData]);
240
- // Call onChange when data changes
241
- useEffect(() => {
242
- if (isInitializingRef.current)
243
- return;
244
- const payload = {
245
- question,
246
- imageUrl,
247
- correctAnswer,
248
- points: points || 1,
249
- groupNumber: currentGroupNumber,
250
- isExample,
251
- askerName,
252
- answererName,
253
- options,
254
- groupImageUrl,
255
- groupTitle,
256
- explanation,
257
- groups,
258
- };
259
- const payloadString = JSON.stringify(payload);
260
- if (previousPayloadRef.current !== payloadString) {
261
- previousPayloadRef.current = payloadString;
262
- // Save shared data to store
263
- if (partId) {
264
- setSharedPassageData(partId, currentGroupNumber, { options, groupImageUrl, groupTitle });
265
- }
266
- debouncedOnChange(payload);
267
- }
268
- }, [question, imageUrl, correctAnswer, points, currentGroupNumber, isExample, askerName, answererName, options, groupImageUrl, groupTitle, explanation, groups, partId, setSharedPassageData, debouncedOnChange]);
269
- // Update option
270
- const updateOption = (index, field, value) => {
271
- setOptions((prev) => {
272
- const updated = [...prev];
273
- updated[index] = { ...updated[index], [field]: value };
274
- return updated;
275
- });
276
- };
277
- // Add option
278
- const addOption = () => {
279
- const nextNumber = String(options.length + 1); // 1, 2, 3...
280
- setOptions((prev) => [...prev, { answer: '', content: '', imageUrl: '' }]);
281
- };
282
- // Remove option
283
- const removeOption = (index) => {
284
- setOptions((prev) => prev.filter((_, i) => i !== index));
285
- };
286
- // Validate all fields
287
- const validate = () => {
288
- const errors = {};
289
- let isValid = true;
290
- if (showQuestionField && (!question || question.trim() === '')) {
291
- errors.question = 'Vui lòng nhập tên/câu hỏi';
292
- isValid = false;
293
- }
294
- if (showAnswerField && (!correctAnswer || correctAnswer.trim() === '')) {
295
- errors.correctAnswer = 'Vui lòng nhập đáp án';
296
- isValid = false;
297
- }
298
- setValidationErrors(errors);
299
- return isValid;
300
- };
301
- // Handle save
302
- const handleSave = () => {
303
- if (!validate())
304
- return;
305
- const payload = {
306
- question,
307
- imageUrl,
308
- correctAnswer,
309
- points: points || 1,
310
- groupNumber: currentGroupNumber,
311
- isExample,
312
- askerName,
313
- answererName,
314
- options,
315
- groupImageUrl,
316
- groupTitle,
317
- explanation,
318
- groups,
319
- };
320
- savedStateRef.current = getCurrentStateString();
321
- setHasUnsavedChanges(false);
322
- onSave?.(payload);
323
- };
324
- // Client mode preview
325
- if (isClientMode) {
326
- const questionData = {
327
- question,
328
- imageUrl: displayPreviewUrl || imageUrl,
329
- correctAnswer,
330
- points,
331
- groupNumber: currentGroupNumber,
332
- isExample,
333
- options,
334
- };
335
- return (_jsxs("div", { className: "space-y-4", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Match by Writing Answer (Preview)" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Ch\u1EBF \u0111\u1ED9 xem tr\u01B0\u1EDBc - Hi\u1EC3n th\u1ECB \u0111\u00E1p \u00E1n \u0111\u00FAng" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(false), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), "Quay l\u1EA1i Edit Mode"] })] }) }) }), _jsx(MatchByWritingAnswerClient, { questionData: questionData, isReviewMode: true, autoFillCorrectAnswers: true, explanation: explanation })] }));
336
- }
337
- // Creator UI
338
- return (_jsxs("div", { className: "space-y-4", children: [groups && groups.length > 0 && (_jsx(MatchAnswerGroupCard, { currentGroupNumber: currentGroupNumber, groupTitle: groupTitle, groupImageUrl: groupImageUrl, groupImagePreviewUrl: groupImagePreviewUrl, options: options, groupConfig: groupConfig, showGroupTitle: showGroupTitle, showGroupImageUrl: showGroupImageUrl, onGroupTitleChange: setGroupTitle, onGroupImageChange: setGroupImageUrl, onGroupImagePreviewUrlChange: setGroupImagePreviewUrl, onUpdateOption: updateOption, onRemoveOption: removeOption, onAddOption: addOption })), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50", children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-blue-600 shadow-sm", children: _jsx(Grid3X3, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-base font-bold text-gray-900", children: "Gh\u00E9p \u0111\u00E1p \u00E1n b\u1EB1ng c\u00E1ch vi\u1EBFt" }), _jsx("p", { className: "text-xs text-gray-500", children: "Nh\u00ECn h\u00ECnh v\u00E0 vi\u1EBFt \u0111\u00E1p \u00E1n t\u01B0\u01A1ng \u1EE9ng" })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [headerExtra, _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: !question, className: "gap-2 border-teal-200 text-teal-600 hover:bg-teal-50 hover:text-teal-700", children: [_jsx(Eye, { className: "h-4 w-4" }), "Xem tr\u01B0\u1EDBc"] })] })] }) }), _jsxs(CardContent, { className: "space-y-4 px-4 pb-4", children: [_jsxs("div", { className: "flex items-center gap-6", children: [_jsx(PointsInput, { value: points || 1, onChange: (e) => setPoints(parseFloat(e.target.value) || 1) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Checkbox, { id: "isExample", checked: isExample || false, onCheckedChange: (checked) => setIsExample(!!checked) }), _jsx(Label, { htmlFor: "isExample", className: "flex items-center gap-1.5 cursor-pointer text-sm font-medium text-gray-700", children: "\u0110\u00E1nh d\u1EA5u l\u00E0 c\u00E2u h\u1ECFi Example" })] })] }), viewMode === 'CONVERSATION' && (_jsxs("div", { className: "space-y-3", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(Users, { className: "h-4 w-4 text-indigo-500" }), "H\u1ED9i tho\u1EA1i ", _jsx("span", { className: "text-sm font-normal text-gray-500", children: "(tu\u1EF3 ch\u1ECDn - d\u00F9ng cho c\u00E2u h\u1ECFi d\u1EA1ng h\u1ED9i tho\u1EA1i)" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4 rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "askerName", className: "text-sm font-medium text-gray-600", children: "T\u00EAn ng\u01B0\u1EDDi h\u1ECFi" }), _jsx(Input, { id: "askerName", value: askerName, onChange: (e) => setAskerName(e.target.value), placeholder: "VD: John, Mary, Teacher...", className: "border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "answererName", className: "text-sm font-medium text-gray-600", children: "T\u00EAn ng\u01B0\u1EDDi tr\u1EA3 l\u1EDDi" }), _jsx(Input, { id: "answererName", value: answererName, onChange: (e) => setAnswererName(e.target.value), placeholder: "VD: Tom, Anna, Student...", className: "border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200" })] })] })] })), showQuestionField && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "question", className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-4 w-4 text-teal-500" }), "T\u00EAn / C\u00E2u h\u1ECFi ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(t\u00F9y ch\u1ECDn)" })] }), _jsx(Input, { id: "question", value: question, onChange: (e) => {
339
- setQuestion(e.target.value);
340
- if (validationErrors.question) {
341
- setValidationErrors((prev) => ({ ...prev, question: undefined }));
342
- }
343
- }, placeholder: "Nh\u1EADp t\u00EAn ho\u1EB7c c\u00E2u h\u1ECFi... (vd: Mum, Dad, Julia)", className: `border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200 ${validationErrors.question ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.question && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.question }))] })), showImageUrlField && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-teal-500" }), "H\u00ECnh \u1EA3nh c\u00E2u h\u1ECFi ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(t\u00F9y ch\u1ECDn)" })] }), _jsx(FileUpload, { id: "question-image", label: "", accept: "image/*", value: imageUrl || '', onChange: (url) => {
344
- setImageUrl(url);
345
- if (validationErrors.imageUrl) {
346
- setValidationErrors((prev) => ({ ...prev, imageUrl: undefined }));
347
- }
348
- }, onPresignedUrlChange: setImagePreviewUrl, maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" }), displayPreviewUrl && (_jsx(ImagePreview, { src: displayPreviewUrl, alt: "Preview", className: "mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200" }))] })), showAnswerField && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "correctAnswer", className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(HelpCircle, { className: "h-4 w-4 text-green-500" }), "\u0110\u00E1p \u00E1n \u0111\u00FAng ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsxs("select", { id: "correctAnswer", value: correctAnswer, onChange: (e) => {
349
- setCorrectAnswer(e.target.value);
350
- if (validationErrors.correctAnswer) {
351
- setValidationErrors((prev) => ({ ...prev, correctAnswer: undefined }));
352
- }
353
- }, className: `flex h-10 w-full rounded-md border px-3 py-2 text-sm bg-white transition-all focus:outline-none focus:ring-2 focus:ring-offset-0 ${validationErrors.correctAnswer ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : 'border-gray-200 focus:border-green-300 focus:ring-green-200'}`, children: [_jsx("option", { value: "", children: "-- Ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng --" }), options
354
- .filter((opt) => opt.answer && opt.answer.trim() !== '')
355
- .map((opt, idx) => (_jsx("option", { value: opt.answer, children: opt.answer }, idx)))] }), validationErrors.correctAnswer && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.correctAnswer }))] })), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100", children: _jsx("span", { className: "text-lg", children: "\u274C" }) }), _jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("ul", { className: "mt-1 list-inside list-disc text-red-700", children: externalErrors.map((error, index) => (_jsx("li", { children: error }, index))) })] })] }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50", children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm", children: _jsx(BookOpen, { className: "h-3.5 w-3.5 text-white" }) }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-base font-semibold text-gray-800", children: "Gi\u1EA3i th\u00EDch" }), _jsx("span", { className: "inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-600", children: "Kh\u00F4ng b\u1EAFt bu\u1ED9c" })] }), _jsx("p", { className: "text-xs text-gray-500 mt-0.5", children: "Gi\u1EA3i th\u00EDch cho \u0111\u00E1p \u00E1n \u0111\u00FAng \u2014 hi\u1EC3n th\u1ECB sau khi h\u1ECDc sinh ho\u00E0n th\u00E0nh c\u00E2u h\u1ECFi" })] })] }) }), _jsx(CardContent, { className: "px-4 pb-4", children: _jsx(Textarea, { id: "explanation", value: explanation, onChange: (e) => setExplanation(e.target.value), placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0... v\u00EC...", rows: 3, className: "min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none" }) })] })] }));
356
- }
357
- export function MatchByWritingAnswerCreator(props) {
358
- return _jsx(MatchByWritingAnswerContent, { ...props });
359
- }
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{ImagePreview as t}from"../../../../components/ui/image-preview";import{useState as n,useRef as a,useEffect as i}from"react";import{Button as s}from"../../../../components/ui/button";import{Label as o}from"../../../../components/ui/label";import{Input as l}from"../../../../components/ui/input";import{Textarea as c}from"../../../../components/ui/textarea";import{PointsInput as m}from"../../../../components/ui/points-input";import{Card as d,CardContent as u,CardHeader as g,CardTitle as p}from"../../../../components/ui/card";import{Pencil as h,Eye as x,ImageIcon as f,MessageSquare as N,HelpCircle as b,BookOpen as w,Grid3X3 as v,Users as y}from"lucide-react";import{FileUpload as C}from"../../../../components/ui/file-upload";import{Checkbox as U}from"../../../../components/ui/checkbox";import{MatchByWritingAnswerClient as A}from"./MatchByWritingAnswerClient";import{MatchAnswerGroupCard as T}from"../../groups/MatchAnswerGroupCard";import{useDebouncedCallback as q,usePresignedFileUrl as I}from"../../../../shared/lib/hooks";import{useExamQuestionStore as k}from"../../../../shared/lib/stores/examQuestionStore";function E(e,r){if(!r||0===r.length)return 1;let t=0;for(const n of r){if(e<t+n.questionCount)return n.number;t+=n.questionCount}return r[r.length-1]?.number||1}function S({initialData:S,onSave:G,onSaveToQuestionBank:D,onCancel:M,onChange:j,onUnsavedChangesChange:F,externalErrors:P,groups:O,groupConfig:V,questionConfig:R,viewMode:z,questionIndexInPart:J=0,partId:B="",validationRef:H,headerExtra:Q}){const W=k(e=>e.setSharedPassageData),L=k(e=>e.getSharedPassageData),K=E(J,O),[X,Y]=n(S?.question||""),[Z,$]=n(S?.imageUrl||""),[_,ee]=n(S?.correctAnswer||""),[re,te]=n(S?.points||1),[ne,ae]=n(S?.isExample||!1),[ie,se]=n(S?.groupNumber||K),[oe,le]=n(S?.explanation||""),[ce,me]=n(S?.askerName||""),[de,ue]=n(S?.answererName||""),[ge,pe]=n(()=>{if(B){const e=L(B,K);if(e?.options&&Array.isArray(e.options))return e.options}return S?.options||[]}),[he,xe]=n(()=>{if(B){const e=L(B,K);if(e?.groupImageUrl)return e.groupImageUrl}return S?.groupImageUrl||""}),[fe,Ne]=n(""),[be,we]=n(()=>{if(B){const e=L(B,K);if(e?.groupTitle)return e.groupTitle}return S?.groupTitle||""}),ve=V?.includes("groupImageUrl")??!1,ye=V?.includes("groupTitle")??!1,[Ce,Ue]=n(!1),[Ae,Te]=n(!1),[qe,Ie]=n(""),[ke,Ee]=n({}),Se=a(""),Ge=a(S),De=a(J),Me=a(!1),je=a(!1),Fe=a(j),Pe=a(""),Oe=a(F),Ve=ie||1,Re=!R||R.includes("question"),ze=!R||R.includes("imageUrl"),Je=!R||R.includes("answer"),{previewUrl:Be}=I(Z),He=qe||Be||"";i(()=>{Oe.current=F},[F]),i(()=>{Fe.current=j},[j]);const Qe=q(e=>{Fe.current?.(e)},300),We=()=>JSON.stringify({question:X,imageUrl:Z,correctAnswer:_,points:re,isExample:ne,askerName:ce,answererName:de,options:ge,groupImageUrl:he,groupTitle:be});i(()=>{if(Me.current)return;const e=setTimeout(()=>{Se.current=We(),Te(!1)},150);return()=>clearTimeout(e)},[J]),i(()=>{if(Me.current)return;if(!Se.current)return;const e=We()!==Se.current;e!==Ae&&(Te(e),Oe.current?.(e))},[X,Z,_,re,ne,ce,de,ge,he,be,Ae]),i(()=>{H&&(H.current={triggerValidation:async()=>{const e={};let r=!0;return!Re||X&&""!==X.trim()||(e.question="Vui lòng nhập tên/câu hỏi",r=!1),!Je||_&&""!==_.trim()||(e.correctAnswer="Vui lòng nhập đáp án",r=!1),Ee(e),r},getFormData:()=>({question:X,imageUrl:Z,correctAnswer:_,points:re,groupNumber:Ve,isExample:ne,askerName:ce,answererName:de,options:ge,groupImageUrl:he,groupTitle:be,explanation:oe,groups:O})})},[H,X,Z,_,re,Ve,ne,ce,de,ge,he,be,oe,O]),i(()=>{const e=J!==De.current,r=!!S?.question;if(!(!je.current&&r)&&!e)return;Me.current=!0,Ge.current=S,De.current=J,je.current=!0;const t=E(J,O);Y(S?.question||""),$(S?.imageUrl||""),ee(S?.correctAnswer||""),te(S?.points||1),ae(S?.isExample||!1),se(t),le(S?.explanation||""),me(S?.askerName||""),ue(S?.answererName||"");let n=[],a="",i="";if(B){const e=L(B,t);e?.options&&Array.isArray(e.options)?(n=e.options,a=e?.groupImageUrl||"",i=e?.groupTitle||""):(n=S?.options||[],a=S?.groupImageUrl||"",i=S?.groupTitle||"",(n.length>0||a||i)&&W(B,t,{options:n,groupImageUrl:a,groupTitle:i}))}else n=S?.options||[],a=S?.groupImageUrl||"",i=S?.groupTitle||"";pe(n),xe(a),we(i),setTimeout(()=>{Me.current=!1},100)},[S,O,J,B,L,W]),i(()=>{if(Me.current)return;const e={question:X,imageUrl:Z,correctAnswer:_,points:re||1,groupNumber:Ve,isExample:ne,askerName:ce,answererName:de,options:ge,groupImageUrl:he,groupTitle:be,explanation:oe,groups:O},r=JSON.stringify(e);Pe.current!==r&&(Pe.current=r,B&&W(B,Ve,{options:ge,groupImageUrl:he,groupTitle:be}),Qe(e))},[X,Z,_,re,Ve,ne,ce,de,ge,he,be,oe,O,B,W,Qe]);if(Ce){const t={question:X,imageUrl:He||Z,correctAnswer:_,points:re,groupNumber:Ve,isExample:ne,options:ge};return r("div",{className:"space-y-4",children:[e(d,{children:e(g,{children:r("div",{className:"flex items-center justify-between",children:[r("div",{children:[e(p,{children:"Match by Writing Answer (Preview)"}),e("p",{className:"mt-1 text-sm text-gray-500",children:"Chế độ xem trước - Hiển thị đáp án đúng"})]}),r(s,{variant:"outline",size:"sm",onClick:()=>Ue(!1),children:[e(h,{className:"mr-2 h-4 w-4"}),"Quay lại Edit Mode"]})]})})}),e(A,{questionData:t,isReviewMode:!0,autoFillCorrectAnswers:!0,explanation:oe})]})}return r("div",{className:"space-y-4",children:[O&&O.length>0&&e(T,{currentGroupNumber:Ve,groupTitle:be,groupImageUrl:he,groupImagePreviewUrl:fe,options:ge,groupConfig:V,showGroupTitle:ye,showGroupImageUrl:ve,onGroupTitleChange:we,onGroupImageChange:xe,onGroupImagePreviewUrlChange:Ne,onUpdateOption:(e,r,t)=>{pe(n=>{const a=[...n];return a[e]={...a[e],[r]:t},a})},onRemoveOption:e=>{pe(r=>r.filter((r,t)=>t!==e))},onAddOption:()=>{String(ge.length+1);pe(e=>[...e,{answer:"",content:"",imageUrl:""}])}}),r(d,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50",children:[e(g,{className:"px-4 py-3",children:r("div",{className:"flex items-center justify-between",children:[r("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-blue-600 shadow-sm",children:e(v,{className:"h-4 w-4 text-white"})}),r("div",{children:[e(p,{className:"text-base font-bold text-gray-900",children:"Ghép đáp án bằng cách viết"}),e("p",{className:"text-xs text-gray-500",children:"Nhìn hình và viết đáp án tương ứng"})]})]}),r("div",{className:"flex items-center gap-2",children:[Q,r(s,{variant:"outline",size:"sm",onClick:()=>Ue(!0),disabled:!X,className:"gap-2 border-teal-200 text-teal-600 hover:bg-teal-50 hover:text-teal-700",children:[e(x,{className:"h-4 w-4"}),"Xem trước"]})]})]})}),r(u,{className:"space-y-4 px-4 pb-4",children:[r("div",{className:"flex items-center gap-6",children:[e(m,{value:re||1,onChange:e=>te(parseFloat(e.target.value)||1)}),r("div",{className:"flex items-center gap-2",children:[e(U,{id:"isExample",checked:ne||!1,onCheckedChange:e=>ae(!!e)}),e(o,{htmlFor:"isExample",className:"flex items-center gap-1.5 cursor-pointer text-sm font-medium text-gray-700",children:"Đánh dấu là câu hỏi Example"})]})]}),"CONVERSATION"===z&&r("div",{className:"space-y-3",children:[r(o,{className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(y,{className:"h-4 w-4 text-indigo-500"}),"Hội thoại ",e("span",{className:"text-sm font-normal text-gray-500",children:"(tuỳ chọn - dùng cho câu hỏi dạng hội thoại)"})]}),r("div",{className:"grid grid-cols-2 gap-4 rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4",children:[r("div",{className:"space-y-1.5",children:[e(o,{htmlFor:"askerName",className:"text-sm font-medium text-gray-600",children:"Tên người hỏi"}),e(l,{id:"askerName",value:ce,onChange:e=>me(e.target.value),placeholder:"VD: John, Mary, Teacher...",className:"border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200"})]}),r("div",{className:"space-y-1.5",children:[e(o,{htmlFor:"answererName",className:"text-sm font-medium text-gray-600",children:"Tên người trả lời"}),e(l,{id:"answererName",value:de,onChange:e=>ue(e.target.value),placeholder:"VD: Tom, Anna, Student...",className:"border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200"})]})]})]}),Re&&r("div",{className:"space-y-2",children:[r(o,{htmlFor:"question",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(N,{className:"h-4 w-4 text-teal-500"}),"Tên / Câu hỏi ",e("span",{className:"text-xs font-normal text-gray-500",children:"(tùy chọn)"})]}),e(l,{id:"question",value:X,onChange:e=>{Y(e.target.value),ke.question&&Ee(e=>({...e,question:void 0}))},placeholder:"Nhập tên hoặc câu hỏi... (vd: Mum, Dad, Julia)",className:"border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200 "+(ke.question?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),ke.question&&e("p",{className:"text-sm text-red-600",children:ke.question})]}),ze&&r("div",{className:"space-y-2",children:[r(o,{className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(f,{className:"h-4 w-4 text-teal-500"}),"Hình ảnh câu hỏi ",e("span",{className:"text-xs font-normal text-gray-500",children:"(tùy chọn)"})]}),e(C,{id:"question-image",label:"",accept:"image/*",value:Z||"",onChange:e=>{$(e),ke.imageUrl&&Ee(e=>({...e,imageUrl:void 0}))},onPresignedUrlChange:Ie,maxSize:5,placeholder:"Upload ảnh hoặc paste URL",autoUpload:!0,prefix:"questions"}),He&&e(t,{src:He,alt:"Preview",className:"mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200"})]}),Je&&r("div",{className:"space-y-2",children:[r(o,{htmlFor:"correctAnswer",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(b,{className:"h-4 w-4 text-green-500"}),"Đáp án đúng ",e("span",{className:"text-red-500",children:"*"})]}),r("select",{id:"correctAnswer",value:_,onChange:e=>{ee(e.target.value),ke.correctAnswer&&Ee(e=>({...e,correctAnswer:void 0}))},className:"flex h-10 w-full rounded-md border px-3 py-2 text-sm bg-white transition-all focus:outline-none focus:ring-2 focus:ring-offset-0 "+(ke.correctAnswer?"border-red-400 focus:border-red-500 focus:ring-red-200":"border-gray-200 focus:border-green-300 focus:ring-green-200"),children:[e("option",{value:"",children:"-- Chọn đáp án đúng --"}),ge.filter(e=>e.answer&&""!==e.answer.trim()).map((r,t)=>e("option",{value:r.answer,children:r.answer},t))]}),ke.correctAnswer&&e("p",{className:"text-sm text-red-600",children:ke.correctAnswer})]}),P&&P.length>0&&r("div",{className:"flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4",children:[e("div",{className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100",children:e("span",{className:"text-lg",children:"❌"})}),r("div",{className:"text-sm",children:[e("p",{className:"font-semibold text-red-800",children:"Lỗi:"}),e("ul",{className:"mt-1 list-inside list-disc text-red-700",children:P.map((r,t)=>e("li",{children:r},t))})]})]})]})]}),r(d,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50",children:[e(g,{className:"px-4 py-3",children:r("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm",children:e(w,{className:"h-3.5 w-3.5 text-white"})}),r("div",{children:[r("div",{className:"flex items-center gap-2",children:[e(p,{className:"text-base font-semibold text-gray-800",children:"Giải thích"}),e("span",{className:"inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-600",children:"Không bắt buộc"})]}),e("p",{className:"text-xs text-gray-500 mt-0.5",children:"Giải thích cho đáp án đúng — hiển thị sau khi học sinh hoàn thành câu hỏi"})]})]})}),e(u,{className:"px-4 pb-4",children:e(c,{id:"explanation",value:oe,onChange:e=>le(e.target.value),placeholder:"VD: Câu trả lời đúng là... vì...",rows:3,className:"min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none"})})]})]})}export function MatchByWritingAnswerCreator(r){return e(S,{...r})}
@@ -1,3 +1 @@
1
- export { MatchByWritingAnswerCreator } from './MatchByWritingAnswerCreator';
2
- export { MatchByWritingAnswerClient } from './MatchByWritingAnswerClient';
3
- export { matchByWritingAnswerRegistration } from './register';
1
+ export{MatchByWritingAnswerCreator}from"./MatchByWritingAnswerCreator";export{MatchByWritingAnswerClient}from"./MatchByWritingAnswerClient";export{matchByWritingAnswerRegistration}from"./register";