@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,631 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } 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 { Checkbox } from '../../../../components/ui/checkbox';
12
- import { Switch } from '../../../../components/ui/switch';
13
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../../../components/ui/select';
14
- import { usePresignedFileUrl, useDebouncedCallback } from '../../../../shared/lib/hooks';
15
- import { useReactHookForm } from '../../../../shared/lib/hooks/useReactHookForm';
16
- import { Pencil, Eye, BookOpen, ImageIcon, Lightbulb, FileText, Library, ToggleLeft, Plus, Trash2, ListChecks, CheckCircle2, Mail, Star, } from 'lucide-react';
17
- import { FileUpload } from '../../../../components/ui/file-upload';
18
- import { WordFillParagraphClient } from './WordFillParagraphClient';
19
- import { BasicQuestionGroupCard } from '../../groups/BasicQuestionGroupCard';
20
- import { useBasicQuestionGroup } from '../../_shared/hooks/useBasicQuestionGroup';
21
- import { wordFillParagraphFormSchema, } from '../../../../shared/lib/validation/word-fill-paragraph.validation';
22
- import { wordFillParagraphWithOptionsFormSchema, } from '../../../../shared/lib/validation/word-fill-paragraph-with-options.validation';
23
- import { wordFillParagraphDefaultFormSchema, } from '../../../../shared/lib/validation/word-fill-paragraph-default.validation';
24
- // ---------------------------------------------------------------------------
25
- // Helpers
26
- // ---------------------------------------------------------------------------
27
- /** Parse {0}, {1}, ... from paragraph and return sorted unique indices */
28
- function parsePlaceholders(paragraph) {
29
- const regex = /\{(\d+)\}/g;
30
- const indices = new Set();
31
- let match;
32
- while ((match = regex.exec(paragraph)) !== null) {
33
- indices.add(parseInt(match[1], 10));
34
- }
35
- return Array.from(indices).sort((a, b) => a - b);
36
- }
37
- /** Small component so we can call usePresignedFileUrl per word-bank item */
38
- function WordBankImagePreview({ imageUrl, localPreviewUrl, index, }) {
39
- const { previewUrl: presignedUrl } = usePresignedFileUrl(imageUrl || '');
40
- const src = localPreviewUrl || presignedUrl || '';
41
- if (!src)
42
- return null;
43
- return (_jsx(ImagePreview, { src: src, alt: `Word bank ${index} preview`, className: "mt-2 h-32 w-full rounded-lg border border-gray-200 bg-gray-50 object-contain" }));
44
- }
45
- // ---------------------------------------------------------------------------
46
- // Shared header / paragraph / image / points fields
47
- // ---------------------------------------------------------------------------
48
- function CommonFields({ form, displayPreviewUrl, setImagePreviewUrl, watchedImageUrl, watchedParagraph, blankCount, externalErrors, showCaseSensitive, watchedCaseSensitive, afterPoints, }) {
49
- return (_jsxs(_Fragment, { children: [showCaseSensitive ? (_jsxs("div", { className: "flex flex-wrap items-center gap-4", children: [_jsx("div", { className: "flex-1 rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: _jsx(PointsInput, { ...form.register('points', { valueAsNumber: true }), error: form.hasError('points') ? form.getFieldError('points') : undefined }) }), _jsxs("div", { className: `flex items-center gap-3 rounded-xl border-2 px-4 py-3 transition-all ${watchedCaseSensitive
50
- ? 'border-teal-400 bg-teal-50'
51
- : 'border-gray-200 bg-white hover:border-gray-300'}`, children: [_jsx("div", { className: `flex h-8 w-8 items-center justify-center rounded-lg transition-colors ${watchedCaseSensitive ? 'bg-teal-100' : 'bg-gray-100'}`, children: _jsx(ToggleLeft, { className: `h-4 w-4 transition-colors ${watchedCaseSensitive ? 'text-teal-600' : 'text-gray-400'}` }) }), _jsxs("div", { className: "space-y-0.5", children: [_jsx(Label, { htmlFor: "case-sensitive", className: `font-semibold transition-colors ${watchedCaseSensitive ? 'text-teal-800' : 'text-gray-800'}`, children: "Ph\u00E2n bi\u1EC7t hoa/th\u01B0\u1EDDng" }), _jsx("p", { className: `text-sm transition-colors ${watchedCaseSensitive ? 'text-teal-600' : 'text-gray-500'}`, children: watchedCaseSensitive
52
- ? 'Đang bật — đáp án phân biệt chữ hoa/thường'
53
- : 'Đang tắt — không phân biệt chữ hoa/thường' })] }), _jsx(Switch, { id: "case-sensitive", checked: watchedCaseSensitive || false, onCheckedChange: (checked) => form.setValue('caseSensitive', !!checked), className: watchedCaseSensitive
54
- ? 'data-[state=checked]:bg-teal-500'
55
- : 'data-[state=unchecked]:bg-gray-300' })] })] })) : (_jsx("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: _jsx(PointsInput, { ...form.register('points', { valueAsNumber: true }), error: form.hasError('points') ? form.getFieldError('points') : undefined }) })), afterPoints, _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "title", className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(FileText, { className: "h-4 w-4 text-teal-500" }), "Ti\u00EAu \u0111\u1EC1"] }), _jsx(Input, { id: "title", ...form.register('title'), placeholder: "V\u00ED d\u1EE5: Dogs, My Family...", className: `border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200 ${form.hasError('title') ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), form.hasError('title') && (_jsx("p", { className: "text-sm text-red-600", children: form.getFieldError('title') }))] }), _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 minh h\u1ECDa"] }), _jsx(FileUpload, { id: "question-image", label: "", accept: "image/*", value: watchedImageUrl || '', onChange: (url) => form.setValue('imageUrl', url, { shouldValidate: true }), onPresignedUrlChange: setImagePreviewUrl, maxSize: 5, placeholder: "Upload h\u00ECnh \u1EA3nh ho\u1EB7c d\u00E1n 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: "paragraph", className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(Library, { className: "h-4 w-4 text-teal-500" }), "\u0110o\u1EA1n v\u0103n ", _jsx("span", { className: "text-red-500", children: "*" }), _jsxs("span", { className: "text-xs font-normal text-gray-500", children: ["(S\u1EED d\u1EE5ng ", '{0}', ", ", '{1}', ", ... cho ch\u1ED7 tr\u1ED1ng \u2014 T\u00ECm th\u1EA5y ", blankCount, " ch\u1ED7 tr\u1ED1ng)"] })] }), _jsx(Textarea, { id: "paragraph", ...form.register('paragraph'), placeholder: "Monkeys live in the {0} with their family. They can run with their long {1} ...", rows: 5, className: `border-gray-200 bg-white text-sm transition-all focus:border-teal-300 focus:ring-teal-200 ${form.hasError('paragraph') ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), form.hasError('paragraph') && (_jsx("p", { className: "text-sm text-red-600", children: form.getFieldError('paragraph') }))] }), 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))) })] })] }))] }));
56
- }
57
- // ---------------------------------------------------------------------------
58
- // Unified WordFillParagraphCreator
59
- // ---------------------------------------------------------------------------
60
- export function WordFillParagraphCreator({ initialData, onSave, onCancel, onChange, externalErrors, onUnsavedChangesChange, validationRef, viewMode, groupConfig, groups, questionIndexInPart = 0, partId = '', }) {
61
- const apiData = initialData;
62
- // ============ GROUP (shared title / content / image) via useBasicQuestionGroup ============
63
- const { isGrouped, currentGroupNumber, groupTitle, groupSubtitle, groupContent, imageUrl: groupImageUrl, setGroupTitle, setGroupSubtitle, setGroupContent, setImageUrl: setGroupImageUrl, } = useBasicQuestionGroup({
64
- partId,
65
- groups,
66
- questionIndexInPart,
67
- initialData: {
68
- title: apiData?.groupTitle || '',
69
- subtitle: apiData?.groupSubtitle || '',
70
- content: apiData?.groupContent || '',
71
- imageUrl: apiData?.groupImageUrl || '',
72
- },
73
- });
74
- // Determine which group fields to show based on groupConfig
75
- const hasGroupConfig = groupConfig && groupConfig.length > 0;
76
- const showGroupTitle = !hasGroupConfig || groupConfig.includes('title');
77
- const showGroupSubtitle = !hasGroupConfig || groupConfig.includes('subtitle');
78
- const showGroupContent = !hasGroupConfig || groupConfig.includes('content');
79
- const showGroupImage = !hasGroupConfig || groupConfig.includes('imageUrl');
80
- // ── Form — schema adapts to viewMode ──────────────────────────────────────
81
- const isWithOptions = viewMode === 'WITH_OPTIONS';
82
- const isWithWordBank = viewMode === 'WITH_WORD_BANK';
83
- const isEmail = viewMode === 'EMAIL';
84
- const isDefault = viewMode === 'DEFAULT' || isEmail;
85
- const formWithOptions = useReactHookForm({
86
- defaultValues: {
87
- title: apiData?.title || '',
88
- imageUrl: apiData?.imageUrl || '',
89
- paragraph: apiData?.paragraph || '',
90
- wordBank: apiData?.wordBank?.length > 0 ? apiData.wordBank : [],
91
- answers: apiData?.answers?.length > 0 ? apiData.answers : [],
92
- caseSensitive: apiData?.caseSensitive || false,
93
- explanation: apiData?.explanation || '',
94
- points: apiData?.points ?? 1,
95
- },
96
- zodSchema: wordFillParagraphWithOptionsFormSchema,
97
- mode: 'onBlur',
98
- });
99
- const formWithWordBank = useReactHookForm({
100
- defaultValues: {
101
- title: apiData?.title || '',
102
- imageUrl: apiData?.imageUrl || '',
103
- paragraph: apiData?.paragraph || '',
104
- wordBank: apiData?.wordBank?.length > 0 ? apiData.wordBank : [],
105
- answers: apiData?.answers?.length > 0 ? apiData.answers : [],
106
- exampleAnswer: apiData?.exampleAnswer?.length > 0 ? apiData.exampleAnswer : [],
107
- isExample: apiData?.isExample || false,
108
- caseSensitive: apiData?.caseSensitive || false,
109
- explanation: apiData?.explanation || '',
110
- points: apiData?.points ?? 1,
111
- },
112
- zodSchema: wordFillParagraphFormSchema,
113
- mode: 'onBlur',
114
- });
115
- const formDefault = useReactHookForm({
116
- defaultValues: {
117
- title: apiData?.title || '',
118
- imageUrl: apiData?.imageUrl || '',
119
- paragraph: apiData?.paragraph || '',
120
- answers: apiData?.answers?.length > 0 ? apiData.answers : [],
121
- exampleAnswer: apiData?.exampleAnswer?.length > 0 ? apiData.exampleAnswer
122
- : apiData?.content?.exampleAnswer?.length > 0 ? apiData.content.exampleAnswer
123
- : apiData?.answer?.example?.length > 0 ? apiData.answer.example
124
- : apiData?.correctAnswer?.example?.length > 0 ? apiData.correctAnswer.example
125
- : [],
126
- caseSensitive: apiData?.caseSensitive || false,
127
- explanation: apiData?.explanation || '',
128
- points: apiData?.points ?? 1,
129
- },
130
- zodSchema: wordFillParagraphDefaultFormSchema,
131
- mode: 'onBlur',
132
- });
133
- // Active form — typed as `any` to avoid union-overload TS errors when calling
134
- // shared methods like .register(), .watch(), .reset(), .trigger(), .setValue().
135
- // Field-specific watches always use the concrete form instance directly.
136
- const form = isWithOptions ? formWithOptions : isDefault ? formDefault : formWithWordBank;
137
- // ── State ──────────────────────────────────────────────────────────────────
138
- const [isClientMode, setIsClientMode] = useState(false);
139
- const [imagePreviewUrl, setImagePreviewUrl] = useState('');
140
- const [wordBankImagePreviews, setWordBankImagePreviews] = useState({});
141
- // ── Email fields (only for EMAIL viewMode) ─────────────────────────────────
142
- const [emailFrom, setEmailFrom] = useState(apiData?.emailFrom || '');
143
- const [emailTo, setEmailTo] = useState(apiData?.emailTo || '');
144
- const previousInitialDataRef = useRef(initialData);
145
- const isInitializingRef = useRef(false);
146
- const onChangeRef = useRef(onChange);
147
- const previousPayloadRef = useRef('');
148
- const sentOnChangeRef = useRef(false);
149
- useEffect(() => { onChangeRef.current = onChange; }, [onChange]);
150
- const debouncedOnChange = useDebouncedCallback((data) => {
151
- sentOnChangeRef.current = true;
152
- onChangeRef.current?.(data);
153
- }, 300);
154
- // ── Watch values ───────────────────────────────────────────────────────────
155
- const watchedTitle = form.watch('title');
156
- const watchedImageUrl = form.watch('imageUrl');
157
- const watchedParagraph = form.watch('paragraph');
158
- const watchedWordBank = (isDefault ? [] : form.watch('wordBank'));
159
- const watchedAnswers = form.watch('answers');
160
- const watchedCaseSensitive = form.watch('caseSensitive');
161
- const watchedExplanation = form.watch('explanation');
162
- const watchedPoints = form.watch('points');
163
- // WITH_WORD_BANK or DEFAULT — both carry exampleAnswer (list of answer texts marked as example)
164
- const watchedExampleAnswer = isWithWordBank
165
- ? formWithWordBank.watch('exampleAnswer')
166
- : isDefault
167
- ? (formDefault.watch('exampleAnswer') || [])
168
- : [];
169
- const watchedIsExample = isWithWordBank
170
- ? formWithWordBank.watch('isExample')
171
- : false;
172
- const { previewUrl: generatedPreviewUrl } = usePresignedFileUrl(watchedImageUrl || '');
173
- const displayPreviewUrl = imagePreviewUrl || generatedPreviewUrl || '';
174
- const placeholderIndices = useMemo(() => parsePlaceholders(watchedParagraph), [watchedParagraph]);
175
- const blankCount = placeholderIndices.length;
176
- const availableWords = useMemo(() => isWithWordBank
177
- ? watchedWordBank.map((item) => item.word).filter((w) => w.trim().length > 0)
178
- : [], [isWithWordBank, watchedWordBank]);
179
- // ── Auto-sync arrays when blankCount changes ───────────────────────────────
180
- useEffect(() => {
181
- if (isInitializingRef.current || blankCount === 0)
182
- return;
183
- if (isWithOptions) {
184
- const currentWordBank = formWithOptions.getValues('wordBank');
185
- const currentAnswers = formWithOptions.getValues('answers');
186
- if (currentWordBank.length !== blankCount) {
187
- formWithOptions.setValue('wordBank', Array.from({ length: blankCount }, (_, i) => i < currentWordBank.length ? currentWordBank[i] : { options: ['', '', ''] }));
188
- }
189
- if (currentAnswers.length !== blankCount) {
190
- formWithOptions.setValue('answers', Array.from({ length: blankCount }, (_, i) => i < currentAnswers.length ? currentAnswers[i] : ''));
191
- }
192
- }
193
- else if (isDefault) {
194
- const currentAnswers = formDefault.getValues('answers');
195
- if (currentAnswers.length !== blankCount) {
196
- formDefault.setValue('answers', Array.from({ length: blankCount }, (_, i) => i < currentAnswers.length ? currentAnswers[i] : ''));
197
- }
198
- }
199
- else if (isWithWordBank) {
200
- const currentAnswers = formWithWordBank.getValues('answers');
201
- if (currentAnswers.length !== blankCount) {
202
- formWithWordBank.setValue('answers', Array.from({ length: blankCount }, (_, i) => i < currentAnswers.length ? currentAnswers[i] : ''));
203
- }
204
- }
205
- }, [blankCount, isWithOptions, isWithWordBank, isDefault, formWithOptions, formWithWordBank, formDefault]);
206
- // Secondary guard: keep WITH_OPTIONS wordBank length in sync
207
- useEffect(() => {
208
- if (!isWithOptions || isInitializingRef.current || blankCount === watchedWordBank.length)
209
- return;
210
- const newWordBank = [...watchedWordBank];
211
- let changed = false;
212
- while (newWordBank.length < blankCount) {
213
- newWordBank.push({ options: ['', '', ''] });
214
- changed = true;
215
- }
216
- if (newWordBank.length > blankCount) {
217
- newWordBank.length = blankCount;
218
- changed = true;
219
- }
220
- if (changed) {
221
- formWithOptions.setValue('wordBank', newWordBank, { shouldValidate: true });
222
- if (watchedAnswers.length > blankCount) {
223
- const a = [...watchedAnswers];
224
- a.length = blankCount;
225
- formWithOptions.setValue('answers', a, { shouldValidate: true });
226
- }
227
- }
228
- }, [blankCount, watchedWordBank.length, isWithOptions, formWithOptions, watchedAnswers]);
229
- // ── Expose validation ref ─────────────────────────────────────────────────
230
- useEffect(() => {
231
- if (!validationRef)
232
- return;
233
- validationRef.current = {
234
- triggerValidation: async () => {
235
- const isValid = await form.trigger();
236
- console.log('[WordFillParagraphCreator] triggerValidation:', isValid, form.formState.errors);
237
- return isValid;
238
- },
239
- getFormData: () => {
240
- const base = {
241
- title: watchedTitle,
242
- imageUrl: watchedImageUrl,
243
- paragraph: watchedParagraph,
244
- wordBank: isDefault ? [] : watchedWordBank,
245
- answers: watchedAnswers,
246
- caseSensitive: watchedCaseSensitive,
247
- explanation: watchedExplanation,
248
- points: watchedPoints,
249
- viewMode,
250
- groupTitle,
251
- groupSubtitle,
252
- groupContent,
253
- groupImageUrl,
254
- groupNumber: currentGroupNumber,
255
- };
256
- if (isEmail) {
257
- base.emailFrom = emailFrom;
258
- base.emailTo = emailTo;
259
- }
260
- console.log('[WordFillParagraphCreator] getFormData returning:', base);
261
- if (isWithWordBank) {
262
- return { ...base, exampleAnswer: watchedExampleAnswer, isExample: watchedIsExample };
263
- }
264
- if (isDefault) {
265
- return { ...base, exampleAnswer: watchedExampleAnswer };
266
- }
267
- return base;
268
- },
269
- };
270
- }, [
271
- validationRef, form, viewMode,
272
- watchedTitle, watchedImageUrl, watchedParagraph, watchedWordBank, watchedAnswers,
273
- watchedCaseSensitive, watchedExplanation, watchedPoints,
274
- watchedExampleAnswer, watchedIsExample, isWithWordBank, isDefault,
275
- isEmail, emailFrom, emailTo,
276
- groupTitle, groupSubtitle, groupContent, groupImageUrl, currentGroupNumber,
277
- ]);
278
- // ── Sync from initialData changes ─────────────────────────────────────────
279
- useEffect(() => {
280
- const d = initialData;
281
- const normalised = {
282
- title: d?.title || '',
283
- imageUrl: d?.imageUrl || '',
284
- paragraph: d?.paragraph || '',
285
- wordBank: d?.wordBank?.length > 0 ? d.wordBank : [],
286
- answers: d?.answers?.length > 0 ? d.answers : [],
287
- caseSensitive: d?.caseSensitive || false,
288
- explanation: d?.explanation || '',
289
- points: d?.points ?? 1,
290
- ...(isWithWordBank && {
291
- exampleAnswer: d?.exampleAnswer?.length > 0 ? d.exampleAnswer : [],
292
- isExample: d?.isExample || false,
293
- }),
294
- ...(isDefault && {
295
- exampleAnswer: d?.exampleAnswer?.length > 0 ? d.exampleAnswer
296
- : d?.content?.exampleAnswer?.length > 0 ? d.content.exampleAnswer
297
- : d?.answer?.example?.length > 0 ? d.answer.example
298
- : d?.correctAnswer?.example?.length > 0 ? d.correctAnswer.example
299
- : [],
300
- }),
301
- };
302
- const changed = JSON.stringify(previousInitialDataRef.current) !== JSON.stringify(initialData);
303
- if (!changed)
304
- return;
305
- previousInitialDataRef.current = initialData;
306
- // FIX: When sentOnChangeRef is true, it means our own onChange fired.
307
- // However, if the incoming data has meaningfully different content (e.g., from
308
- // question bank selection), we must NOT skip the reset. Only skip if the incoming
309
- // data matches what the form already has (a true echo).
310
- if (sentOnChangeRef.current) {
311
- sentOnChangeRef.current = false;
312
- const hasNewContent = normalised.paragraph && normalised.paragraph !== watchedParagraph;
313
- const hasNewTitle = normalised.title && normalised.title !== watchedTitle;
314
- if (!hasNewContent && !hasNewTitle)
315
- return;
316
- }
317
- const isEcho = watchedTitle === normalised.title &&
318
- watchedParagraph === normalised.paragraph &&
319
- watchedImageUrl === normalised.imageUrl &&
320
- watchedPoints === normalised.points;
321
- if (isEcho && initialData?.isCompleted === true)
322
- return;
323
- const shouldReset = !isEcho &&
324
- (normalised.title !== watchedTitle ||
325
- normalised.paragraph !== watchedParagraph ||
326
- normalised.imageUrl !== watchedImageUrl);
327
- if (shouldReset && normalised.paragraph) {
328
- isInitializingRef.current = true;
329
- form.reset(normalised);
330
- if (isEmail) {
331
- setEmailFrom(d?.emailFrom || '');
332
- setEmailTo(d?.emailTo || '');
333
- }
334
- setTimeout(() => { isInitializingRef.current = false; }, 150);
335
- }
336
- }, [
337
- initialData, form, isWithWordBank,
338
- watchedTitle, watchedImageUrl, watchedParagraph, watchedPoints,
339
- ]);
340
- // ── onChange effect ────────────────────────────────────────────────────────
341
- const watchedWordBankStr = JSON.stringify(watchedWordBank);
342
- const watchedAnswersStr = JSON.stringify(watchedAnswers);
343
- useEffect(() => {
344
- const payload = {
345
- title: watchedTitle, imageUrl: watchedImageUrl, paragraph: watchedParagraph,
346
- wordBank: isDefault ? [] : watchedWordBank, answers: watchedAnswers,
347
- caseSensitive: watchedCaseSensitive, explanation: watchedExplanation,
348
- points: watchedPoints, viewMode,
349
- groupTitle, groupSubtitle, groupContent, groupImageUrl,
350
- groupNumber: currentGroupNumber,
351
- };
352
- if (isWithWordBank) {
353
- payload.exampleAnswer = watchedExampleAnswer;
354
- payload.isExample = watchedIsExample;
355
- }
356
- if (isDefault) {
357
- payload.exampleAnswer = watchedExampleAnswer;
358
- }
359
- if (isEmail) {
360
- payload.emailFrom = emailFrom;
361
- payload.emailTo = emailTo;
362
- }
363
- if (isInitializingRef.current) {
364
- previousPayloadRef.current = JSON.stringify(payload);
365
- return;
366
- }
367
- const str = JSON.stringify(payload);
368
- if (previousPayloadRef.current !== str) {
369
- previousPayloadRef.current = str;
370
- debouncedOnChange(payload);
371
- }
372
- }, [
373
- watchedTitle, watchedImageUrl, watchedParagraph,
374
- watchedWordBankStr, watchedAnswersStr,
375
- watchedCaseSensitive, watchedExplanation, watchedPoints,
376
- watchedExampleAnswer, watchedIsExample,
377
- emailFrom, emailTo,
378
- groupTitle, groupSubtitle, groupContent, groupImageUrl, currentGroupNumber,
379
- ]);
380
- // ── WITH_OPTIONS handlers ──────────────────────────────────────────────────
381
- const handleOptionChange = (blankIndex, optionIndex, value) => {
382
- const newWordBank = [...watchedWordBank];
383
- const newOptions = [...(newWordBank[blankIndex]?.options || [])];
384
- const old = newOptions[optionIndex];
385
- newOptions[optionIndex] = value;
386
- newWordBank[blankIndex] = { ...newWordBank[blankIndex], options: newOptions };
387
- formWithOptions.setValue('wordBank', newWordBank, { shouldValidate: true });
388
- if (old) {
389
- const ans = [...watchedAnswers];
390
- if (ans[blankIndex] === old) {
391
- ans[blankIndex] = value;
392
- formWithOptions.setValue('answers', ans);
393
- }
394
- }
395
- };
396
- const handleAddOption = (blankIndex) => {
397
- const newWordBank = [...watchedWordBank];
398
- newWordBank[blankIndex] = { ...newWordBank[blankIndex], options: [...(newWordBank[blankIndex]?.options || []), ''] };
399
- formWithOptions.setValue('wordBank', newWordBank);
400
- };
401
- const handleRemoveOption = (blankIndex, optionIndex) => {
402
- const newWordBank = [...watchedWordBank];
403
- const removed = newWordBank[blankIndex]?.options?.[optionIndex];
404
- newWordBank[blankIndex] = {
405
- ...newWordBank[blankIndex],
406
- options: (newWordBank[blankIndex]?.options || []).filter((_, i) => i !== optionIndex),
407
- };
408
- formWithOptions.setValue('wordBank', newWordBank, { shouldValidate: true });
409
- if (removed) {
410
- const ans = [...watchedAnswers];
411
- if (ans[blankIndex] === removed) {
412
- ans[blankIndex] = '';
413
- formWithOptions.setValue('answers', ans);
414
- }
415
- }
416
- };
417
- const handleSelectAnswerOptions = (blankIndex, option) => {
418
- const ans = [...watchedAnswers];
419
- while (ans.length <= blankIndex)
420
- ans.push('');
421
- ans[blankIndex] = option;
422
- formWithOptions.setValue('answers', ans);
423
- };
424
- const handleToggleExampleOptions = (blankIndex, checked) => {
425
- const newWordBank = [...watchedWordBank];
426
- newWordBank[blankIndex] = { ...newWordBank[blankIndex], isExample: checked };
427
- formWithOptions.setValue('wordBank', newWordBank);
428
- };
429
- // ── WITH_WORD_BANK handlers ────────────────────────────────────────────────
430
- const handleAddWord = () => {
431
- formWithWordBank.setValue('wordBank', [...watchedWordBank, { word: '', imageUrl: '' }]);
432
- };
433
- const handleRemoveWord = (index) => {
434
- const removedWord = watchedWordBank[index]?.word;
435
- const newWordBank = watchedWordBank.filter((_, i) => i !== index);
436
- formWithWordBank.setValue('wordBank', newWordBank);
437
- const newPreviews = { ...wordBankImagePreviews };
438
- delete newPreviews[index];
439
- const reindexed = {};
440
- Object.entries(newPreviews).forEach(([key, val]) => {
441
- const k = parseInt(key);
442
- reindexed[k > index ? k - 1 : k] = val;
443
- });
444
- setWordBankImagePreviews(reindexed);
445
- if (removedWord) {
446
- formWithWordBank.setValue('answers', watchedAnswers.map((a) => (a === removedWord ? '' : a)));
447
- formWithWordBank.setValue('exampleAnswer', (watchedExampleAnswer || []).filter((e) => e !== removedWord));
448
- }
449
- };
450
- const handleWordChange = (index, newWord) => {
451
- const oldWord = watchedWordBank[index]?.word;
452
- const newWordBank = [...watchedWordBank];
453
- newWordBank[index] = { ...newWordBank[index], word: newWord };
454
- formWithWordBank.setValue('wordBank', newWordBank, { shouldValidate: true });
455
- if (oldWord && oldWord !== newWord) {
456
- formWithWordBank.setValue('answers', watchedAnswers.map((a) => (a === oldWord ? newWord : a)));
457
- formWithWordBank.setValue('exampleAnswer', (watchedExampleAnswer || []).map((e) => (e === oldWord ? newWord : e)));
458
- }
459
- };
460
- const handleExampleToggle = (index, checked) => {
461
- const newWordBank = [...watchedWordBank];
462
- newWordBank[index] = { ...newWordBank[index], isExample: checked };
463
- formWithWordBank.setValue('wordBank', newWordBank);
464
- formWithWordBank.setValue('exampleAnswer', newWordBank.filter((w) => w.isExample).map((w) => w.word));
465
- };
466
- const handleAnswerSelect = (blankIndex, word) => {
467
- const ans = [...watchedAnswers];
468
- ans[blankIndex] = word;
469
- formWithWordBank.setValue('answers', ans);
470
- };
471
- // ── DEFAULT mode handlers ────────────────────────────────────────────────
472
- const handleDefaultAnswerChange = (index, value) => {
473
- const ans = [...watchedAnswers];
474
- const oldValue = ans[index];
475
- ans[index] = value;
476
- formDefault.setValue('answers', ans, { shouldValidate: true });
477
- // Keep exampleAnswer in sync when the answer text of an example blank changes
478
- const currentExamples = formDefault.getValues('exampleAnswer') || [];
479
- if (oldValue && currentExamples.includes(oldValue)) {
480
- const updated = currentExamples.map((e) => (e === oldValue ? value : e)).filter((e) => e);
481
- formDefault.setValue('exampleAnswer', updated);
482
- }
483
- };
484
- const handleDefaultToggleExample = (index) => {
485
- const answerText = (watchedAnswers[index] || '').trim();
486
- if (!answerText)
487
- return;
488
- const current = (formDefault.getValues('exampleAnswer') || []).filter((e) => e);
489
- const next = current.includes(answerText)
490
- ? current.filter((e) => e !== answerText)
491
- : [...current, answerText];
492
- formDefault.setValue('exampleAnswer', next, { shouldValidate: true });
493
- };
494
- // ── Preview mode ───────────────────────────────────────────────────────────
495
- if (isClientMode) {
496
- const baseQuestionData = {
497
- title: watchedTitle,
498
- imageUrl: displayPreviewUrl || watchedImageUrl,
499
- paragraph: watchedParagraph,
500
- wordBank: watchedWordBank,
501
- answers: watchedAnswers,
502
- caseSensitive: watchedCaseSensitive,
503
- explanation: watchedExplanation,
504
- points: watchedPoints,
505
- viewMode,
506
- emailFrom: isEmail ? emailFrom : undefined,
507
- emailTo: isEmail ? emailTo : undefined,
508
- };
509
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: isWithOptions ? 'Điền từ vào đoạn văn - Có lựa chọn (Preview)' : isDefault ? 'Điền từ vào đoạn văn - Mặc định (Xem trước)' : 'Điền từ vào đoạn văn (Xem trước)' }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Ch\u1EBF \u0111\u1ED9 xem tr\u01B0\u1EDBc \u2014 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 ch\u1EC9nh s\u1EEDa"] })] }) }) }), _jsx(WordFillParagraphClient, { questionData: isWithOptions
510
- ? baseQuestionData
511
- : isDefault
512
- ? { ...baseQuestionData, exampleAnswer: watchedExampleAnswer }
513
- : { ...baseQuestionData, exampleAnswer: watchedExampleAnswer, isExample: watchedIsExample }, isReviewMode: true, autoFillCorrectAnswers: isWithOptions ? true : undefined, userAnswer: isWithOptions ? undefined : watchedAnswers })] }));
514
- }
515
- // ── Header bar label / icon / instructions based on viewMode ──────────────
516
- const headerTitle = isWithOptions
517
- ? 'Điền từ vào đoạn văn - Có lựa chọn'
518
- : isDefault
519
- ? 'Điền từ vào đoạn văn - Mặc định'
520
- : isWithWordBank
521
- ? 'Điền từ vào đoạn văn'
522
- : 'Điền từ vào đoạn văn';
523
- const headerSubtitle = isWithOptions
524
- ? 'Đọc đoạn văn, chọn từ đúng từ các lựa chọn và viết vào chỗ trống'
525
- : isDefault
526
- ? 'Đọc đoạn văn và nhập đáp án trực tiếp cho từng chỗ trống'
527
- : isWithWordBank
528
- ? 'Đọc đoạn văn và chọn từ đúng từ word bank'
529
- : 'Đọc đoạn văn và điền từ vào chỗ trống';
530
- const instructionItems = isWithOptions
531
- ? [
532
- _jsxs(_Fragment, { children: ["Nh\u1EADp ", _jsx("strong", { children: "\u0111o\u1EA1n v\u0103n" }), " v\u1EDBi ch\u1ED7 tr\u1ED1ng d\u1EA1ng ", '{0}', ", ", '{1}', ", ", '{2}', "..."] }),
533
- _jsxs(_Fragment, { children: ["H\u1EC7 th\u1ED1ng t\u1EF1 t\u1EA1o ", _jsx("strong", { children: "l\u1EF1a ch\u1ECDn" }), " cho t\u1EEBng ch\u1ED7 tr\u1ED1ng (th\u01B0\u1EDDng 3 l\u1EF1a ch\u1ECDn m\u1ED7i ch\u1ED7)"] }),
534
- _jsxs(_Fragment, { children: ["Nh\u1EA5n ", _jsx("strong", { children: "\u00F4 ch\u1EEF c\u00E1i" }), " (A, B, C) b\u00EAn tr\u00E1i \u0111\u1EC3 ch\u1ECDn ", _jsx("strong", { children: "\u0111\u00E1p \u00E1n \u0111\u00FAng" }), " cho t\u1EEBng ch\u1ED7 tr\u1ED1ng"] }),
535
- _jsxs(_Fragment, { children: ["\u0110\u00E1nh d\u1EA5u ", _jsx("strong", { children: "Example" }), " cho ch\u1ED7 tr\u1ED1ng m\u1EABu (s\u1EBD \u0111\u01B0\u1EE3c \u0111i\u1EC1n s\u1EB5n cho h\u1ECDc sinh)"] }),
536
- ]
537
- : isDefault
538
- ? [
539
- _jsxs(_Fragment, { children: ["Nh\u1EADp ", _jsx("strong", { children: "\u0111o\u1EA1n v\u0103n" }), " v\u1EDBi ch\u1ED7 tr\u1ED1ng d\u1EA1ng ", '{0}', ", ", '{1}', ", ..."] }),
540
- _jsxs(_Fragment, { children: ["Nh\u1EADp ", _jsx("strong", { children: "\u0111\u00E1p \u00E1n \u0111\u00FAng" }), " tr\u1EF1c ti\u1EBFp cho t\u1EEBng ch\u1ED7 tr\u1ED1ng b\u00EAn d\u01B0\u1EDBi"] }),
541
- ]
542
- : isWithWordBank
543
- ? [
544
- _jsxs(_Fragment, { children: ["Nh\u1EADp ", _jsx("strong", { children: "\u0111o\u1EA1n v\u0103n" }), " v\u1EDBi ch\u1ED7 tr\u1ED1ng d\u1EA1ng ", '{0}', ", ", '{1}', ", ..."] }),
545
- _jsxs(_Fragment, { children: ["Th\u00EAm c\u00E1c t\u1EEB v\u00E0o ", _jsx("strong", { children: "Word Bank" }), " (bao g\u1ED3m c\u1EA3 \u0111\u00E1p \u00E1n \u0111\u00FAng v\u00E0 t\u1EEB nhi\u1EC5u)"] }),
546
- _jsxs(_Fragment, { children: ["Ch\u1ECDn ", _jsx("strong", { children: "\u0111\u00E1p \u00E1n \u0111\u00FAng" }), " cho t\u1EEBng ch\u1ED7 tr\u1ED1ng t\u1EEB danh s\u00E1ch Word Bank"] }),
547
- _jsxs(_Fragment, { children: ["\u0110\u00E1nh d\u1EA5u ", _jsx("strong", { children: "Example" }), " cho t\u1EEB m\u1EABu (s\u1EBD \u0111\u01B0\u1EE3c \u0111i\u1EC1n s\u1EB5n cho h\u1ECDc sinh)"] }),
548
- ]
549
- : [
550
- _jsxs(_Fragment, { children: ["Nh\u1EADp ", _jsx("strong", { children: "\u0111o\u1EA1n v\u0103n" }), " v\u1EDBi ch\u1ED7 tr\u1ED1ng d\u1EA1ng ", '{0}', ", ", '{1}', ", ..."] }),
551
- _jsxs(_Fragment, { children: ["Nh\u1EADp ", _jsx("strong", { children: "\u0111\u00E1p \u00E1n \u0111\u00FAng" }), " cho t\u1EEBng ch\u1ED7 tr\u1ED1ng"] }),
552
- ];
553
- const canPreview = isWithOptions
554
- ? !!watchedParagraph && watchedWordBank.length > 0
555
- : !!watchedParagraph && blankCount > 0;
556
- const gradientBar = isWithOptions
557
- ? 'bg-gradient-to-r from-emerald-500 via-teal-500 to-cyan-500'
558
- : isDefault
559
- ? 'bg-gradient-to-r from-indigo-500 via-blue-500 to-cyan-500'
560
- : 'bg-gradient-to-r from-teal-500 via-cyan-500 to-blue-500';
561
- const iconBg = isWithOptions
562
- ? 'bg-gradient-to-br from-emerald-500 to-teal-600'
563
- : isDefault
564
- ? 'bg-gradient-to-br from-indigo-500 to-blue-600'
565
- : 'bg-gradient-to-br from-teal-500 to-cyan-600';
566
- const IconComponent = isWithOptions ? BookOpen : isDefault ? FileText : FileText;
567
- // ── Creator UI ─────────────────────────────────────────────────────────────
568
- return (_jsxs("div", { className: "space-y-6", children: [isGrouped && (_jsx(BasicQuestionGroupCard, { groupNumber: currentGroupNumber, title: groupTitle, subtitle: groupSubtitle, content: groupContent, imageUrl: groupImageUrl, onTitleChange: setGroupTitle, onSubtitleChange: setGroupSubtitle, onContentChange: setGroupContent, onImageChange: setGroupImageUrl, groupConfig: groupConfig, uploadIdPrefix: `word-fill-paragraph-${partId ?? 'group'}` })), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50", children: [_jsx("div", { className: `h-1.5 ${gradientBar}` }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: `flex h-10 w-10 items-center justify-center rounded-xl ${iconBg} shadow-md`, children: _jsx(IconComponent, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold text-gray-900", children: headerTitle }), _jsx("p", { className: "text-sm text-gray-500", children: headerSubtitle })] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: !canPreview, 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-6", children: [_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" }), _jsx("ul", { className: "mt-2 space-y-1.5 text-blue-700", children: instructionItems.map((item, i) => (_jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-blue-400" }), item] }, i))) })] })] }) }), _jsx(CommonFields, { form: form, displayPreviewUrl: displayPreviewUrl, setImagePreviewUrl: setImagePreviewUrl, watchedImageUrl: watchedImageUrl, watchedParagraph: watchedParagraph, blankCount: blankCount, externalErrors: externalErrors, showCaseSensitive: isWithWordBank, watchedCaseSensitive: watchedCaseSensitive, afterPoints: isEmail ? (_jsxs("div", { className: "space-y-4 rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-sky-50 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(Mail, { className: "h-4 w-4 text-blue-500" }), "Email"] }), _jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "emailFrom", className: "text-sm font-medium text-gray-700", children: "From" }), _jsx(Input, { id: "emailFrom", value: emailFrom, onChange: (e) => setEmailFrom(e.target.value), placeholder: "V\u00ED d\u1EE5: Brenda", className: "border-gray-200 bg-white transition-all focus:border-blue-300 focus:ring-blue-200" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "emailTo", className: "text-sm font-medium text-gray-700", children: "To" }), _jsx(Input, { id: "emailTo", value: emailTo, onChange: (e) => setEmailTo(e.target.value), placeholder: "V\u00ED d\u1EE5: Ewan", className: "border-gray-200 bg-white transition-all focus:border-blue-300 focus:ring-blue-200" })] })] })] })) : undefined })] })] }), isWithOptions && (_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-teal-400 to-cyan-400" }), _jsx(CardHeader, { className: "pb-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-teal-100", children: _jsx(ListChecks, { className: "h-3.5 w-3.5 text-teal-600" }) }), _jsxs(CardTitle, { className: "text-lg font-semibold text-gray-800", children: ["L\u1EF1a ch\u1ECDn cho t\u1EEBng ch\u1ED7 tr\u1ED1ng (", watchedWordBank.length, ")"] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [watchedWordBank.length === 0 && (_jsx("div", { className: "rounded-lg border-2 border-dashed border-gray-200 p-8 text-center", children: _jsxs("p", { className: "text-sm text-gray-500", children: ["Ch\u01B0a c\u00F3 ch\u1ED7 tr\u1ED1ng n\u00E0o. Nh\u1EADp \u0111o\u1EA1n v\u0103n v\u1EDBi ", '{0}', ", ", '{1}', "... \u0111\u1EC3 t\u1EA1o ch\u1ED7 tr\u1ED1ng."] }) })), watchedWordBank.map((blank, blankIndex) => {
569
- const selectedAnswer = watchedAnswers[blankIndex] || '';
570
- const isExampleItem = blank.isExample || false;
571
- return (_jsxs("div", { className: `rounded-xl border-2 p-4 transition-all ${isExampleItem
572
- ? 'border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50'
573
- : 'border-gray-200 bg-white hover:border-teal-200'}`, children: [_jsxs("div", { className: "mb-3 flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: `flex h-8 w-8 items-center justify-center rounded-lg text-sm font-bold text-white ${isExampleItem
574
- ? 'bg-gradient-to-br from-amber-400 to-orange-500'
575
- : 'bg-gradient-to-br from-teal-500 to-cyan-600'}`, children: blankIndex }), _jsx("span", { className: "text-sm font-semibold text-gray-700", children: isExampleItem ? `Chỗ trống ${blankIndex} (Example)` : `Chỗ trống ${blankIndex}` }), selectedAnswer && (_jsxs("span", { className: "flex items-center gap-1 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-700", children: [_jsx(CheckCircle2, { className: "h-3 w-3" }), selectedAnswer] }))] }), _jsxs("label", { className: "flex items-center gap-2 text-xs text-gray-600", children: [_jsx(Checkbox, { checked: isExampleItem, onCheckedChange: (checked) => handleToggleExampleOptions(blankIndex, checked === true) }), "Example"] })] }), _jsxs("div", { className: "space-y-2", children: [(blank.options || []).map((option, optionIndex) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("button", { type: "button", onClick: () => option.trim() && handleSelectAnswerOptions(blankIndex, option), className: `flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg text-xs font-bold transition-all ${selectedAnswer === option && option.trim()
576
- ? 'bg-green-500 text-white shadow-sm'
577
- : 'bg-gray-100 text-gray-500 hover:bg-green-100 hover:text-green-600'}`, title: "Ch\u1ECDn l\u00E0m \u0111\u00E1p \u00E1n \u0111\u00FAng", children: String.fromCharCode(65 + optionIndex) }), _jsx(Input, { value: option, onChange: (e) => handleOptionChange(blankIndex, optionIndex, e.target.value), placeholder: `Lựa chọn ${String.fromCharCode(65 + optionIndex)}`, className: "flex-1 border-gray-200 text-sm" }), (blank.options || []).length > 2 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => handleRemoveOption(blankIndex, optionIndex), className: "h-8 w-8 p-0 text-red-400 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }, optionIndex))), _jsxs(Button, { variant: "ghost", size: "sm", onClick: () => handleAddOption(blankIndex), className: "mt-1 gap-1 text-xs text-teal-600 hover:bg-teal-50 hover:text-teal-700", children: [_jsx(Plus, { className: "h-3 w-3" }), "Th\u00EAm l\u1EF1a ch\u1ECDn"] })] }), form.formState.errors.answers?.[blankIndex] && (_jsx("p", { className: "mt-2 text-xs font-medium text-red-500", children: form.formState.errors.answers[blankIndex]?.message }))] }, blankIndex));
578
- })] })] })), isWithWordBank && (_jsxs(_Fragment, { children: [_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-purple-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-purple-400 to-pink-400" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-purple-100", children: _jsx(Library, { className: "h-3.5 w-3.5 text-purple-600" }) }), _jsxs(CardTitle, { className: "text-lg font-semibold text-gray-800", children: ["Word Bank (", watchedWordBank.length, " t\u1EEB)"] })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: handleAddWord, className: "gap-1.5 border-purple-200 text-purple-600 hover:bg-purple-50 hover:text-purple-700", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm t\u1EEB"] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("p", { className: "text-xs text-gray-500", children: ["Th\u00EAm t\u1EA5t c\u1EA3 c\u00E1c t\u1EEB g\u1EE3i \u00FD cho h\u1ECDc sinh (bao g\u1ED3m ", _jsx("strong", { children: "\u0111\u00E1p \u00E1n \u0111\u00FAng" }), " v\u00E0", ' ', _jsx("strong", { children: "t\u1EEB nhi\u1EC5u" }), "). \u0110\u00E1nh d\u1EA5u ", _jsx("strong", { children: "Example" }), " cho t\u1EEB m\u1EABu \u0111\u00E3 \u0111\u01B0\u1EE3c \u0111i\u1EC1n s\u1EB5n."] }), watchedWordBank.length > 0 ? (_jsx("div", { className: "grid grid-cols-1 gap-3 md:grid-cols-2", children: watchedWordBank.map((item, index) => (_jsxs("div", { className: `space-y-3 rounded-lg border p-4 transition-all ${item.isExample
579
- ? 'border-amber-300 bg-amber-50/50'
580
- : 'border-gray-200 bg-gray-50/50 hover:border-gray-300'}`, children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: `flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-lg text-xs font-bold text-white shadow-sm ${item.isExample
581
- ? 'bg-gradient-to-br from-amber-400 to-orange-500'
582
- : 'bg-gradient-to-br from-purple-500 to-pink-600'}`, children: index + 1 }), item.isExample && (_jsx("span", { className: "rounded-full border border-amber-200 bg-amber-100 px-1.5 py-0.5 text-[10px] font-semibold text-amber-700", children: "EXAMPLE" }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Checkbox, { id: `example-${index}`, checked: item.isExample || false, onCheckedChange: (checked) => handleExampleToggle(index, !!checked) }), _jsx(Label, { htmlFor: `example-${index}`, className: "cursor-pointer text-xs font-medium text-gray-600", children: "Example" })] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => handleRemoveWord(index), className: "h-7 w-7 p-0 text-gray-400 hover:bg-red-50 hover:text-red-500", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] })] }), _jsxs("div", { children: [_jsxs(Label, { className: "text-xs text-gray-500", children: ["T\u1EEB ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { value: item.word, onChange: (e) => handleWordChange(index, e.target.value), placeholder: "Nh\u1EADp t\u1EEB...", className: `${item.isExample ? 'bg-amber-50' : ''} ${formWithWordBank.formState.errors.wordBank?.[index]?.word
583
- ? 'border-red-300 focus:border-red-400 focus:ring-red-200'
584
- : 'border-gray-200'}` }), formWithWordBank.formState.errors.wordBank?.[index]?.word?.message && (_jsx("p", { className: "mt-1 text-xs text-red-500", children: formWithWordBank.formState.errors.wordBank[index].word.message }))] }), _jsxs("div", { children: [_jsx(Label, { className: "text-xs text-gray-500", children: "N\u1ED9i dung (tu\u1EF3 ch\u1ECDn)" }), _jsx(Textarea, { value: item.content || '', onChange: (e) => {
585
- const nb = [...watchedWordBank];
586
- nb[index] = { ...nb[index], content: e.target.value };
587
- formWithWordBank.setValue('wordBank', nb);
588
- }, placeholder: "Nh\u1EADp n\u1ED9i dung...", className: `${item.isExample ? 'bg-amber-50' : ''} min-h-[88px] border-gray-200` })] }), _jsxs("div", { children: [_jsx(Label, { className: "text-xs text-gray-500", children: "H\u00ECnh \u1EA3nh (tu\u1EF3 ch\u1ECDn)" }), _jsx(FileUpload, { id: `word-bank-image-${index}`, label: "", accept: "image/*", value: item.imageUrl || '', onChange: (url) => {
589
- const nb = [...watchedWordBank];
590
- nb[index] = { ...nb[index], imageUrl: url };
591
- formWithWordBank.setValue('wordBank', nb);
592
- }, onPresignedUrlChange: (url) => setWordBankImagePreviews((prev) => ({ ...prev, [index]: url })), maxSize: 5, placeholder: "Upload h\u00ECnh", autoUpload: true, prefix: "questions" }), _jsx(WordBankImagePreview, { imageUrl: item.imageUrl || '', localPreviewUrl: wordBankImagePreviews[index], index: index })] })] }, index))) })) : (_jsxs("div", { className: "flex h-28 flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed border-gray-200 bg-gray-50", children: [_jsx("p", { className: "text-sm text-gray-400", children: "Ch\u01B0a c\u00F3 t\u1EEB n\u00E0o trong Word Bank" }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: handleAddWord, className: "gap-1.5 border-purple-200 text-purple-600", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm t\u1EEB \u0111\u1EA7u ti\u00EAn"] })] })), formWithWordBank.formState.errors.wordBank?.message && (_jsx("p", { className: "text-sm text-red-600", children: formWithWordBank.formState.errors.wordBank.message }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-green-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-green-400 to-emerald-400" }), _jsx(CardHeader, { className: "pb-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-green-100", children: _jsx(ListChecks, { className: "h-3.5 w-3.5 text-green-600" }) }), _jsxs(CardTitle, { className: "text-lg font-semibold text-gray-800", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng (", blankCount, " ch\u1ED7 tr\u1ED1ng)"] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [blankCount > 0 ? (_jsxs(_Fragment, { children: [_jsxs("p", { className: "text-xs text-gray-500", children: ["Ch\u1ECDn t\u1EEB \u0111\u00FAng t\u1EEB ", _jsx("strong", { children: "Word Bank" }), " cho m\u1ED7i ch\u1ED7 tr\u1ED1ng trong \u0111o\u1EA1n v\u0103n."] }), _jsx("div", { className: "space-y-3", children: placeholderIndices.map((placeholderIdx) => {
593
- const currentAnswer = watchedAnswers[placeholderIdx] || '';
594
- const isExampleBlank = watchedWordBank.some((w) => w.isExample && w.word === currentAnswer);
595
- return (_jsxs("div", { className: `flex items-center gap-4 rounded-lg border p-4 transition-all ${currentAnswer
596
- ? isExampleBlank
597
- ? 'border-amber-300 bg-amber-50/50'
598
- : 'border-green-300 bg-green-50/50'
599
- : 'border-gray-200 bg-gray-50/50'}`, children: [_jsxs("div", { className: "flex min-w-[140px] items-center gap-3", children: [_jsx("div", { className: `flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg text-sm font-bold text-white shadow-sm ${currentAnswer
600
- ? isExampleBlank
601
- ? 'bg-gradient-to-br from-amber-400 to-orange-500'
602
- : 'bg-gradient-to-br from-green-500 to-emerald-600'
603
- : 'bg-gradient-to-br from-gray-400 to-gray-500'}`, children: '{' + placeholderIdx + '}' }), _jsxs("div", { children: [_jsxs("span", { className: "text-sm font-semibold text-gray-700", children: ["Ch\u1ED7 tr\u1ED1ng (", placeholderIdx + 1, ")"] }), isExampleBlank && (_jsx("span", { className: "block text-[10px] font-medium text-amber-600", children: "Example - \u0110i\u1EC1n s\u1EB5n" }))] })] }), _jsx("div", { className: "flex-1", children: _jsxs(Select, { value: currentAnswer || undefined, onValueChange: (value) => handleAnswerSelect(placeholderIdx, value), children: [_jsx(SelectTrigger, { className: `w-full ${formWithWordBank.formState.errors.answers?.[placeholderIdx]
604
- ? 'border-red-300 ring-1 ring-red-200'
605
- : currentAnswer
606
- ? 'border-green-300 bg-white'
607
- : 'border-gray-200'}`, children: _jsx(SelectValue, { placeholder: "Ch\u1ECDn t\u1EEB \u0111\u00FAng t\u1EEB Word Bank..." }) }), _jsx(SelectContent, { children: availableWords.length > 0 ? (availableWords.map((word, wordIdx) => {
608
- const isUsedElsewhere = watchedAnswers.some((ans, ansIdx) => ans === word && ansIdx !== placeholderIdx);
609
- const isExampleWord = watchedWordBank.find((w) => w.word === word)?.isExample;
610
- return (_jsx(SelectItem, { value: word, disabled: isUsedElsewhere, children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: isUsedElsewhere ? 'text-gray-400' : '', children: word }), isExampleWord && (_jsx("span", { className: "rounded bg-amber-100 px-1 py-0.5 text-[10px] font-medium text-amber-600", children: "example" })), isUsedElsewhere && (_jsx("span", { className: "text-[10px] text-gray-400", children: "(\u0111\u00E3 d\u00F9ng)" }))] }) }, `${word}-${wordIdx}`));
611
- })) : (_jsx("div", { className: "py-4 text-center text-sm text-gray-400", children: "Ch\u01B0a c\u00F3 t\u1EEB n\u00E0o trong Word Bank" })) })] }) }), _jsx("div", { className: "flex-shrink-0", children: currentAnswer ? (_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-green-100", children: _jsx(ListChecks, { className: "h-4 w-4 text-green-600" }) })) : formWithWordBank.formState.errors.answers?.[placeholderIdx] ? (_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-red-100", children: _jsx("span", { className: "text-xs font-bold text-red-500", children: "!" }) })) : (_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-gray-100", children: _jsx("span", { className: "text-xs text-gray-400", children: "?" }) })) })] }, placeholderIdx));
612
- }) }), _jsx("div", { className: "rounded-lg border border-gray-200 bg-gray-50 p-3", children: _jsxs("div", { className: "flex items-center justify-between text-sm", children: [_jsxs("span", { className: "text-gray-600", children: ["\u0110\u00E3 ch\u1ECDn \u0111\u00E1p \u00E1n: ", _jsx("strong", { children: watchedAnswers.filter((a) => a).length }), " / ", blankCount] }), _jsxs("span", { className: "text-gray-600", children: ["T\u1EEB nhi\u1EC5u:", ' ', _jsx("strong", { children: watchedWordBank.length - watchedAnswers.filter((a) => a).length }), " t\u1EEB"] })] }) })] })) : (_jsx("div", { className: "flex h-24 items-center justify-center rounded-lg border-2 border-dashed border-gray-200 bg-gray-50", children: _jsxs("p", { className: "text-sm text-gray-400", children: ["Nh\u1EADp \u0111o\u1EA1n v\u0103n c\u00F3 ", '{0}', ", ", '{1}', ", ... \u0111\u1EC3 t\u1EA1o ch\u1ED7 tr\u1ED1ng"] }) })), formWithWordBank.formState.errors.answers?.message && (_jsx("p", { className: "text-sm text-red-600", children: formWithWordBank.formState.errors.answers.message }))] })] })] })), isDefault && (_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-indigo-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-indigo-400 via-blue-500 to-cyan-500" }), _jsx(CardHeader, { className: "pb-4", 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-indigo-500 to-blue-600 shadow-md", children: _jsx(ListChecks, { className: "h-4 w-4 text-white" }) }), _jsx(CardTitle, { className: "text-lg font-semibold text-gray-800", children: "\u0110\u00E1p \u00E1n cho t\u1EEBng ch\u1ED7 tr\u1ED1ng" })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [blankCount > 0 ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "space-y-3", children: placeholderIndices.map((idx) => {
613
- const answerText = (watchedAnswers[idx] || '').trim();
614
- const isExampleAnswer = !!answerText && watchedExampleAnswer.includes(answerText);
615
- return (_jsxs("div", { className: `rounded-xl border-2 p-4 transition-all ${isExampleAnswer
616
- ? 'border-amber-300 bg-gradient-to-r from-amber-50 to-yellow-50'
617
- : watchedAnswers[idx]
618
- ? 'border-green-200 bg-gradient-to-r from-green-50 to-emerald-50'
619
- : '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-bold transition-all ${isExampleAnswer
620
- ? 'bg-gradient-to-br from-amber-500 to-orange-500 text-white shadow-md'
621
- : watchedAnswers[idx]
622
- ? 'bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md'
623
- : 'bg-indigo-100 text-indigo-700'}`, children: `{${idx}}` }), _jsx(Input, { value: watchedAnswers[idx] || '', onChange: (e) => handleDefaultAnswerChange(idx, e.target.value), placeholder: `Đáp án cho {${idx}}`, className: `transition-all ${isExampleAnswer
624
- ? 'border-amber-300 bg-white focus:border-amber-400 focus:ring-amber-200'
625
- : watchedAnswers[idx]
626
- ? 'border-green-300 bg-white focus:border-green-400 focus:ring-green-200'
627
- : 'border-gray-200 focus:border-indigo-400 focus:ring-indigo-200'}` }), _jsxs("button", { type: "button", disabled: !answerText, onClick: () => handleDefaultToggleExample(idx), title: isExampleAnswer ? 'Bỏ đánh dấu Example' : 'Đánh dấu làm Example', className: `inline-flex h-10 items-center gap-1.5 rounded-lg border px-3 text-sm font-medium transition-colors select-none ${isExampleAnswer
628
- ? 'border-amber-300 bg-amber-50 text-amber-700 hover:bg-amber-100'
629
- : 'border-gray-200 bg-white text-gray-400 hover:text-gray-600'} ${!answerText ? 'cursor-not-allowed opacity-50' : ''}`, children: [_jsx(Star, { className: `h-4 w-4 ${isExampleAnswer ? 'fill-amber-400 text-amber-400' : ''}` }), "Example"] })] }), formDefault.formState.errors.answers?.[idx] && (_jsx("p", { className: "mt-1.5 ml-13 text-xs text-red-500", children: formDefault.formState.errors.answers[idx]?.message }))] }, idx));
630
- }) }), _jsxs("div", { className: "flex items-center gap-2 rounded-lg bg-indigo-50/50 px-3 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-indigo-500" }), _jsxs("span", { className: "text-xs text-indigo-700", children: [watchedAnswers.filter((a) => a?.trim()).length, " / ", blankCount, " \u0111\u00E1p \u00E1n \u0111\u00E3 \u0111i\u1EC1n"] })] })] })) : (_jsx("div", { className: "flex h-24 items-center justify-center rounded-lg border-2 border-dashed border-gray-200 bg-gray-50", children: _jsxs("p", { className: "text-sm text-gray-400", children: ["Nh\u1EADp \u0111o\u1EA1n v\u0103n c\u00F3 ", '{0}', ", ", '{1}', ", ... \u0111\u1EC3 t\u1EA1o ch\u1ED7 tr\u1ED1ng"] }) })), formDefault.formState.errors.answers?.message && (_jsx("p", { className: "text-sm text-red-600", children: formDefault.formState.errors.answers.message }))] })] })), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-amber-400 to-orange-400" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm", children: _jsx(BookOpen, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-lg 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-sm 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, { children: _jsx(Textarea, { ...form.register('explanation'), placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0... v\u00EC...", rows: 3, className: "min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none" }) })] })] }));
631
- }
1
+ "use client";import{jsx as e,jsxs as r,Fragment as t}from"react/jsx-runtime";import{ImagePreview as a}from"../../../../components/ui/image-preview";import{useState as n,useRef as s,useEffect as l,useMemo as i}from"react";import{Button as o}from"../../../../components/ui/button";import{Label as d}from"../../../../components/ui/label";import{Input as c}from"../../../../components/ui/input";import{Textarea as m}from"../../../../components/ui/textarea";import{PointsInput as h}from"../../../../components/ui/points-input";import{Card as g,CardContent as p,CardHeader as u,CardTitle as x}from"../../../../components/ui/card";import{Checkbox as b}from"../../../../components/ui/checkbox";import{Switch as f}from"../../../../components/ui/switch";import{Select as w,SelectContent as v,SelectItem as N,SelectTrigger as y,SelectValue as k}from"../../../../components/ui/select";import{usePresignedFileUrl as C,useDebouncedCallback as S}from"../../../../shared/lib/hooks";import{useReactHookForm as V}from"../../../../shared/lib/hooks/useReactHookForm";import{Pencil as E,Eye as A,BookOpen as U,ImageIcon as B,Lightbulb as j,FileText as F,Library as T,ToggleLeft as P,Plus as I,Trash2 as $,ListChecks as W,CheckCircle2 as z,Mail as M,Star as O}from"lucide-react";import{FileUpload as D}from"../../../../components/ui/file-upload";import{WordFillParagraphClient as G}from"./WordFillParagraphClient";import{BasicQuestionGroupCard as H}from"../../groups/BasicQuestionGroupCard";import{useBasicQuestionGroup as L}from"../../_shared/hooks/useBasicQuestionGroup";import{wordFillParagraphFormSchema as q}from"../../../../shared/lib/validation/word-fill-paragraph.validation";import{wordFillParagraphWithOptionsFormSchema as J}from"../../../../shared/lib/validation/word-fill-paragraph-with-options.validation";import{wordFillParagraphDefaultFormSchema as R}from"../../../../shared/lib/validation/word-fill-paragraph-default.validation";function X({imageUrl:r,localPreviewUrl:t,index:n}){const{previewUrl:s}=C(r||""),l=t||s||"";return l?e(a,{src:l,alt:`Word bank ${n} preview`,className:"mt-2 h-32 w-full rounded-lg border border-gray-200 bg-gray-50 object-contain"}):null}function _({form:n,displayPreviewUrl:s,setImagePreviewUrl:l,watchedImageUrl:i,watchedParagraph:o,blankCount:g,externalErrors:p,showCaseSensitive:u,watchedCaseSensitive:x,afterPoints:b}){return r(t,{children:[u?r("div",{className:"flex flex-wrap items-center gap-4",children:[e("div",{className:"flex-1 rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4",children:e(h,{...n.register("points",{valueAsNumber:!0}),error:n.hasError("points")?n.getFieldError("points"):void 0})}),r("div",{className:"flex items-center gap-3 rounded-xl border-2 px-4 py-3 transition-all "+(x?"border-teal-400 bg-teal-50":"border-gray-200 bg-white hover:border-gray-300"),children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg transition-colors "+(x?"bg-teal-100":"bg-gray-100"),children:e(P,{className:"h-4 w-4 transition-colors "+(x?"text-teal-600":"text-gray-400")})}),r("div",{className:"space-y-0.5",children:[e(d,{htmlFor:"case-sensitive",className:"font-semibold transition-colors "+(x?"text-teal-800":"text-gray-800"),children:"Phân biệt hoa/thường"}),e("p",{className:"text-sm transition-colors "+(x?"text-teal-600":"text-gray-500"),children:x?"Đang bật — đáp án phân biệt chữ hoa/thường":"Đang tắt — không phân biệt chữ hoa/thường"})]}),e(f,{id:"case-sensitive",checked:x||!1,onCheckedChange:e=>n.setValue("caseSensitive",!!e),className:x?"data-[state=checked]:bg-teal-500":"data-[state=unchecked]:bg-gray-300"})]})]}):e("div",{className:"rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4",children:e(h,{...n.register("points",{valueAsNumber:!0}),error:n.hasError("points")?n.getFieldError("points"):void 0})}),b,r("div",{className:"space-y-2",children:[r(d,{htmlFor:"title",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(F,{className:"h-4 w-4 text-teal-500"}),"Tiêu đề"]}),e(c,{id:"title",...n.register("title"),placeholder:"Ví dụ: Dogs, My Family...",className:"border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200 "+(n.hasError("title")?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),n.hasError("title")&&e("p",{className:"text-sm text-red-600",children:n.getFieldError("title")})]}),r("div",{className:"space-y-2",children:[r(d,{className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(B,{className:"h-4 w-4 text-teal-500"}),"Hình ảnh minh họa"]}),e(D,{id:"question-image",label:"",accept:"image/*",value:i||"",onChange:e=>n.setValue("imageUrl",e,{shouldValidate:!0}),onPresignedUrlChange:l,maxSize:5,placeholder:"Upload hình ảnh hoặc dán URL",autoUpload:!0,prefix:"questions"}),s&&e(a,{src:s,alt:"Preview",className:"mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200"})]}),r("div",{className:"space-y-2",children:[r(d,{htmlFor:"paragraph",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(T,{className:"h-4 w-4 text-teal-500"}),"Đoạn văn ",e("span",{className:"text-red-500",children:"*"}),r("span",{className:"text-xs font-normal text-gray-500",children:["(Sử dụng ","{0}",", ","{1}",", ... cho chỗ trống — Tìm thấy ",g," chỗ trống)"]})]}),e(m,{id:"paragraph",...n.register("paragraph"),placeholder:"Monkeys live in the {0} with their family. They can run with their long {1} ...",rows:5,className:"border-gray-200 bg-white text-sm transition-all focus:border-teal-300 focus:ring-teal-200 "+(n.hasError("paragraph")?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),n.hasError("paragraph")&&e("p",{className:"text-sm text-red-600",children:n.getFieldError("paragraph")})]}),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))})]})]})]})}export function WordFillParagraphCreator({initialData:a,onSave:h,onCancel:f,onChange:B,externalErrors:P,onUnsavedChangesChange:Q,validationRef:K,viewMode:Y,groupConfig:Z,groups:ee,questionIndexInPart:re=0,partId:te=""}){const ae=a,{isGrouped:ne,currentGroupNumber:se,groupTitle:le,groupSubtitle:ie,groupContent:oe,imageUrl:de,setGroupTitle:ce,setGroupSubtitle:me,setGroupContent:he,setImageUrl:ge}=L({partId:te,groups:ee,questionIndexInPart:re,initialData:{title:ae?.groupTitle||"",subtitle:ae?.groupSubtitle||"",content:ae?.groupContent||"",imageUrl:ae?.groupImageUrl||""}}),pe=Z&&Z.length>0,ue=(!pe||Z.includes("title"),!pe||Z.includes("subtitle"),!pe||Z.includes("content"),!pe||Z.includes("imageUrl"),"WITH_OPTIONS"===Y),xe="WITH_WORD_BANK"===Y,be="EMAIL"===Y,fe="DEFAULT"===Y||be,we=V({defaultValues:{title:ae?.title||"",imageUrl:ae?.imageUrl||"",paragraph:ae?.paragraph||"",wordBank:ae?.wordBank?.length>0?ae.wordBank:[],answers:ae?.answers?.length>0?ae.answers:[],caseSensitive:ae?.caseSensitive||!1,explanation:ae?.explanation||"",points:ae?.points??1},zodSchema:J,mode:"onBlur"}),ve=V({defaultValues:{title:ae?.title||"",imageUrl:ae?.imageUrl||"",paragraph:ae?.paragraph||"",wordBank:ae?.wordBank?.length>0?ae.wordBank:[],answers:ae?.answers?.length>0?ae.answers:[],exampleAnswer:ae?.exampleAnswer?.length>0?ae.exampleAnswer:[],isExample:ae?.isExample||!1,caseSensitive:ae?.caseSensitive||!1,explanation:ae?.explanation||"",points:ae?.points??1},zodSchema:q,mode:"onBlur"}),Ne=V({defaultValues:{title:ae?.title||"",imageUrl:ae?.imageUrl||"",paragraph:ae?.paragraph||"",answers:ae?.answers?.length>0?ae.answers:[],exampleAnswer:ae?.exampleAnswer?.length>0?ae.exampleAnswer:ae?.content?.exampleAnswer?.length>0?ae.content.exampleAnswer:ae?.answer?.example?.length>0?ae.answer.example:ae?.correctAnswer?.example?.length>0?ae.correctAnswer.example:[],caseSensitive:ae?.caseSensitive||!1,explanation:ae?.explanation||"",points:ae?.points??1},zodSchema:R,mode:"onBlur"}),ye=ue?we:fe?Ne:ve,[ke,Ce]=n(!1),[Se,Ve]=n(""),[Ee,Ae]=n({}),[Ue,Be]=n(ae?.emailFrom||""),[je,Fe]=n(ae?.emailTo||""),Te=s(a),Pe=s(!1),Ie=s(B),$e=s(""),We=s(!1);l(()=>{Ie.current=B},[B]);const ze=S(e=>{We.current=!0,Ie.current?.(e)},300),Me=ye.watch("title"),Oe=ye.watch("imageUrl"),De=ye.watch("paragraph"),Ge=fe?[]:ye.watch("wordBank"),He=ye.watch("answers"),Le=ye.watch("caseSensitive"),qe=ye.watch("explanation"),Je=ye.watch("points"),Re=xe?ve.watch("exampleAnswer"):fe&&Ne.watch("exampleAnswer")||[],Xe=!!xe&&ve.watch("isExample"),{previewUrl:_e}=C(Oe||""),Qe=Se||_e||"",Ke=i(()=>function(e){const r=/\{(\d+)\}/g,t=new Set;let a;for(;null!==(a=r.exec(e));)t.add(parseInt(a[1],10));return Array.from(t).sort((e,r)=>e-r)}(De),[De]),Ye=Ke.length,Ze=i(()=>xe?Ge.map(e=>e.word).filter(e=>e.trim().length>0):[],[xe,Ge]);l(()=>{if(!Pe.current&&0!==Ye)if(ue){const e=we.getValues("wordBank"),r=we.getValues("answers");e.length!==Ye&&we.setValue("wordBank",Array.from({length:Ye},(r,t)=>t<e.length?e[t]:{options:["","",""]})),r.length!==Ye&&we.setValue("answers",Array.from({length:Ye},(e,t)=>t<r.length?r[t]:""))}else if(fe){const e=Ne.getValues("answers");e.length!==Ye&&Ne.setValue("answers",Array.from({length:Ye},(r,t)=>t<e.length?e[t]:""))}else if(xe){const e=ve.getValues("answers");e.length!==Ye&&ve.setValue("answers",Array.from({length:Ye},(r,t)=>t<e.length?e[t]:""))}},[Ye,ue,xe,fe,we,ve,Ne]),l(()=>{if(!ue||Pe.current||Ye===Ge.length)return;const e=[...Ge];let r=!1;for(;e.length<Ye;)e.push({options:["","",""]}),r=!0;if(e.length>Ye&&(e.length=Ye,r=!0),r&&(we.setValue("wordBank",e,{shouldValidate:!0}),He.length>Ye)){const e=[...He];e.length=Ye,we.setValue("answers",e,{shouldValidate:!0})}},[Ye,Ge.length,ue,we,He]),l(()=>{K&&(K.current={triggerValidation:async()=>{const e=await ye.trigger();return console.log("[WordFillParagraphCreator] triggerValidation:",e,ye.formState.errors),e},getFormData:()=>{const e={title:Me,imageUrl:Oe,paragraph:De,wordBank:fe?[]:Ge,answers:He,caseSensitive:Le,explanation:qe,points:Je,viewMode:Y,groupTitle:le,groupSubtitle:ie,groupContent:oe,groupImageUrl:de,groupNumber:se};return be&&(e.emailFrom=Ue,e.emailTo=je),console.log("[WordFillParagraphCreator] getFormData returning:",e),xe?{...e,exampleAnswer:Re,isExample:Xe}:fe?{...e,exampleAnswer:Re}:e}})},[K,ye,Y,Me,Oe,De,Ge,He,Le,qe,Je,Re,Xe,xe,fe,be,Ue,je,le,ie,oe,de,se]),l(()=>{const e=a,r={title:e?.title||"",imageUrl:e?.imageUrl||"",paragraph:e?.paragraph||"",wordBank:e?.wordBank?.length>0?e.wordBank:[],answers:e?.answers?.length>0?e.answers:[],caseSensitive:e?.caseSensitive||!1,explanation:e?.explanation||"",points:e?.points??1,...xe&&{exampleAnswer:e?.exampleAnswer?.length>0?e.exampleAnswer:[],isExample:e?.isExample||!1},...fe&&{exampleAnswer:e?.exampleAnswer?.length>0?e.exampleAnswer:e?.content?.exampleAnswer?.length>0?e.content.exampleAnswer:e?.answer?.example?.length>0?e.answer.example:e?.correctAnswer?.example?.length>0?e.correctAnswer.example:[]}};if(!(JSON.stringify(Te.current)!==JSON.stringify(a)))return;if(Te.current=a,We.current){We.current=!1;if(!(r.paragraph&&r.paragraph!==De)&&!(r.title&&r.title!==Me))return}const t=Me===r.title&&De===r.paragraph&&Oe===r.imageUrl&&Je===r.points;if(t&&!0===a?.isCompleted)return;!t&&(r.title!==Me||r.paragraph!==De||r.imageUrl!==Oe)&&r.paragraph&&(Pe.current=!0,ye.reset(r),be&&(Be(e?.emailFrom||""),Fe(e?.emailTo||"")),setTimeout(()=>{Pe.current=!1},150))},[a,ye,xe,Me,Oe,De,Je]);const er=JSON.stringify(Ge),rr=JSON.stringify(He);l(()=>{const e={title:Me,imageUrl:Oe,paragraph:De,wordBank:fe?[]:Ge,answers:He,caseSensitive:Le,explanation:qe,points:Je,viewMode:Y,groupTitle:le,groupSubtitle:ie,groupContent:oe,groupImageUrl:de,groupNumber:se};if(xe&&(e.exampleAnswer=Re,e.isExample=Xe),fe&&(e.exampleAnswer=Re),be&&(e.emailFrom=Ue,e.emailTo=je),Pe.current)return void($e.current=JSON.stringify(e));const r=JSON.stringify(e);$e.current!==r&&($e.current=r,ze(e))},[Me,Oe,De,er,rr,Le,qe,Je,Re,Xe,Ue,je,le,ie,oe,de,se]);const tr=()=>{ve.setValue("wordBank",[...Ge,{word:"",imageUrl:""}])};if(ke){const t={title:Me,imageUrl:Qe||Oe,paragraph:De,wordBank:Ge,answers:He,caseSensitive:Le,explanation:qe,points:Je,viewMode:Y,emailFrom:be?Ue:void 0,emailTo:be?je:void 0};return r("div",{className:"space-y-6",children:[e(g,{children:e(u,{children:r("div",{className:"flex items-center justify-between",children:[r("div",{children:[e(x,{children:ue?"Điền từ vào đoạn văn - Có lựa chọn (Preview)":fe?"Điền từ vào đoạn văn - Mặc định (Xem trước)":"Điền từ vào đoạn văn (Xem trước)"}),e("p",{className:"mt-1 text-sm text-gray-500",children:"Chế độ xem trước — Hiển thị đáp án đúng"})]}),r(o,{variant:"outline",size:"sm",onClick:()=>Ce(!1),children:[e(E,{className:"mr-2 h-4 w-4"}),"Quay lại chỉnh sửa"]})]})})}),e(G,{questionData:ue?t:fe?{...t,exampleAnswer:Re}:{...t,exampleAnswer:Re,isExample:Xe},isReviewMode:!0,autoFillCorrectAnswers:!!ue||void 0,userAnswer:ue?void 0:He})]})}const ar=ue?"Điền từ vào đoạn văn - Có lựa chọn":fe?"Điền từ vào đoạn văn - Mặc định":"Điền từ vào đoạn văn",nr=ue?"Đọc đoạn văn, chọn từ đúng từ các lựa chọn và viết vào chỗ trống":fe?"Đọc đoạn văn và nhập đáp án trực tiếp cho từng chỗ trống":xe?"Đọc đoạn văn và chọn từ đúng từ word bank":"Đọc đoạn văn và điền từ vào chỗ trống",sr=ue?[r(t,{children:["Nhập ",e("strong",{children:"đoạn văn"})," với chỗ trống dạng ","{0}",", ","{1}",", ","{2}","..."]}),r(t,{children:["Hệ thống tự tạo ",e("strong",{children:"lựa chọn"})," cho từng chỗ trống (thường 3 lựa chọn mỗi chỗ)"]}),r(t,{children:["Nhấn ",e("strong",{children:"ô chữ cái"})," (A, B, C) bên trái để chọn ",e("strong",{children:"đáp án đúng"})," cho từng chỗ trống"]}),r(t,{children:["Đánh dấu ",e("strong",{children:"Example"})," cho chỗ trống mẫu (sẽ được điền sẵn cho học sinh)"]})]:fe?[r(t,{children:["Nhập ",e("strong",{children:"đoạn văn"})," với chỗ trống dạng ","{0}",", ","{1}",", ..."]}),r(t,{children:["Nhập ",e("strong",{children:"đáp án đúng"})," trực tiếp cho từng chỗ trống bên dưới"]})]:xe?[r(t,{children:["Nhập ",e("strong",{children:"đoạn văn"})," với chỗ trống dạng ","{0}",", ","{1}",", ..."]}),r(t,{children:["Thêm các từ vào ",e("strong",{children:"Word Bank"})," (bao gồm cả đáp án đúng và từ nhiễu)"]}),r(t,{children:["Chọn ",e("strong",{children:"đáp án đúng"})," cho từng chỗ trống từ danh sách Word Bank"]}),r(t,{children:["Đánh dấu ",e("strong",{children:"Example"})," cho từ mẫu (sẽ được điền sẵn cho học sinh)"]})]:[r(t,{children:["Nhập ",e("strong",{children:"đoạn văn"})," với chỗ trống dạng ","{0}",", ","{1}",", ..."]}),r(t,{children:["Nhập ",e("strong",{children:"đáp án đúng"})," cho từng chỗ trống"]})],lr=ue?!!De&&Ge.length>0:!!De&&Ye>0,ir=ue?"bg-gradient-to-r from-emerald-500 via-teal-500 to-cyan-500":fe?"bg-gradient-to-r from-indigo-500 via-blue-500 to-cyan-500":"bg-gradient-to-r from-teal-500 via-cyan-500 to-blue-500",or=ue?"bg-gradient-to-br from-emerald-500 to-teal-600":fe?"bg-gradient-to-br from-indigo-500 to-blue-600":"bg-gradient-to-br from-teal-500 to-cyan-600",dr=ue?U:F;return r("div",{className:"space-y-6",children:[ne&&e(H,{groupNumber:se,title:le,subtitle:ie,content:oe,imageUrl:de,onTitleChange:ce,onSubtitleChange:me,onContentChange:he,onImageChange:ge,groupConfig:Z,uploadIdPrefix:`word-fill-paragraph-${te??"group"}`}),r(g,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e("div",{className:`h-1.5 ${ir}`}),e(u,{className:"pb-4",children:r("div",{className:"flex items-center justify-between",children:[r("div",{className:"flex items-center gap-3",children:[e("div",{className:`flex h-10 w-10 items-center justify-center rounded-xl ${or} shadow-md`,children:e(dr,{className:"h-5 w-5 text-white"})}),r("div",{children:[e(x,{className:"text-xl font-bold text-gray-900",children:ar}),e("p",{className:"text-sm text-gray-500",children:nr})]})]}),r(o,{variant:"outline",size:"sm",onClick:()=>Ce(!0),disabled:!lr,className:"gap-2 border-teal-200 text-teal-600 hover:bg-teal-50 hover:text-teal-700",children:[e(A,{className:"h-4 w-4"}),"Xem trước"]})]})}),r(p,{className:"space-y-6",children:[e("div",{className:"rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-5 shadow-sm",children:r("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(j,{className:"h-4 w-4 text-blue-600"})}),r("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"}),e("ul",{className:"mt-2 space-y-1.5 text-blue-700",children:sr.map((t,a)=>r("li",{className:"flex items-center gap-2",children:[e("span",{className:"h-1.5 w-1.5 rounded-full bg-blue-400"}),t]},a))})]})]})}),e(_,{form:ye,displayPreviewUrl:Qe,setImagePreviewUrl:Ve,watchedImageUrl:Oe,watchedParagraph:De,blankCount:Ye,externalErrors:P,showCaseSensitive:xe,watchedCaseSensitive:Le,afterPoints:be?r("div",{className:"space-y-4 rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-sky-50 p-4",children:[r("div",{className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(M,{className:"h-4 w-4 text-blue-500"}),"Email"]}),r("div",{className:"grid grid-cols-1 gap-4 sm:grid-cols-2",children:[r("div",{className:"space-y-1.5",children:[e(d,{htmlFor:"emailFrom",className:"text-sm font-medium text-gray-700",children:"From"}),e(c,{id:"emailFrom",value:Ue,onChange:e=>Be(e.target.value),placeholder:"Ví dụ: Brenda",className:"border-gray-200 bg-white transition-all focus:border-blue-300 focus:ring-blue-200"})]}),r("div",{className:"space-y-1.5",children:[e(d,{htmlFor:"emailTo",className:"text-sm font-medium text-gray-700",children:"To"}),e(c,{id:"emailTo",value:je,onChange:e=>Fe(e.target.value),placeholder:"Ví dụ: Ewan",className:"border-gray-200 bg-white transition-all focus:border-blue-300 focus:ring-blue-200"})]})]})]}):void 0})]})]}),ue&&r(g,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e("div",{className:"h-1 bg-gradient-to-r from-teal-400 to-cyan-400"}),e(u,{className:"pb-4",children:r("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-teal-100",children:e(W,{className:"h-3.5 w-3.5 text-teal-600"})}),r(x,{className:"text-lg font-semibold text-gray-800",children:["Lựa chọn cho từng chỗ trống (",Ge.length,")"]})]})}),r(p,{className:"space-y-4",children:[0===Ge.length&&e("div",{className:"rounded-lg border-2 border-dashed border-gray-200 p-8 text-center",children:r("p",{className:"text-sm text-gray-500",children:["Chưa có chỗ trống nào. Nhập đoạn văn với ","{0}",", ","{1}","... để tạo chỗ trống."]})}),Ge.map((t,a)=>{const n=He[a]||"",s=t.isExample||!1;return r("div",{className:"rounded-xl border-2 p-4 transition-all "+(s?"border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50":"border-gray-200 bg-white hover:border-teal-200"),children:[r("div",{className:"mb-3 flex items-center justify-between",children:[r("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg text-sm font-bold text-white "+(s?"bg-gradient-to-br from-amber-400 to-orange-500":"bg-gradient-to-br from-teal-500 to-cyan-600"),children:a}),e("span",{className:"text-sm font-semibold text-gray-700",children:s?`Chỗ trống ${a} (Example)`:`Chỗ trống ${a}`}),n&&r("span",{className:"flex items-center gap-1 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-700",children:[e(z,{className:"h-3 w-3"}),n]})]}),r("label",{className:"flex items-center gap-2 text-xs text-gray-600",children:[e(b,{checked:s,onCheckedChange:e=>((e,r)=>{const t=[...Ge];t[e]={...t[e],isExample:r},we.setValue("wordBank",t)})(a,!0===e)}),"Example"]})]}),r("div",{className:"space-y-2",children:[(t.options||[]).map((s,l)=>r("div",{className:"flex items-center gap-2",children:[e("button",{type:"button",onClick:()=>s.trim()&&((e,r)=>{const t=[...He];for(;t.length<=e;)t.push("");t[e]=r,we.setValue("answers",t)})(a,s),className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg text-xs font-bold transition-all "+(n===s&&s.trim()?"bg-green-500 text-white shadow-sm":"bg-gray-100 text-gray-500 hover:bg-green-100 hover:text-green-600"),title:"Chọn làm đáp án đúng",children:String.fromCharCode(65+l)}),e(c,{value:s,onChange:e=>((e,r,t)=>{const a=[...Ge],n=[...a[e]?.options||[]],s=n[r];if(n[r]=t,a[e]={...a[e],options:n},we.setValue("wordBank",a,{shouldValidate:!0}),s){const r=[...He];r[e]===s&&(r[e]=t,we.setValue("answers",r))}})(a,l,e.target.value),placeholder:`Lựa chọn ${String.fromCharCode(65+l)}`,className:"flex-1 border-gray-200 text-sm"}),(t.options||[]).length>2&&e(o,{variant:"ghost",size:"sm",onClick:()=>((e,r)=>{const t=[...Ge],a=t[e]?.options?.[r];if(t[e]={...t[e],options:(t[e]?.options||[]).filter((e,t)=>t!==r)},we.setValue("wordBank",t,{shouldValidate:!0}),a){const r=[...He];r[e]===a&&(r[e]="",we.setValue("answers",r))}})(a,l),className:"h-8 w-8 p-0 text-red-400 hover:bg-red-50 hover:text-red-600",children:e($,{className:"h-3.5 w-3.5"})})]},l)),r(o,{variant:"ghost",size:"sm",onClick:()=>(e=>{const r=[...Ge];r[e]={...r[e],options:[...r[e]?.options||[],""]},we.setValue("wordBank",r)})(a),className:"mt-1 gap-1 text-xs text-teal-600 hover:bg-teal-50 hover:text-teal-700",children:[e(I,{className:"h-3 w-3"}),"Thêm lựa chọn"]})]}),ye.formState.errors.answers?.[a]&&e("p",{className:"mt-2 text-xs font-medium text-red-500",children:ye.formState.errors.answers[a]?.message})]},a)})]})]}),xe&&r(t,{children:[r(g,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-purple-100/50",children:[e("div",{className:"h-1 bg-gradient-to-r from-purple-400 to-pink-400"}),e(u,{className:"pb-4",children:r("div",{className:"flex items-center justify-between",children:[r("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-purple-100",children:e(T,{className:"h-3.5 w-3.5 text-purple-600"})}),r(x,{className:"text-lg font-semibold text-gray-800",children:["Word Bank (",Ge.length," từ)"]})]}),r(o,{type:"button",variant:"outline",size:"sm",onClick:tr,className:"gap-1.5 border-purple-200 text-purple-600 hover:bg-purple-50 hover:text-purple-700",children:[e(I,{className:"h-3.5 w-3.5"}),"Thêm từ"]})]})}),r(p,{className:"space-y-4",children:[r("p",{className:"text-xs text-gray-500",children:["Thêm tất cả các từ gợi ý cho học sinh (bao gồm ",e("strong",{children:"đáp án đúng"})," và"," ",e("strong",{children:"từ nhiễu"}),"). Đánh dấu ",e("strong",{children:"Example"})," cho từ mẫu đã được điền sẵn."]}),Ge.length>0?e("div",{className:"grid grid-cols-1 gap-3 md:grid-cols-2",children:Ge.map((t,a)=>r("div",{className:"space-y-3 rounded-lg border p-4 transition-all "+(t.isExample?"border-amber-300 bg-amber-50/50":"border-gray-200 bg-gray-50/50 hover:border-gray-300"),children:[r("div",{className:"flex items-center justify-between",children:[r("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-lg text-xs font-bold text-white shadow-sm "+(t.isExample?"bg-gradient-to-br from-amber-400 to-orange-500":"bg-gradient-to-br from-purple-500 to-pink-600"),children:a+1}),t.isExample&&e("span",{className:"rounded-full border border-amber-200 bg-amber-100 px-1.5 py-0.5 text-[10px] font-semibold text-amber-700",children:"EXAMPLE"})]}),r("div",{className:"flex items-center gap-2",children:[r("div",{className:"flex items-center gap-1.5",children:[e(b,{id:`example-${a}`,checked:t.isExample||!1,onCheckedChange:e=>((e,r)=>{const t=[...Ge];t[e]={...t[e],isExample:r},ve.setValue("wordBank",t),ve.setValue("exampleAnswer",t.filter(e=>e.isExample).map(e=>e.word))})(a,!!e)}),e(d,{htmlFor:`example-${a}`,className:"cursor-pointer text-xs font-medium text-gray-600",children:"Example"})]}),e(o,{type:"button",variant:"ghost",size:"sm",onClick:()=>(e=>{const r=Ge[e]?.word,t=Ge.filter((r,t)=>t!==e);ve.setValue("wordBank",t);const a={...Ee};delete a[e];const n={};Object.entries(a).forEach(([r,t])=>{const a=parseInt(r);n[a>e?a-1:a]=t}),Ae(n),r&&(ve.setValue("answers",He.map(e=>e===r?"":e)),ve.setValue("exampleAnswer",(Re||[]).filter(e=>e!==r)))})(a),className:"h-7 w-7 p-0 text-gray-400 hover:bg-red-50 hover:text-red-500",children:e($,{className:"h-3.5 w-3.5"})})]})]}),r("div",{children:[r(d,{className:"text-xs text-gray-500",children:["Từ ",e("span",{className:"text-red-500",children:"*"})]}),e(c,{value:t.word,onChange:e=>((e,r)=>{const t=Ge[e]?.word,a=[...Ge];a[e]={...a[e],word:r},ve.setValue("wordBank",a,{shouldValidate:!0}),t&&t!==r&&(ve.setValue("answers",He.map(e=>e===t?r:e)),ve.setValue("exampleAnswer",(Re||[]).map(e=>e===t?r:e)))})(a,e.target.value),placeholder:"Nhập từ...",className:`${t.isExample?"bg-amber-50":""} ${ve.formState.errors.wordBank?.[a]?.word?"border-red-300 focus:border-red-400 focus:ring-red-200":"border-gray-200"}`}),ve.formState.errors.wordBank?.[a]?.word?.message&&e("p",{className:"mt-1 text-xs text-red-500",children:ve.formState.errors.wordBank[a].word.message})]}),r("div",{children:[e(d,{className:"text-xs text-gray-500",children:"Nội dung (tuỳ chọn)"}),e(m,{value:t.content||"",onChange:e=>{const r=[...Ge];r[a]={...r[a],content:e.target.value},ve.setValue("wordBank",r)},placeholder:"Nhập nội dung...",className:(t.isExample?"bg-amber-50":"")+" min-h-[88px] border-gray-200"})]}),r("div",{children:[e(d,{className:"text-xs text-gray-500",children:"Hình ảnh (tuỳ chọn)"}),e(D,{id:`word-bank-image-${a}`,label:"",accept:"image/*",value:t.imageUrl||"",onChange:e=>{const r=[...Ge];r[a]={...r[a],imageUrl:e},ve.setValue("wordBank",r)},onPresignedUrlChange:e=>Ae(r=>({...r,[a]:e})),maxSize:5,placeholder:"Upload hình",autoUpload:!0,prefix:"questions"}),e(X,{imageUrl:t.imageUrl||"",localPreviewUrl:Ee[a],index:a})]})]},a))}):r("div",{className:"flex h-28 flex-col items-center justify-center gap-2 rounded-lg border-2 border-dashed border-gray-200 bg-gray-50",children:[e("p",{className:"text-sm text-gray-400",children:"Chưa có từ nào trong Word Bank"}),r(o,{type:"button",variant:"outline",size:"sm",onClick:tr,className:"gap-1.5 border-purple-200 text-purple-600",children:[e(I,{className:"h-3.5 w-3.5"}),"Thêm từ đầu tiên"]})]}),ve.formState.errors.wordBank?.message&&e("p",{className:"text-sm text-red-600",children:ve.formState.errors.wordBank.message})]})]}),r(g,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-green-100/50",children:[e("div",{className:"h-1 bg-gradient-to-r from-green-400 to-emerald-400"}),e(u,{className:"pb-4",children:r("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-green-100",children:e(W,{className:"h-3.5 w-3.5 text-green-600"})}),r(x,{className:"text-lg font-semibold text-gray-800",children:["Đáp án đúng (",Ye," chỗ trống)"]})]})}),r(p,{className:"space-y-4",children:[Ye>0?r(t,{children:[r("p",{className:"text-xs text-gray-500",children:["Chọn từ đúng từ ",e("strong",{children:"Word Bank"})," cho mỗi chỗ trống trong đoạn văn."]}),e("div",{className:"space-y-3",children:Ke.map(t=>{const a=He[t]||"",n=Ge.some(e=>e.isExample&&e.word===a);return r("div",{className:"flex items-center gap-4 rounded-lg border p-4 transition-all "+(a?n?"border-amber-300 bg-amber-50/50":"border-green-300 bg-green-50/50":"border-gray-200 bg-gray-50/50"),children:[r("div",{className:"flex min-w-[140px] items-center gap-3",children:[e("div",{className:"flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg text-sm font-bold text-white shadow-sm "+(a?n?"bg-gradient-to-br from-amber-400 to-orange-500":"bg-gradient-to-br from-green-500 to-emerald-600":"bg-gradient-to-br from-gray-400 to-gray-500"),children:"{"+t+"}"}),r("div",{children:[r("span",{className:"text-sm font-semibold text-gray-700",children:["Chỗ trống (",t+1,")"]}),n&&e("span",{className:"block text-[10px] font-medium text-amber-600",children:"Example - Điền sẵn"})]})]}),e("div",{className:"flex-1",children:r(w,{value:a||void 0,onValueChange:e=>((e,r)=>{const t=[...He];t[e]=r,ve.setValue("answers",t)})(t,e),children:[e(y,{className:"w-full "+(ve.formState.errors.answers?.[t]?"border-red-300 ring-1 ring-red-200":a?"border-green-300 bg-white":"border-gray-200"),children:e(k,{placeholder:"Chọn từ đúng từ Word Bank..."})}),e(v,{children:Ze.length>0?Ze.map((a,n)=>{const s=He.some((e,r)=>e===a&&r!==t),l=Ge.find(e=>e.word===a)?.isExample;return e(N,{value:a,disabled:s,children:r("div",{className:"flex items-center gap-2",children:[e("span",{className:s?"text-gray-400":"",children:a}),l&&e("span",{className:"rounded bg-amber-100 px-1 py-0.5 text-[10px] font-medium text-amber-600",children:"example"}),s&&e("span",{className:"text-[10px] text-gray-400",children:"(đã dùng)"})]})},`${a}-${n}`)}):e("div",{className:"py-4 text-center text-sm text-gray-400",children:"Chưa có từ nào trong Word Bank"})})]})}),e("div",{className:"flex-shrink-0",children:a?e("div",{className:"flex h-8 w-8 items-center justify-center rounded-full bg-green-100",children:e(W,{className:"h-4 w-4 text-green-600"})}):ve.formState.errors.answers?.[t]?e("div",{className:"flex h-8 w-8 items-center justify-center rounded-full bg-red-100",children:e("span",{className:"text-xs font-bold text-red-500",children:"!"})}):e("div",{className:"flex h-8 w-8 items-center justify-center rounded-full bg-gray-100",children:e("span",{className:"text-xs text-gray-400",children:"?"})})})]},t)})}),e("div",{className:"rounded-lg border border-gray-200 bg-gray-50 p-3",children:r("div",{className:"flex items-center justify-between text-sm",children:[r("span",{className:"text-gray-600",children:["Đã chọn đáp án: ",e("strong",{children:He.filter(e=>e).length})," / ",Ye]}),r("span",{className:"text-gray-600",children:["Từ nhiễu:"," ",e("strong",{children:Ge.length-He.filter(e=>e).length})," từ"]})]})})]}):e("div",{className:"flex h-24 items-center justify-center rounded-lg border-2 border-dashed border-gray-200 bg-gray-50",children:r("p",{className:"text-sm text-gray-400",children:["Nhập đoạn văn có ","{0}",", ","{1}",", ... để tạo chỗ trống"]})}),ve.formState.errors.answers?.message&&e("p",{className:"text-sm text-red-600",children:ve.formState.errors.answers.message})]})]})]}),fe&&r(g,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-indigo-100/50",children:[e("div",{className:"h-1 bg-gradient-to-r from-indigo-400 via-blue-500 to-cyan-500"}),e(u,{className:"pb-4",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-gradient-to-br from-indigo-500 to-blue-600 shadow-md",children:e(W,{className:"h-4 w-4 text-white"})}),e(x,{className:"text-lg font-semibold text-gray-800",children:"Đáp án cho từng chỗ trống"})]})}),r(p,{className:"space-y-4",children:[Ye>0?r(t,{children:[e("div",{className:"space-y-3",children:Ke.map(t=>{const a=(He[t]||"").trim(),n=!!a&&Re.includes(a);return r("div",{className:"rounded-xl border-2 p-4 transition-all "+(n?"border-amber-300 bg-gradient-to-r from-amber-50 to-yellow-50":He[t]?"border-green-200 bg-gradient-to-r from-green-50 to-emerald-50":"border-gray-200 bg-white"),children:[r("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-bold transition-all "+(n?"bg-gradient-to-br from-amber-500 to-orange-500 text-white shadow-md":He[t]?"bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md":"bg-indigo-100 text-indigo-700"),children:`{${t}}`}),e(c,{value:He[t]||"",onChange:e=>((e,r)=>{const t=[...He],a=t[e];t[e]=r,Ne.setValue("answers",t,{shouldValidate:!0});const n=Ne.getValues("exampleAnswer")||[];if(a&&n.includes(a)){const e=n.map(e=>e===a?r:e).filter(e=>e);Ne.setValue("exampleAnswer",e)}})(t,e.target.value),placeholder:`Đáp án cho {${t}}`,className:"transition-all "+(n?"border-amber-300 bg-white focus:border-amber-400 focus:ring-amber-200":He[t]?"border-green-300 bg-white focus:border-green-400 focus:ring-green-200":"border-gray-200 focus:border-indigo-400 focus:ring-indigo-200")}),r("button",{type:"button",disabled:!a,onClick:()=>(e=>{const r=(He[e]||"").trim();if(!r)return;const t=(Ne.getValues("exampleAnswer")||[]).filter(e=>e),a=t.includes(r)?t.filter(e=>e!==r):[...t,r];Ne.setValue("exampleAnswer",a,{shouldValidate:!0})})(t),title:n?"Bỏ đánh dấu Example":"Đánh dấu làm Example",className:`inline-flex h-10 items-center gap-1.5 rounded-lg border px-3 text-sm font-medium transition-colors select-none ${n?"border-amber-300 bg-amber-50 text-amber-700 hover:bg-amber-100":"border-gray-200 bg-white text-gray-400 hover:text-gray-600"} ${a?"":"cursor-not-allowed opacity-50"}`,children:[e(O,{className:"h-4 w-4 "+(n?"fill-amber-400 text-amber-400":"")}),"Example"]})]}),Ne.formState.errors.answers?.[t]&&e("p",{className:"mt-1.5 ml-13 text-xs text-red-500",children:Ne.formState.errors.answers[t]?.message})]},t)})}),r("div",{className:"flex items-center gap-2 rounded-lg bg-indigo-50/50 px-3 py-2",children:[e(z,{className:"h-4 w-4 text-indigo-500"}),r("span",{className:"text-xs text-indigo-700",children:[He.filter(e=>e?.trim()).length," / ",Ye," đáp án đã điền"]})]})]}):e("div",{className:"flex h-24 items-center justify-center rounded-lg border-2 border-dashed border-gray-200 bg-gray-50",children:r("p",{className:"text-sm text-gray-400",children:["Nhập đoạn văn có ","{0}",", ","{1}",", ... để tạo chỗ trống"]})}),Ne.formState.errors.answers?.message&&e("p",{className:"text-sm text-red-600",children:Ne.formState.errors.answers.message})]})]}),r(g,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50",children:[e("div",{className:"h-1 bg-gradient-to-r from-amber-400 to-orange-400"}),e(u,{className:"pb-4",children:r("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-9 w-9 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm",children:e(U,{className:"h-4 w-4 text-white"})}),r("div",{children:[r("div",{className:"flex items-center gap-2",children:[e(x,{className:"text-lg 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-sm text-gray-500 mt-0.5",children:"Giải thích cho đáp án đúng — hiển thị sau khi học sinh hoàn thành câu hỏi"})]})]})}),e(p,{children:e(m,{...ye.register("explanation"),placeholder:"VD: Câu trả lời đúng là... vì...",rows:3,className:"min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none"})})]})]})}