@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,448 +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, useMemo } from 'react';
5
- import { Button } from '../../../../components/ui/button';
6
- import { Label } from '../../../../components/ui/label';
7
- import { Input } from '../../../../components/ui/input';
8
- import { Textarea } from '../../../../components/ui/textarea';
9
- import { PointsInput } from '../../../../components/ui/points-input';
10
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
11
- import { Switch } from '../../../../components/ui/switch';
12
- import { Pencil, Eye, Lightbulb, Type, MessageSquare, ToggleLeft, Plus, Trash2, ListChecks, ImageIcon, BookOpen } from 'lucide-react';
13
- import { FillInBlankClient } from './FillInBlankClient';
14
- import { useDebouncedCallback, usePresignedFileUrl } from '../../../../shared/lib/hooks';
15
- import { FileUpload } from '../../../../components/ui/file-upload';
16
- import { BasicQuestionGroupCard } from '../../groups/BasicQuestionGroupCard';
17
- import { useBasicQuestionGroup } from '../../_shared/hooks/useBasicQuestionGroup';
18
- // Helper: Extract placeholder indices from question text
19
- // e.g., "He {0} 10 years old and she {1} 12." -> [0, 1]
20
- function extractPlaceholderIndices(question) {
21
- const regex = /\{(\d+)\}/g;
22
- const indices = [];
23
- let match;
24
- while ((match = regex.exec(question)) !== null) {
25
- const index = parseInt(match[1], 10);
26
- if (!indices.includes(index)) {
27
- indices.push(index);
28
- }
29
- }
30
- return indices.sort((a, b) => a - b);
31
- }
32
- // Helper: Get the maximum placeholder index
33
- function getMaxPlaceholderIndex(question) {
34
- const indices = extractPlaceholderIndices(question);
35
- return indices.length > 0 ? Math.max(...indices) : -1;
36
- }
37
- // Helper: Determine which group a question belongs to based on its index in the part
38
- function getGroupNumberForIndex(questionIndexInPart, groups) {
39
- if (!groups || groups.length === 0 || questionIndexInPart === undefined)
40
- return 1;
41
- let cumulative = 0;
42
- for (const group of groups) {
43
- cumulative += group.questionCount;
44
- if (questionIndexInPart < cumulative) {
45
- return group.number;
46
- }
47
- }
48
- return groups[groups.length - 1]?.number ?? 1;
49
- }
50
- function FillInBlankCreatorContent({ initialData, onSave, onCancel, onChange, externalErrors, onUnsavedChangesChange, validationRef, groups, questionIndexInPart, partId, headerExtra, }) {
51
- // ============ GROUP (shared title / content / image) ============
52
- const { isGrouped, currentGroupNumber, groupTitle, groupSubtitle, groupContent, imageUrl, setGroupTitle, setGroupSubtitle, setGroupContent, setImageUrl, } = useBasicQuestionGroup({
53
- partId,
54
- groups,
55
- questionIndexInPart,
56
- initialData: {
57
- title: (() => {
58
- if (initialData?.content?.passageTitle)
59
- return initialData.content.passageTitle;
60
- const qgPayload = initialData?.questionGroup?.payload;
61
- if (qgPayload?.title)
62
- return qgPayload.title;
63
- if (initialData?.title)
64
- return initialData.title;
65
- return '';
66
- })(),
67
- subtitle: (() => {
68
- if (initialData?.content?.passageSubtitle)
69
- return initialData.content.passageSubtitle;
70
- const qgPayload = initialData?.questionGroup?.payload;
71
- if (qgPayload?.subtitle)
72
- return qgPayload.subtitle;
73
- return '';
74
- })(),
75
- content: (() => {
76
- if (initialData?.content?.passage)
77
- return initialData.content.passage;
78
- const qgPayload = initialData?.questionGroup?.payload;
79
- if (qgPayload?.content)
80
- return qgPayload.content;
81
- if (initialData?.groupContent)
82
- return initialData.groupContent;
83
- return '';
84
- })(),
85
- imageUrl: (() => {
86
- if (initialData?.content?.imageUrl)
87
- return initialData.content.imageUrl;
88
- const qgPayload = initialData?.questionGroup?.payload;
89
- if (qgPayload?.imageUrl)
90
- return qgPayload.imageUrl;
91
- if (initialData?.imageUrl)
92
- return initialData.imageUrl;
93
- return '';
94
- })(),
95
- },
96
- });
97
- // ============ STATE ============
98
- // Support NEW API format (content.question + correctAnswer.answers) and legacy formats
99
- const getInitialQuestion = () => {
100
- // NEW API format: content.question
101
- if (initialData?.content?.question)
102
- return initialData.content.question;
103
- // Legacy format: question at root level
104
- if (initialData?.question)
105
- return initialData.question;
106
- // Very old format: blanks[].text
107
- if (initialData?.blanks?.[0]?.text)
108
- return initialData.blanks[0].text;
109
- return '';
110
- };
111
- const getInitialAnswers = () => {
112
- // NEW API format: correctAnswer.answers
113
- if (initialData?.correctAnswer?.answers && Array.isArray(initialData.correctAnswer.answers)) {
114
- return initialData.correctAnswer.answers;
115
- }
116
- // Legacy format: answers at root level
117
- if (initialData?.answers && initialData.answers.length > 0) {
118
- return initialData.answers;
119
- }
120
- // Very old format: blanks[].correctAnswer
121
- if (initialData?.blanks?.[0]?.correctAnswer) {
122
- return [initialData.blanks[0].correctAnswer];
123
- }
124
- return [];
125
- };
126
- const getInitialCaseSensitive = () => {
127
- // NEW API format: correctAnswer.caseSensitive
128
- if (initialData?.correctAnswer?.caseSensitive !== undefined) {
129
- return initialData.correctAnswer.caseSensitive;
130
- }
131
- // Legacy format: caseSensitive at root level
132
- return initialData?.caseSensitive || false;
133
- };
134
- const getInitialShowHints = () => {
135
- // NEW API format: content.showHints
136
- if (initialData?.content?.showHints !== undefined) {
137
- return Boolean(initialData.content.showHints);
138
- }
139
- // Flat format from answer or root level
140
- if (initialData?.showHints !== undefined) {
141
- return Boolean(initialData.showHints);
142
- }
143
- // Default: false
144
- return false;
145
- };
146
- const getInitialHints = () => {
147
- // NEW API format: content.hints
148
- if (Array.isArray(initialData?.content?.hints)) {
149
- return initialData.content.hints;
150
- }
151
- // Flat format from answer or root level
152
- if (Array.isArray(initialData?.hints)) {
153
- return initialData.hints;
154
- }
155
- // Default: empty array
156
- return [];
157
- };
158
- const [question, setQuestion] = useState(getInitialQuestion());
159
- const [answers, setAnswers] = useState(getInitialAnswers());
160
- const [caseSensitive, setCaseSensitive] = useState(getInitialCaseSensitive());
161
- const [explanation, setExplanation] = useState(initialData?.explanation || '');
162
- const [points, setPoints] = useState(initialData?.points || 1);
163
- const [showHints, setShowHints] = useState(getInitialShowHints());
164
- const [hints, setHints] = useState(getInitialHints());
165
- const [newHint, setNewHint] = useState('');
166
- const [isExample, setIsExample] = useState(initialData?.isExample || false);
167
- // UI State
168
- const [isClientMode, setIsClientMode] = useState(false);
169
- const [imagePreviewUrl, setImagePreviewUrl] = useState('');
170
- // Presigned URL for per-question image preview (non-grouped only)
171
- const { previewUrl: generatedPreviewUrl } = usePresignedFileUrl(imageUrl);
172
- const displayPreviewUrl = imagePreviewUrl || generatedPreviewUrl || '';
173
- // Inline validation errors
174
- const [validationErrors, setValidationErrors] = useState({});
175
- // Refs for initialization tracking
176
- const previousInitialDataRef = useRef(initialData);
177
- const isInitializingRef = useRef(false);
178
- const onChangeRef = useRef(onChange);
179
- const previousPayloadRef = useRef('');
180
- // NOTE: Status tracking is now handled entirely by ExamCreator via localStorage (exam_question_status_{id})
181
- // The onChange callback triggers handleQuestionDraftChange in ExamCreator which updates localStorage
182
- // Update refs
183
- useEffect(() => {
184
- onChangeRef.current = onChange;
185
- }, [onChange]);
186
- // Debounced onChange callback
187
- const debouncedOnChange = useDebouncedCallback((data) => {
188
- onChangeRef.current?.(data);
189
- }, 300);
190
- // Extract placeholder indices from question
191
- const placeholderIndices = useMemo(() => extractPlaceholderIndices(question), [question]);
192
- const maxIndex = useMemo(() => getMaxPlaceholderIndex(question), [question]);
193
- // Ensure answers array has correct length when placeholders change
194
- useEffect(() => {
195
- if (maxIndex >= 0) {
196
- const requiredLength = maxIndex + 1;
197
- if (answers.length < requiredLength) {
198
- // Extend array with empty strings
199
- const newAnswers = [...answers];
200
- while (newAnswers.length < requiredLength) {
201
- newAnswers.push('');
202
- }
203
- setAnswers(newAnswers);
204
- }
205
- }
206
- }, [maxIndex, answers.length]);
207
- // Expose validation function via ref
208
- useEffect(() => {
209
- if (validationRef) {
210
- validationRef.current = {
211
- triggerValidation: async () => {
212
- return validateForm();
213
- },
214
- // BUGFIX: Expose getFormData to get fresh form data at save time
215
- // This bypasses the debounced onChange race condition
216
- getFormData: () => ({
217
- question,
218
- answers,
219
- caseSensitive,
220
- explanation,
221
- points,
222
- imageUrl,
223
- showHints,
224
- hints: showHints ? hints : [],
225
- isExample,
226
- title: groupTitle,
227
- subtitle: groupSubtitle,
228
- groupContent,
229
- groupNumber: currentGroupNumber,
230
- groups,
231
- }),
232
- };
233
- }
234
- }, [validationRef, question, answers, caseSensitive, explanation, points, imageUrl, showHints, hints, groupTitle, groupSubtitle, groupContent, currentGroupNumber, groups]);
235
- // ============ SYNC INITIAL DATA ============
236
- useEffect(() => {
237
- const initialDataChanged = JSON.stringify(previousInitialDataRef.current) !== JSON.stringify(initialData);
238
- if (initialDataChanged) {
239
- isInitializingRef.current = true;
240
- previousInitialDataRef.current = initialData;
241
- if (initialData) {
242
- setQuestion(getInitialQuestion());
243
- setAnswers(getInitialAnswers());
244
- setCaseSensitive(getInitialCaseSensitive());
245
- // Explanation can be at root level or in initialData
246
- setExplanation(initialData.explanation || '');
247
- // Points can be at root level
248
- setPoints(initialData.points || 1);
249
- setShowHints(getInitialShowHints());
250
- setHints(getInitialHints());
251
- setIsExample(initialData.isExample || false);
252
- }
253
- else {
254
- setQuestion('');
255
- setAnswers([]);
256
- setCaseSensitive(false);
257
- setExplanation('');
258
- setPoints(1);
259
- setShowHints(false);
260
- setHints([]);
261
- setIsExample(false);
262
- }
263
- setTimeout(() => {
264
- isInitializingRef.current = false;
265
- }, 150);
266
- }
267
- }, [initialData]);
268
- // ============ ON CHANGE CALLBACK ============
269
- useEffect(() => {
270
- if (isInitializingRef.current) {
271
- const currentPayload = JSON.stringify({
272
- question,
273
- answers,
274
- caseSensitive,
275
- explanation,
276
- points,
277
- imageUrl,
278
- showHints,
279
- hints,
280
- isExample,
281
- title: groupTitle,
282
- subtitle: groupSubtitle,
283
- groupContent,
284
- groupNumber: currentGroupNumber,
285
- groups,
286
- });
287
- previousPayloadRef.current = currentPayload;
288
- return;
289
- }
290
- const payload = {
291
- question,
292
- answers,
293
- caseSensitive,
294
- explanation,
295
- points,
296
- imageUrl,
297
- showHints,
298
- hints: showHints ? hints : [],
299
- isExample,
300
- title: groupTitle,
301
- subtitle: groupSubtitle,
302
- groupContent,
303
- groupNumber: currentGroupNumber,
304
- groups,
305
- };
306
- const payloadString = JSON.stringify(payload);
307
- if (previousPayloadRef.current !== payloadString) {
308
- previousPayloadRef.current = payloadString;
309
- debouncedOnChange(payload);
310
- }
311
- }, [question, answers, caseSensitive, explanation, points, imageUrl, showHints, hints, isExample, groupTitle, groupSubtitle, groupContent, currentGroupNumber, groups]);
312
- // ============ VALIDATION ============
313
- const validateForm = () => {
314
- const errors = {};
315
- if (!question.trim()) {
316
- errors.question = 'Vui lòng nhập câu hỏi!';
317
- }
318
- else if (placeholderIndices.length === 0) {
319
- errors.question = 'Câu hỏi cần có ít nhất một chỗ trống {0}!';
320
- }
321
- // Check if placeholder indices are sequential starting from 0
322
- const expectedIndices = Array.from({ length: placeholderIndices.length }, (_, i) => i);
323
- const hasGaps = !placeholderIndices.every((idx, i) => idx === expectedIndices[i]);
324
- if (hasGaps && placeholderIndices.length > 0) {
325
- errors.question = 'Các chỗ trống phải đánh số liên tục từ {0}!';
326
- }
327
- // Validate each answer - use placeholderIndices or default [0]
328
- errors.answers = {};
329
- const indicesToValidate = placeholderIndices.length > 0 ? placeholderIndices : [0];
330
- indicesToValidate.forEach((idx) => {
331
- if (!answers[idx] || !answers[idx].trim()) {
332
- errors.answers[idx] = 'Vui lòng nhập đáp án!';
333
- }
334
- });
335
- // Clean up empty answers object
336
- if (Object.keys(errors.answers).length === 0) {
337
- delete errors.answers;
338
- }
339
- setValidationErrors(errors);
340
- return !errors.question && !errors.answers;
341
- };
342
- // Handle save
343
- const handleSave = () => {
344
- if (!validateForm()) {
345
- return;
346
- }
347
- // NOTE: Save status is now managed by ExamCreator via localStorage
348
- const saveData = {
349
- question,
350
- answers,
351
- caseSensitive,
352
- explanation,
353
- points,
354
- imageUrl,
355
- showHints,
356
- hints: showHints ? hints : [],
357
- isExample,
358
- title: groupTitle,
359
- subtitle: groupSubtitle,
360
- groupContent,
361
- groupNumber: currentGroupNumber,
362
- groups,
363
- };
364
- onSave?.(saveData);
365
- };
366
- // Update answer at specific index
367
- const updateAnswer = (index, value) => {
368
- const newAnswers = [...answers];
369
- // Ensure array is long enough
370
- while (newAnswers.length <= index) {
371
- newAnswers.push('');
372
- }
373
- newAnswers[index] = value;
374
- setAnswers(newAnswers);
375
- // Clear validation error for this answer
376
- if (validationErrors.answers?.[index]) {
377
- setValidationErrors(prev => {
378
- const newAnswerErrors = { ...prev.answers };
379
- delete newAnswerErrors[index];
380
- return {
381
- ...prev,
382
- answers: Object.keys(newAnswerErrors).length > 0 ? newAnswerErrors : undefined,
383
- };
384
- });
385
- }
386
- };
387
- // Generate preview text with answers filled in
388
- const getPreviewText = () => {
389
- let preview = question;
390
- placeholderIndices.forEach((idx) => {
391
- const answer = answers[idx] || '_____';
392
- preview = preview.replace(`{${idx}}`, `[${answer}]`);
393
- });
394
- return preview;
395
- };
396
- // ============ CLIENT MODE PREVIEW ============
397
- if (isClientMode) {
398
- const questionData = {
399
- question,
400
- answers,
401
- caseSensitive,
402
- explanation,
403
- imageUrl: isGrouped ? '' : (displayPreviewUrl || imageUrl),
404
- showHints,
405
- hints: showHints ? hints : [],
406
- isExample,
407
- // Group fields for Client preview
408
- isGroup: isGrouped,
409
- groupTitle: isGrouped ? groupTitle : '',
410
- groupSubtitle: isGrouped ? groupSubtitle : '',
411
- groupContent: isGrouped ? groupContent : '',
412
- groupImageUrl: isGrouped ? (displayPreviewUrl || imageUrl) : '',
413
- groupNumber: isGrouped ? currentGroupNumber : undefined,
414
- };
415
- return (_jsxs("div", { className: "space-y-4", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(CardTitle, { children: "Fill In Blank (Preview)" }), _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(FillInBlankClient, { questionData: questionData, autoFillCorrectAnswers: true, isReviewMode: false, explanation: explanation })] }));
416
- }
417
- // ============ CREATOR UI ============
418
- return (_jsxs("div", { className: "space-y-4", children: [isGrouped && (_jsx(BasicQuestionGroupCard, { groupNumber: currentGroupNumber, title: groupTitle, subtitle: groupSubtitle, content: groupContent, imageUrl: imageUrl, onTitleChange: setGroupTitle, onSubtitleChange: setGroupSubtitle, onContentChange: setGroupContent, onImageChange: setImageUrl, uploadIdPrefix: `fill-in-blank-${partId ?? 'group'}` })), _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-gradient-to-br from-blue-500 to-indigo-600 shadow-sm", children: _jsx(Type, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-base font-bold text-gray-900", children: "\u0110i\u1EC1n v\u00E0o ch\u1ED7 tr\u1ED1ng" }), _jsx("p", { className: "text-xs text-gray-500", children: "H\u1ED7 tr\u1EE3 nhi\u1EC1u ch\u1ED7 tr\u1ED1ng trong m\u1ED9t c\u00E2u h\u1ECFi" })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [headerExtra, _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: !question?.trim() || placeholderIndices.length === 0, className: "gap-2 border-indigo-200 text-indigo-600 hover:bg-indigo-50 hover:text-indigo-700", children: [_jsx(Eye, { className: "h-4 w-4" }), "Xem tr\u01B0\u1EDBc"] })] })] }) }), _jsxs(CardContent, { className: "space-y-4 px-4 pb-4", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-6", children: [_jsx(PointsInput, { defaultValue: points, onChange: (e) => setPoints(Number(e.target.value) || 1) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: "show-suggested-words", checked: showHints, onCheckedChange: setShowHints }), _jsx(Label, { htmlFor: "show-suggested-words", className: "cursor-pointer text-sm font-medium text-gray-700", children: "Hi\u1EC3n th\u1ECB t\u1EEB g\u1EE3i \u00FD" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: "isExample", checked: isExample, onCheckedChange: setIsExample }), _jsx(Label, { htmlFor: "isExample", className: "cursor-pointer text-sm font-medium text-gray-700", children: "\u0110\u00E1nh d\u1EA5u l\u00E0 c\u00E2u h\u1ECFi Example" })] })] }), _jsx("div", { className: "rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-5 shadow-sm", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-blue-100", children: _jsx(Lightbulb, { className: "h-4 w-4 text-blue-600" }) }), _jsxs("div", { className: "text-sm text-blue-900", children: [_jsx("p", { className: "font-semibold text-blue-800", children: "H\u01B0\u1EDBng d\u1EABn t\u1EA1o c\u00E2u h\u1ECFi" }), _jsxs("ul", { className: "mt-2 space-y-1.5 text-blue-700", children: [_jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-blue-400" }), "S\u1EED d\u1EE5ng ", _jsx("code", { className: "mx-1 rounded bg-blue-100 px-1.5 py-0.5 font-mono text-blue-800", children: '{0}' }), ", ", _jsx("code", { className: "mx-1 rounded bg-blue-100 px-1.5 py-0.5 font-mono text-blue-800", children: '{1}' }), ", ", _jsx("code", { className: "mx-1 rounded bg-blue-100 px-1.5 py-0.5 font-mono text-blue-800", children: '{2}' }), "... \u0111\u1EC3 \u0111\u00E1nh d\u1EA5u c\u00E1c ch\u1ED7 tr\u1ED1ng"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-blue-400" }), "H\u1EC7 th\u1ED1ng s\u1EBD t\u1EF1 \u0111\u1ED9ng t\u1EA1o \u00F4 nh\u1EADp \u0111\u00E1p \u00E1n cho m\u1ED7i ch\u1ED7 tr\u1ED1ng"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-blue-400" }), "V\u00ED d\u1EE5: \u201CHe ", '{0}', " 10 years old and she ", '{1}', " 12.\u201D"] })] })] })] }) }), !isGrouped && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-indigo-500" }), "H\u00ECnh \u1EA3nh ", _jsx("span", { className: "text-sm font-normal text-gray-500", children: "(T\u00F9y ch\u1ECDn)" })] }), _jsx(FileUpload, { id: "fill-in-blank-image", label: "", accept: "image/*", value: imageUrl || '', onChange: (url) => {
419
- setImageUrl(url);
420
- }, 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" }))] })), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "question", className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-4 w-4 text-indigo-500" }), "C\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Textarea, { id: "question", value: question, onChange: (e) => {
421
- setQuestion(e.target.value);
422
- if (validationErrors.question) {
423
- setValidationErrors(prev => ({ ...prev, question: undefined }));
424
- }
425
- }, placeholder: "V\u00ED d\u1EE5: He {0} 10 years old and she {1} 12.", rows: 3, className: `min-h-[100px] border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-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 })), placeholderIndices.length > 0 && (_jsx("div", { className: "flex items-center gap-2 rounded-lg bg-green-50 px-3 py-2", children: _jsxs("span", { className: "text-sm text-green-700", children: ["\u2713 \u0110\u00E3 ph\u00E1t hi\u1EC7n ", _jsx("strong", { children: placeholderIndices.length }), " ch\u1ED7 tr\u1ED1ng: ", placeholderIndices.map(i => (_jsx("code", { className: "mx-0.5 rounded bg-green-100 px-1.5 py-0.5 font-mono text-green-800", children: `{${i}}` }, i)))] }) }))] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100", children: _jsx(Type, { className: "h-3.5 w-3.5 text-indigo-600" }) }), _jsxs(Label, { className: "text-base font-semibold text-gray-800", children: ["\u0110\u00E1p \u00E1n cho c\u00E1c ch\u1ED7 tr\u1ED1ng ", _jsx("span", { className: "text-red-500", children: "*" })] })] }), _jsxs("div", { className: "grid gap-6 lg:grid-cols-2", children: [_jsx("div", { className: "space-y-3", children: (placeholderIndices.length > 0 ? placeholderIndices : [0]).map((idx) => (_jsxs("div", { className: `rounded-xl border-2 p-4 transition-all ${answers[idx]
426
- ? 'border-green-200 bg-gradient-to-r from-green-50 to-emerald-50'
427
- : 'border-gray-200 bg-white'}`, children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: `flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl font-mono font-bold transition-all ${answers[idx]
428
- ? 'bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md'
429
- : 'bg-indigo-100 text-indigo-700'}`, children: `{${idx}}` }), _jsx(Input, { value: answers[idx] || '', onChange: (e) => updateAnswer(idx, e.target.value), placeholder: `Đáp án cho {${idx}}`, className: `border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${validationErrors.answers?.[idx] ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` })] }), validationErrors.answers?.[idx] && (_jsx("p", { className: "mt-2 ml-13 text-sm text-red-600", children: validationErrors.answers[idx] }))] }, idx))) }), showHints && (_jsxs("div", { className: "space-y-4 rounded-xl border-2 border-purple-200 bg-gradient-to-r from-purple-50 to-indigo-50 p-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-purple-100", children: _jsx(ListChecks, { className: "h-3.5 w-3.5 text-purple-600" }) }), _jsxs(Label, { className: "text-base font-semibold text-gray-800", children: ["Danh s\u00E1ch t\u1EEB g\u1EE3i \u00FD ", _jsxs("span", { className: "text-sm font-normal text-gray-500", children: ["(", hints.length, " t\u1EEB)"] })] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { value: newHint, onChange: (e) => setNewHint(e.target.value), placeholder: "Nh\u1EADp t\u1EEB g\u1EE3i \u00FD m\u1EDBi...", className: "flex-1 border-gray-200 bg-white", onKeyDown: (e) => {
430
- if (e.key === 'Enter' && newHint.trim()) {
431
- e.preventDefault();
432
- if (!hints.includes(newHint.trim())) {
433
- setHints([...hints, newHint.trim()]);
434
- }
435
- setNewHint('');
436
- }
437
- } }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => {
438
- if (newHint.trim() && !hints.includes(newHint.trim())) {
439
- setHints([...hints, newHint.trim()]);
440
- setNewHint('');
441
- }
442
- }, disabled: !newHint.trim(), className: "gap-2 border-green-200 text-green-600 hover:bg-green-50 hover:text-green-700", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm"] })] }), hints.length > 0 ? (_jsx("div", { className: "flex flex-wrap gap-2", children: hints.map((word, index) => (_jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-purple-200 bg-white px-3 py-2 shadow-sm transition-all hover:border-purple-300 hover:shadow", children: [_jsx("span", { className: "text-sm font-medium text-gray-700", children: word }), _jsx("button", { type: "button", onClick: () => {
443
- setHints(hints.filter((_, i) => i !== index));
444
- }, className: "rounded-full p-0.5 text-gray-400 transition-colors hover:bg-red-100 hover:text-red-500", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }, index))) })) : (_jsx("p", { className: "text-center text-sm text-gray-500 italic", children: "Ch\u01B0a c\u00F3 t\u1EEB g\u1EE3i \u00FD n\u00E0o. Th\u00EAm t\u1EEB \u0111\u1EC3 hi\u1EC3n th\u1ECB cho h\u1ECDc sinh." }))] }))] })] }), question && (_jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2 text-xs font-semibold text-gray-600", children: [_jsx(Eye, { className: "h-3.5 w-3.5" }), "Xem tr\u01B0\u1EDBc c\u00E2u ho\u00E0n ch\u1EC9nh"] }), _jsx("p", { className: "text-sm text-gray-700", children: getPreviewText() })] })), _jsxs("div", { className: "flex items-center justify-between rounded-xl border-2 border-gray-200 bg-white p-4 transition-all hover:border-gray-300", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gray-100", children: _jsx(ToggleLeft, { className: "h-4 w-4 text-gray-600" }) }), _jsxs("div", { className: "space-y-0.5", children: [_jsx(Label, { htmlFor: "case-sensitive", className: "font-semibold text-gray-800", children: "Ph\u00E2n bi\u1EC7t hoa/th\u01B0\u1EDDng" }), _jsx("p", { className: "text-sm text-gray-500", children: "B\u1EADt n\u1EBFu \u0111\u00E1p \u00E1n c\u1EA7n ph\u00E2n bi\u1EC7t ch\u1EEF hoa/th\u01B0\u1EDDng" })] })] }), _jsx(Switch, { id: "case-sensitive", checked: caseSensitive, onCheckedChange: setCaseSensitive })] }), 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" }) })] })] }));
445
- }
446
- export function FillInBlankCreator(props) {
447
- return _jsx(FillInBlankCreatorContent, { ...props });
448
- }
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{ImagePreview as n}from"../../../../components/ui/image-preview";import{useState as r,useRef as s,useEffect as i,useMemo as a}from"react";import{Button as l}from"../../../../components/ui/button";import{Label as o}from"../../../../components/ui/label";import{Input as c}from"../../../../components/ui/input";import{Textarea as d}from"../../../../components/ui/textarea";import{PointsInput as m}from"../../../../components/ui/points-input";import{Card as h,CardContent as g,CardHeader as u,CardTitle as p}from"../../../../components/ui/card";import{Switch as x}from"../../../../components/ui/switch";import{Pencil as b,Eye as f,Lightbulb as N,Type as w,MessageSquare as v,ToggleLeft as y,Plus as C,Trash2 as k,ListChecks as q,ImageIcon as U,BookOpen as S}from"lucide-react";import{FillInBlankClient as j}from"./FillInBlankClient";import{useDebouncedCallback as A,usePresignedFileUrl as E}from"../../../../shared/lib/hooks";import{FileUpload as H}from"../../../../components/ui/file-upload";import{BasicQuestionGroupCard as G}from"../../groups/BasicQuestionGroupCard";import{useBasicQuestionGroup as I}from"../../_shared/hooks/useBasicQuestionGroup";function T(e){const t=/\{(\d+)\}/g,n=[];let r;for(;null!==(r=t.exec(e));){const e=parseInt(r[1],10);n.includes(e)||n.push(e)}return n.sort((e,t)=>e-t)}function F({initialData:F,onSave:B,onCancel:D,onChange:P,externalErrors:V,onUnsavedChangesChange:O,validationRef:$,groups:_,questionIndexInPart:z,partId:J,headerExtra:M}){const{isGrouped:Q,currentGroupNumber:R,groupTitle:K,groupSubtitle:L,groupContent:X,imageUrl:W,setGroupTitle:Y,setGroupSubtitle:Z,setGroupContent:ee,setImageUrl:te}=I({partId:J,groups:_,questionIndexInPart:z,initialData:{title:(()=>{if(F?.content?.passageTitle)return F.content.passageTitle;const e=F?.questionGroup?.payload;return e?.title?e.title:F?.title?F.title:""})(),subtitle:(()=>{if(F?.content?.passageSubtitle)return F.content.passageSubtitle;const e=F?.questionGroup?.payload;return e?.subtitle?e.subtitle:""})(),content:(()=>{if(F?.content?.passage)return F.content.passage;const e=F?.questionGroup?.payload;return e?.content?e.content:F?.groupContent?F.groupContent:""})(),imageUrl:(()=>{if(F?.content?.imageUrl)return F.content.imageUrl;const e=F?.questionGroup?.payload;return e?.imageUrl?e.imageUrl:F?.imageUrl?F.imageUrl:""})()}}),ne=()=>F?.content?.question?F.content.question:F?.question?F.question:F?.blanks?.[0]?.text?F.blanks[0].text:"",re=()=>F?.correctAnswer?.answers&&Array.isArray(F.correctAnswer.answers)?F.correctAnswer.answers:F?.answers&&F.answers.length>0?F.answers:F?.blanks?.[0]?.correctAnswer?[F.blanks[0].correctAnswer]:[],se=()=>void 0!==F?.correctAnswer?.caseSensitive?F.correctAnswer.caseSensitive:F?.caseSensitive||!1,ie=()=>void 0!==F?.content?.showHints?Boolean(F.content.showHints):void 0!==F?.showHints&&Boolean(F.showHints),ae=()=>Array.isArray(F?.content?.hints)?F.content.hints:Array.isArray(F?.hints)?F.hints:[],[le,oe]=r(ne()),[ce,de]=r(re()),[me,he]=r(se()),[ge,ue]=r(F?.explanation||""),[pe,xe]=r(F?.points||1),[be,fe]=r(ie()),[Ne,we]=r(ae()),[ve,ye]=r(""),[Ce,ke]=r(F?.isExample||!1),[qe,Ue]=r(!1),[Se,je]=r(""),{previewUrl:Ae}=E(W),Ee=Se||Ae||"",[He,Ge]=r({}),Ie=s(F),Te=s(!1),Fe=s(P),Be=s("");i(()=>{Fe.current=P},[P]);const De=A(e=>{Fe.current?.(e)},300),Pe=a(()=>T(le),[le]),Ve=a(()=>function(e){const t=T(e);return t.length>0?Math.max(...t):-1}(le),[le]);i(()=>{if(Ve>=0){const e=Ve+1;if(ce.length<e){const t=[...ce];for(;t.length<e;)t.push("");de(t)}}},[Ve,ce.length]),i(()=>{$&&($.current={triggerValidation:async()=>Oe(),getFormData:()=>({question:le,answers:ce,caseSensitive:me,explanation:ge,points:pe,imageUrl:W,showHints:be,hints:be?Ne:[],isExample:Ce,title:K,subtitle:L,groupContent:X,groupNumber:R,groups:_})})},[$,le,ce,me,ge,pe,W,be,Ne,K,L,X,R,_]),i(()=>{JSON.stringify(Ie.current)!==JSON.stringify(F)&&(Te.current=!0,Ie.current=F,F?(oe(ne()),de(re()),he(se()),ue(F.explanation||""),xe(F.points||1),fe(ie()),we(ae()),ke(F.isExample||!1)):(oe(""),de([]),he(!1),ue(""),xe(1),fe(!1),we([]),ke(!1)),setTimeout(()=>{Te.current=!1},150))},[F]),i(()=>{if(Te.current){const e=JSON.stringify({question:le,answers:ce,caseSensitive:me,explanation:ge,points:pe,imageUrl:W,showHints:be,hints:Ne,isExample:Ce,title:K,subtitle:L,groupContent:X,groupNumber:R,groups:_});return void(Be.current=e)}const e={question:le,answers:ce,caseSensitive:me,explanation:ge,points:pe,imageUrl:W,showHints:be,hints:be?Ne:[],isExample:Ce,title:K,subtitle:L,groupContent:X,groupNumber:R,groups:_},t=JSON.stringify(e);Be.current!==t&&(Be.current=t,De(e))},[le,ce,me,ge,pe,W,be,Ne,Ce,K,L,X,R,_]);const Oe=()=>{const e={};le.trim()?0===Pe.length&&(e.question="Câu hỏi cần có ít nhất một chỗ trống {0}!"):e.question="Vui lòng nhập câu hỏi!";const t=Array.from({length:Pe.length},(e,t)=>t);!Pe.every((e,n)=>e===t[n])&&Pe.length>0&&(e.question="Các chỗ trống phải đánh số liên tục từ {0}!"),e.answers={};return(Pe.length>0?Pe:[0]).forEach(t=>{ce[t]&&ce[t].trim()||(e.answers[t]="Vui lòng nhập đáp án!")}),0===Object.keys(e.answers).length&&delete e.answers,Ge(e),!e.question&&!e.answers};if(qe){const n={question:le,answers:ce,caseSensitive:me,explanation:ge,imageUrl:Q?"":Ee||W,showHints:be,hints:be?Ne:[],isExample:Ce,isGroup:Q,groupTitle:Q?K:"",groupSubtitle:Q?L:"",groupContent:Q?X:"",groupImageUrl:Q?Ee||W:"",groupNumber:Q?R:void 0};return t("div",{className:"space-y-4",children:[e(h,{children:e(u,{children:t("div",{className:"flex items-center justify-between",children:[e(p,{children:"Fill In Blank (Preview)"}),t(l,{variant:"outline",size:"sm",onClick:()=>Ue(!1),children:[e(b,{className:"mr-2 h-4 w-4"}),"Quay lại Edit Mode"]})]})})}),e(j,{questionData:n,autoFillCorrectAnswers:!0,isReviewMode:!1,explanation:ge})]})}return t("div",{className:"space-y-4",children:[Q&&e(G,{groupNumber:R,title:K,subtitle:L,content:X,imageUrl:W,onTitleChange:Y,onSubtitleChange:Z,onContentChange:ee,onImageChange:te,uploadIdPrefix:`fill-in-blank-${J??"group"}`}),t(h,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50",children:[e(u,{className:"px-4 py-3",children:t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 shadow-sm",children:e(w,{className:"h-4 w-4 text-white"})}),t("div",{children:[e(p,{className:"text-base font-bold text-gray-900",children:"Điền vào chỗ trống"}),e("p",{className:"text-xs text-gray-500",children:"Hỗ trợ nhiều chỗ trống trong một câu hỏi"})]})]}),t("div",{className:"flex items-center gap-2",children:[M,t(l,{variant:"outline",size:"sm",onClick:()=>Ue(!0),disabled:!le?.trim()||0===Pe.length,className:"gap-2 border-indigo-200 text-indigo-600 hover:bg-indigo-50 hover:text-indigo-700",children:[e(f,{className:"h-4 w-4"}),"Xem trước"]})]})]})}),t(g,{className:"space-y-4 px-4 pb-4",children:[t("div",{className:"flex flex-wrap items-center gap-6",children:[e(m,{defaultValue:pe,onChange:e=>xe(Number(e.target.value)||1)}),t("div",{className:"flex items-center gap-2",children:[e(x,{id:"show-suggested-words",checked:be,onCheckedChange:fe}),e(o,{htmlFor:"show-suggested-words",className:"cursor-pointer text-sm font-medium text-gray-700",children:"Hiển thị từ gợi ý"})]}),t("div",{className:"flex items-center gap-2",children:[e(x,{id:"isExample",checked:Ce,onCheckedChange:ke}),e(o,{htmlFor:"isExample",className:"cursor-pointer text-sm font-medium text-gray-700",children:"Đánh dấu là câu hỏi Example"})]})]}),e("div",{className:"rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-5 shadow-sm",children:t("div",{className:"flex items-start gap-3",children:[e("div",{className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-blue-100",children:e(N,{className:"h-4 w-4 text-blue-600"})}),t("div",{className:"text-sm text-blue-900",children:[e("p",{className:"font-semibold text-blue-800",children:"Hướng dẫn tạo câu hỏi"}),t("ul",{className:"mt-2 space-y-1.5 text-blue-700",children:[t("li",{className:"flex items-center gap-2",children:[e("span",{className:"h-1.5 w-1.5 rounded-full bg-blue-400"}),"Sử dụng ",e("code",{className:"mx-1 rounded bg-blue-100 px-1.5 py-0.5 font-mono text-blue-800",children:"{0}"}),", ",e("code",{className:"mx-1 rounded bg-blue-100 px-1.5 py-0.5 font-mono text-blue-800",children:"{1}"}),", ",e("code",{className:"mx-1 rounded bg-blue-100 px-1.5 py-0.5 font-mono text-blue-800",children:"{2}"}),"... để đánh dấu các chỗ trống"]}),t("li",{className:"flex items-center gap-2",children:[e("span",{className:"h-1.5 w-1.5 rounded-full bg-blue-400"}),"Hệ thống sẽ tự động tạo ô nhập đáp án cho mỗi chỗ trống"]}),t("li",{className:"flex items-center gap-2",children:[e("span",{className:"h-1.5 w-1.5 rounded-full bg-blue-400"}),"Ví dụ: “He ","{0}"," 10 years old and she ","{1}"," 12.”"]})]})]})]})}),!Q&&t("div",{className:"space-y-2",children:[t(o,{className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(U,{className:"h-4 w-4 text-indigo-500"}),"Hình ảnh ",e("span",{className:"text-sm font-normal text-gray-500",children:"(Tùy chọn)"})]}),e(H,{id:"fill-in-blank-image",label:"",accept:"image/*",value:W||"",onChange:e=>{te(e)},onPresignedUrlChange:je,maxSize:5,placeholder:"Upload ảnh hoặc paste URL",autoUpload:!0,prefix:"questions"}),Ee&&e(n,{src:Ee,alt:"Preview",className:"mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200"})]}),t("div",{className:"space-y-2",children:[t(o,{htmlFor:"question",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(v,{className:"h-4 w-4 text-indigo-500"}),"Câu hỏi ",e("span",{className:"text-red-500",children:"*"})]}),e(d,{id:"question",value:le,onChange:e=>{oe(e.target.value),He.question&&Ge(e=>({...e,question:void 0}))},placeholder:"Ví dụ: He {0} 10 years old and she {1} 12.",rows:3,className:"min-h-[100px] border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 "+(He.question?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),He.question&&e("p",{className:"text-sm text-red-600",children:He.question}),Pe.length>0&&e("div",{className:"flex items-center gap-2 rounded-lg bg-green-50 px-3 py-2",children:t("span",{className:"text-sm text-green-700",children:["✓ Đã phát hiện ",e("strong",{children:Pe.length})," chỗ trống: ",Pe.map(t=>e("code",{className:"mx-0.5 rounded bg-green-100 px-1.5 py-0.5 font-mono text-green-800",children:`{${t}}`},t))]})})]}),t("div",{className:"space-y-4",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100",children:e(w,{className:"h-3.5 w-3.5 text-indigo-600"})}),t(o,{className:"text-base font-semibold text-gray-800",children:["Đáp án cho các chỗ trống ",e("span",{className:"text-red-500",children:"*"})]})]}),t("div",{className:"grid gap-6 lg:grid-cols-2",children:[e("div",{className:"space-y-3",children:(Pe.length>0?Pe:[0]).map(n=>t("div",{className:"rounded-xl border-2 p-4 transition-all "+(ce[n]?"border-green-200 bg-gradient-to-r from-green-50 to-emerald-50":"border-gray-200 bg-white"),children:[t("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl font-mono font-bold transition-all "+(ce[n]?"bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md":"bg-indigo-100 text-indigo-700"),children:`{${n}}`}),e(c,{value:ce[n]||"",onChange:e=>((e,t)=>{const n=[...ce];for(;n.length<=e;)n.push("");n[e]=t,de(n),He.answers?.[e]&&Ge(t=>{const n={...t.answers};return delete n[e],{...t,answers:Object.keys(n).length>0?n:void 0}})})(n,e.target.value),placeholder:`Đáp án cho {${n}}`,className:"border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 "+(He.answers?.[n]?"border-red-400 focus:border-red-500 focus:ring-red-200":"")})]}),He.answers?.[n]&&e("p",{className:"mt-2 ml-13 text-sm text-red-600",children:He.answers[n]})]},n))}),be&&t("div",{className:"space-y-4 rounded-xl border-2 border-purple-200 bg-gradient-to-r from-purple-50 to-indigo-50 p-4",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-purple-100",children:e(q,{className:"h-3.5 w-3.5 text-purple-600"})}),t(o,{className:"text-base font-semibold text-gray-800",children:["Danh sách từ gợi ý ",t("span",{className:"text-sm font-normal text-gray-500",children:["(",Ne.length," từ)"]})]})]}),t("div",{className:"flex gap-2",children:[e(c,{value:ve,onChange:e=>ye(e.target.value),placeholder:"Nhập từ gợi ý mới...",className:"flex-1 border-gray-200 bg-white",onKeyDown:e=>{"Enter"===e.key&&ve.trim()&&(e.preventDefault(),Ne.includes(ve.trim())||we([...Ne,ve.trim()]),ye(""))}}),t(l,{type:"button",variant:"outline",size:"sm",onClick:()=>{ve.trim()&&!Ne.includes(ve.trim())&&(we([...Ne,ve.trim()]),ye(""))},disabled:!ve.trim(),className:"gap-2 border-green-200 text-green-600 hover:bg-green-50 hover:text-green-700",children:[e(C,{className:"h-4 w-4"}),"Thêm"]})]}),Ne.length>0?e("div",{className:"flex flex-wrap gap-2",children:Ne.map((n,r)=>t("div",{className:"flex items-center gap-2 rounded-lg border border-purple-200 bg-white px-3 py-2 shadow-sm transition-all hover:border-purple-300 hover:shadow",children:[e("span",{className:"text-sm font-medium text-gray-700",children:n}),e("button",{type:"button",onClick:()=>{we(Ne.filter((e,t)=>t!==r))},className:"rounded-full p-0.5 text-gray-400 transition-colors hover:bg-red-100 hover:text-red-500",children:e(k,{className:"h-3.5 w-3.5"})})]},r))}):e("p",{className:"text-center text-sm text-gray-500 italic",children:"Chưa có từ gợi ý nào. Thêm từ để hiển thị cho học sinh."})]})]})]}),le&&t("div",{className:"rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4",children:[t("div",{className:"mb-2 flex items-center gap-2 text-xs font-semibold text-gray-600",children:[e(f,{className:"h-3.5 w-3.5"}),"Xem trước câu hoàn chỉnh"]}),e("p",{className:"text-sm text-gray-700",children:(()=>{let e=le;return Pe.forEach(t=>{const n=ce[t]||"_____";e=e.replace(`{${t}}`,`[${n}]`)}),e})()})]}),t("div",{className:"flex items-center justify-between rounded-xl border-2 border-gray-200 bg-white p-4 transition-all hover:border-gray-300",children:[t("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gray-100",children:e(y,{className:"h-4 w-4 text-gray-600"})}),t("div",{className:"space-y-0.5",children:[e(o,{htmlFor:"case-sensitive",className:"font-semibold text-gray-800",children:"Phân biệt hoa/thường"}),e("p",{className:"text-sm text-gray-500",children:"Bật nếu đáp án cần phân biệt chữ hoa/thường"})]})]}),e(x,{id:"case-sensitive",checked:me,onCheckedChange:he})]}),V&&V.length>0&&t("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:"❌"})}),t("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:V.map((t,n)=>e("li",{children:t},n))})]})]})]})]}),t(h,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50",children:[e(u,{className:"px-4 py-3",children:t("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(S,{className:"h-3.5 w-3.5 text-white"})}),t("div",{children:[t("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(g,{className:"px-4 pb-4",children:e(d,{id:"explanation",value:ge,onChange:e=>ue(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 FillInBlankCreator(t){return e(F,{...t})}
@@ -1,2 +1 @@
1
- export * from './FillInBlankCreator';
2
- export * from './FillInBlankClient';
1
+ export*from"./FillInBlankCreator";export*from"./FillInBlankClient";