@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,196 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useRef, useEffect } from 'react';
4
- import { Button } from '../../../../components/ui/button';
5
- import { Label } from '../../../../components/ui/label';
6
- import { Input } from '../../../../components/ui/input';
7
- import { Textarea } from '../../../../components/ui/textarea';
8
- import { PointsInput } from '../../../../components/ui/points-input';
9
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
10
- import { Switch } from '../../../../components/ui/switch';
11
- import { Pencil, Eye, Lightbulb, PenTool, MessageSquare, ToggleLeft, CheckCircle2, BookOpen } from 'lucide-react';
12
- import { WriteCorrectVerbFormClient } from './WriteCorrectVerbFormClient';
13
- import { useDebouncedCallback } from '../../../../shared/lib/hooks';
14
- function WriteCorrectVerbFormCreatorContent({ initialData, onSave, onCancel, onChange, externalErrors, onUnsavedChangesChange, validationRef, }) {
15
- // ============ STATE ============
16
- const [sentence, setSentence] = useState(initialData?.sentence || '');
17
- const [verbHint, setVerbHint] = useState(initialData?.verbHint || '');
18
- const [correctAnswer, setCorrectAnswer] = useState(initialData?.correctAnswer || '');
19
- const [caseSensitive, setCaseSensitive] = useState(initialData?.caseSensitive || false);
20
- const [instruction, setInstruction] = useState(initialData?.instruction ||
21
- 'Write the correct form of the verb in parentheses to complete each sentence. Pay attention to the tense.');
22
- const [explanation, setExplanation] = useState(initialData?.explanation || '');
23
- const [points, setPoints] = useState(initialData?.points || 1);
24
- // UI State
25
- const [isClientMode, setIsClientMode] = useState(false);
26
- // Inline validation errors
27
- const [validationErrors, setValidationErrors] = useState({});
28
- // Refs for initialization tracking
29
- const previousInitialDataRef = useRef(initialData);
30
- const isInitializingRef = useRef(false);
31
- const onChangeRef = useRef(onChange);
32
- const previousPayloadRef = useRef('');
33
- // Update onChange ref when it changes
34
- useEffect(() => {
35
- onChangeRef.current = onChange;
36
- }, [onChange]);
37
- // Debounced onChange callback
38
- const debouncedOnChange = useDebouncedCallback((data) => {
39
- onChangeRef.current?.(data);
40
- }, 300);
41
- // ============ SYNC INITIAL DATA ============
42
- useEffect(() => {
43
- const initialDataChanged = JSON.stringify(previousInitialDataRef.current) !== JSON.stringify(initialData);
44
- if (initialDataChanged) {
45
- isInitializingRef.current = true;
46
- previousInitialDataRef.current = initialData;
47
- if (initialData) {
48
- if (initialData.sentence)
49
- setSentence(initialData.sentence);
50
- if (initialData.verbHint)
51
- setVerbHint(initialData.verbHint);
52
- if (initialData.correctAnswer)
53
- setCorrectAnswer(initialData.correctAnswer);
54
- if (initialData.caseSensitive !== undefined)
55
- setCaseSensitive(initialData.caseSensitive);
56
- if (initialData.instruction)
57
- setInstruction(initialData.instruction);
58
- if (initialData.explanation)
59
- setExplanation(initialData.explanation);
60
- if (initialData.points)
61
- setPoints(initialData.points);
62
- }
63
- setTimeout(() => {
64
- isInitializingRef.current = false;
65
- }, 150);
66
- }
67
- }, [initialData]);
68
- // ============ ON CHANGE CALLBACK ============
69
- useEffect(() => {
70
- if (isInitializingRef.current) {
71
- const currentPayload = JSON.stringify({
72
- sentence,
73
- verbHint,
74
- correctAnswer,
75
- caseSensitive,
76
- instruction,
77
- explanation,
78
- points,
79
- });
80
- previousPayloadRef.current = currentPayload;
81
- return;
82
- }
83
- const payload = {
84
- sentence,
85
- verbHint,
86
- correctAnswer,
87
- caseSensitive,
88
- instruction,
89
- explanation,
90
- points,
91
- };
92
- const payloadString = JSON.stringify(payload);
93
- if (previousPayloadRef.current !== payloadString) {
94
- previousPayloadRef.current = payloadString;
95
- debouncedOnChange(payload);
96
- }
97
- }, [sentence, verbHint, correctAnswer, caseSensitive, instruction, explanation, points]);
98
- // NOTE: Status tracking is now handled entirely by ExamCreator via localStorage (exam_question_status_{id})
99
- // The onChange callback triggers handleQuestionDraftChange in ExamCreator which updates localStorage
100
- // Expose validation function via ref
101
- useEffect(() => {
102
- if (validationRef) {
103
- validationRef.current = {
104
- triggerValidation: async () => {
105
- return validateForm();
106
- },
107
- // BUGFIX: Expose getFormData to get fresh form data at save time
108
- // This bypasses the debounced onChange race condition
109
- getFormData: () => ({
110
- sentence,
111
- verbHint,
112
- correctAnswer,
113
- caseSensitive,
114
- instruction,
115
- explanation,
116
- points,
117
- }),
118
- };
119
- }
120
- }, [validationRef, sentence, verbHint, correctAnswer, caseSensitive, instruction, explanation, points]);
121
- // ============ VALIDATION ============
122
- const validateForm = () => {
123
- const errors = {};
124
- if (!sentence.trim()) {
125
- errors.sentence = 'Vui lòng nhập câu hỏi!';
126
- }
127
- else if (!sentence.includes('{0}')) {
128
- errors.sentence = 'Câu cần có chỗ trống {0}!';
129
- }
130
- if (!verbHint.trim()) {
131
- errors.verbHint = 'Vui lòng nhập gợi ý động từ!';
132
- }
133
- if (!correctAnswer.trim()) {
134
- errors.correctAnswer = 'Vui lòng nhập đáp án đúng!';
135
- }
136
- setValidationErrors(errors);
137
- return Object.keys(errors).length === 0;
138
- };
139
- const isValid = () => {
140
- if (!sentence.trim() || !verbHint.trim() || !correctAnswer.trim()) {
141
- return false;
142
- }
143
- if (!sentence.includes('{0}')) {
144
- return false;
145
- }
146
- return true;
147
- };
148
- // Handle save
149
- const handleSave = () => {
150
- if (!validateForm()) {
151
- return;
152
- }
153
- // NOTE: Save status is now managed by ExamCreator via localStorage
154
- const saveData = {
155
- sentence,
156
- verbHint,
157
- correctAnswer,
158
- caseSensitive,
159
- instruction,
160
- explanation,
161
- points,
162
- };
163
- onSave?.(saveData);
164
- };
165
- // ============ CLIENT MODE PREVIEW ============
166
- if (isClientMode) {
167
- const questionData = {
168
- sentence,
169
- verbHint,
170
- correctAnswer,
171
- caseSensitive,
172
- instruction,
173
- };
174
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(CardTitle, { children: "Write the Correct Form of the Verb (Preview)" }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(false), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), "Quay l\u1EA1i Edit Mode"] })] }) }) }), _jsx(WriteCorrectVerbFormClient, { questionData: { ...questionData, explanation }, isReviewMode: true, userAnswer: correctAnswer })] }));
175
- }
176
- // ============ CREATOR UI ============
177
- return (_jsxs("div", { className: "space-y-6", children: [_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50", children: [_jsx("div", { className: "h-1.5 bg-gradient-to-r from-orange-500 via-amber-500 to-yellow-500" }), _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 bg-gradient-to-br from-orange-500 to-amber-600 shadow-md", children: _jsx(PenTool, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold text-gray-900", children: "Vi\u1EBFt d\u1EA1ng \u0111\u00FAng c\u1EE7a \u0111\u1ED9ng t\u1EEB" }), _jsx("p", { className: "text-sm text-gray-500", children: "Chia \u0111\u1ED9ng t\u1EEB trong ngo\u1EB7c \u0111\u00FAng th\u00EC" })] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: !isValid(), className: "gap-2 border-amber-200 text-amber-600 hover:bg-amber-50 hover:text-amber-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-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: _jsx(PointsInput, { defaultValue: points, onChange: (e) => setPoints(Number(e.target.value) || 1) }) }), _jsx("div", { className: "rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-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-amber-100", children: _jsx(Lightbulb, { className: "h-4 w-4 text-amber-600" }) }), _jsxs("div", { className: "text-sm text-amber-900", children: [_jsx("p", { className: "font-semibold text-amber-800", children: "H\u01B0\u1EDBng d\u1EABn t\u1EA1o c\u00E2u h\u1ECFi" }), _jsxs("ul", { className: "mt-2 space-y-1.5 text-amber-700", children: [_jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-amber-400" }), "S\u1EED d\u1EE5ng ", _jsx("code", { className: "mx-1 rounded bg-amber-100 px-1.5 py-0.5 font-mono text-amber-800", children: '{0}' }), " \u0111\u1EC3 \u0111\u00E1nh d\u1EA5u ch\u1ED7 tr\u1ED1ng trong c\u00E2u"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-amber-400" }), "Nh\u1EADp \u0111\u1ED9ng t\u1EEB g\u1EE3i \u00FD (d\u1EA1ng nguy\u00EAn m\u1EABu) v\u00E0o \u00F4 \"G\u1EE3i \u00FD \u0111\u1ED9ng t\u1EEB\""] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-amber-400" }), "H\u1ECDc sinh s\u1EBD vi\u1EBFt d\u1EA1ng \u0111\u00FAng c\u1EE7a \u0111\u1ED9ng t\u1EEB \u0111\u1EC3 ho\u00E0n th\u00E0nh c\u00E2u"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-amber-400" }), "V\u00ED d\u1EE5: ", _jsxs("code", { className: "ml-1 rounded bg-amber-100 px-1.5 py-0.5 font-mono text-amber-800 select-all cursor-pointer", title: "Click \u0111\u1EC3 copy", children: ["John ", '{0}', " his homework at night."] })] })] })] })] }) }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-4 w-4 text-amber-500" }), "C\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { value: sentence, onChange: (e) => {
178
- setSentence(e.target.value);
179
- if (validationErrors.sentence) {
180
- setValidationErrors(prev => ({ ...prev, sentence: undefined }));
181
- }
182
- }, placeholder: "V\u00ED d\u1EE5: John {0} his homework at night.", className: `border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 ${validationErrors.sentence ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.sentence && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.sentence }))] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: `rounded-xl border-2 p-4 transition-all ${verbHint ? 'border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50' : 'border-gray-200 bg-white'}`, children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-700", children: [_jsx("span", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-amber-100 text-xs font-bold text-amber-600", children: "V" }), "G\u1EE3i \u00FD \u0111\u1ED9ng t\u1EEB ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { value: verbHint, onChange: (e) => {
183
- setVerbHint(e.target.value);
184
- if (validationErrors.verbHint) {
185
- setValidationErrors(prev => ({ ...prev, verbHint: undefined }));
186
- }
187
- }, placeholder: "V\u00ED d\u1EE5: do", className: `mt-2 border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 ${validationErrors.verbHint ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.verbHint && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: validationErrors.verbHint }))] }), _jsxs("div", { className: `rounded-xl border-2 p-4 transition-all ${correctAnswer ? 'border-green-200 bg-gradient-to-r from-green-50 to-emerald-50' : 'border-gray-200 bg-white'}`, children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-700", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), "\u0110\u00E1p \u00E1n \u0111\u00FAng ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { value: correctAnswer, onChange: (e) => {
188
- setCorrectAnswer(e.target.value);
189
- if (validationErrors.correctAnswer) {
190
- setValidationErrors(prev => ({ ...prev, correctAnswer: undefined }));
191
- }
192
- }, placeholder: "V\u00ED d\u1EE5: does", className: `mt-2 border-gray-200 bg-white transition-all focus:border-green-300 focus:ring-green-200 ${validationErrors.correctAnswer ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.correctAnswer && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: validationErrors.correctAnswer }))] })] }), _jsxs("div", { className: "flex items-center justify-between rounded-xl border-2 border-gray-200 bg-white p-4 transition-all hover:border-gray-300", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gray-100", children: _jsx(ToggleLeft, { className: "h-4 w-4 text-gray-600" }) }), _jsxs("div", { className: "space-y-0.5", children: [_jsx(Label, { htmlFor: "caseSensitive", className: "font-semibold text-gray-800", children: "Ph\u00E2n bi\u1EC7t ch\u1EEF hoa/th\u01B0\u1EDDng" }), _jsx("p", { className: "text-sm text-gray-500", children: "B\u1EADt n\u1EBFu \u0111\u00E1p \u00E1n c\u1EA7n kh\u1EDBp ch\u00EDnh x\u00E1c (v\u00ED d\u1EE5: \"Does\" \u2260 \"does\")" })] })] }), _jsx(Switch, { id: "caseSensitive", checked: caseSensitive, onCheckedChange: setCaseSensitive })] }), sentence && verbHint && (_jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2 text-xs font-semibold text-gray-600", children: [_jsx(Eye, { className: "h-3.5 w-3.5" }), "Xem tr\u01B0\u1EDBc c\u00E2u h\u1ECFi"] }), _jsxs("p", { className: "text-sm text-gray-700", children: [sentence.replace('{0}', '_______'), " ", _jsxs("span", { className: "font-semibold text-amber-600", children: ["(", verbHint, ")"] })] }), correctAnswer && (_jsxs("div", { className: "mt-2 flex items-center gap-2 rounded-lg bg-green-50 px-3 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-500" }), _jsxs("span", { className: "text-sm text-green-700", children: ["\u0110\u00E1p \u00E1n: ", _jsx("strong", { children: sentence.replace('{0}', correctAnswer) })] })] }))] })), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100", children: _jsx("span", { className: "text-lg", children: "\u274C" }) }), _jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("ul", { className: "mt-1 list-inside list-disc text-red-700", children: externalErrors.map((error, index) => (_jsx("li", { children: error }, index))) })] })] }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50", children: [_jsx("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, { id: "explanation", value: explanation, onChange: (e) => setExplanation(e.target.value), placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0... v\u00EC...", rows: 3, className: "min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none" }) })] })] }));
193
- }
194
- export function WriteCorrectVerbFormCreator(props) {
195
- return _jsx(WriteCorrectVerbFormCreatorContent, { ...props });
196
- }
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{useState as t,useRef as n,useEffect as a}from"react";import{Button as s}from"../../../../components/ui/button";import{Label as i}from"../../../../components/ui/label";import{Input as c}from"../../../../components/ui/input";import{Textarea as l}from"../../../../components/ui/textarea";import{PointsInput as o}from"../../../../components/ui/points-input";import{Card as d,CardContent as m,CardHeader as h,CardTitle as g}from"../../../../components/ui/card";import{Switch as b}from"../../../../components/ui/switch";import{Pencil as u,Eye as p,Lightbulb as x,PenTool as f,MessageSquare as N,ToggleLeft as v,CheckCircle2 as w,BookOpen as y}from"lucide-react";import{WriteCorrectVerbFormClient as C}from"./WriteCorrectVerbFormClient";import{useDebouncedCallback as H}from"../../../../shared/lib/hooks";function S({initialData:S,onSave:V,onCancel:A,onChange:j,externalErrors:k,onUnsavedChangesChange:_,validationRef:J}){const[D,F]=t(S?.sentence||""),[O,G]=t(S?.verbHint||""),[W,z]=t(S?.correctAnswer||""),[P,E]=t(S?.caseSensitive||!1),[M,R]=t(S?.instruction||"Write the correct form of the verb in parentheses to complete each sentence. Pay attention to the tense."),[X,q]=t(S?.explanation||""),[B,K]=t(S?.points||1),[L,Q]=t(!1),[T,U]=t({}),I=n(S),Y=n(!1),Z=n(j),$=n("");a(()=>{Z.current=j},[j]);const ee=H(e=>{Z.current?.(e)},300);a(()=>{JSON.stringify(I.current)!==JSON.stringify(S)&&(Y.current=!0,I.current=S,S&&(S.sentence&&F(S.sentence),S.verbHint&&G(S.verbHint),S.correctAnswer&&z(S.correctAnswer),void 0!==S.caseSensitive&&E(S.caseSensitive),S.instruction&&R(S.instruction),S.explanation&&q(S.explanation),S.points&&K(S.points)),setTimeout(()=>{Y.current=!1},150))},[S]),a(()=>{if(Y.current){const e=JSON.stringify({sentence:D,verbHint:O,correctAnswer:W,caseSensitive:P,instruction:M,explanation:X,points:B});return void($.current=e)}const e={sentence:D,verbHint:O,correctAnswer:W,caseSensitive:P,instruction:M,explanation:X,points:B},r=JSON.stringify(e);$.current!==r&&($.current=r,ee(e))},[D,O,W,P,M,X,B]),a(()=>{J&&(J.current={triggerValidation:async()=>re(),getFormData:()=>({sentence:D,verbHint:O,correctAnswer:W,caseSensitive:P,instruction:M,explanation:X,points:B})})},[J,D,O,W,P,M,X,B]);const re=()=>{const e={};return D.trim()?D.includes("{0}")||(e.sentence="Câu cần có chỗ trống {0}!"):e.sentence="Vui lòng nhập câu hỏi!",O.trim()||(e.verbHint="Vui lòng nhập gợi ý động từ!"),W.trim()||(e.correctAnswer="Vui lòng nhập đáp án đúng!"),U(e),0===Object.keys(e).length};if(L){const t={sentence:D,verbHint:O,correctAnswer:W,caseSensitive:P,instruction:M};return r("div",{className:"space-y-6",children:[e(d,{children:e(h,{children:r("div",{className:"flex items-center justify-between",children:[e(g,{children:"Write the Correct Form of the Verb (Preview)"}),r(s,{variant:"outline",size:"sm",onClick:()=>Q(!1),children:[e(u,{className:"mr-2 h-4 w-4"}),"Quay lại Edit Mode"]})]})})}),e(C,{questionData:{...t,explanation:X},isReviewMode:!0,userAnswer:W})]})}return r("div",{className:"space-y-6",children:[r(d,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50",children:[e("div",{className:"h-1.5 bg-gradient-to-r from-orange-500 via-amber-500 to-yellow-500"}),e(h,{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 bg-gradient-to-br from-orange-500 to-amber-600 shadow-md",children:e(f,{className:"h-5 w-5 text-white"})}),r("div",{children:[e(g,{className:"text-xl font-bold text-gray-900",children:"Viết dạng đúng của động từ"}),e("p",{className:"text-sm text-gray-500",children:"Chia động từ trong ngoặc đúng thì"})]})]}),r(s,{variant:"outline",size:"sm",onClick:()=>Q(!0),disabled:!(D.trim()&&O.trim()&&W.trim()&&D.includes("{0}")),className:"gap-2 border-amber-200 text-amber-600 hover:bg-amber-50 hover:text-amber-700",children:[e(p,{className:"h-4 w-4"}),"Xem trước"]})]})}),r(m,{className:"space-y-6",children:[e("div",{className:"rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4",children:e(o,{defaultValue:B,onChange:e=>K(Number(e.target.value)||1)})}),e("div",{className:"rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-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-amber-100",children:e(x,{className:"h-4 w-4 text-amber-600"})}),r("div",{className:"text-sm text-amber-900",children:[e("p",{className:"font-semibold text-amber-800",children:"Hướng dẫn tạo câu hỏi"}),r("ul",{className:"mt-2 space-y-1.5 text-amber-700",children:[r("li",{className:"flex items-center gap-2",children:[e("span",{className:"h-1.5 w-1.5 rounded-full bg-amber-400"}),"Sử dụng ",e("code",{className:"mx-1 rounded bg-amber-100 px-1.5 py-0.5 font-mono text-amber-800",children:"{0}"})," để đánh dấu chỗ trống trong câu"]}),r("li",{className:"flex items-center gap-2",children:[e("span",{className:"h-1.5 w-1.5 rounded-full bg-amber-400"}),'Nhập động từ gợi ý (dạng nguyên mẫu) vào ô "Gợi ý động từ"']}),r("li",{className:"flex items-center gap-2",children:[e("span",{className:"h-1.5 w-1.5 rounded-full bg-amber-400"}),"Học sinh sẽ viết dạng đúng của động từ để hoàn thành câu"]}),r("li",{className:"flex items-center gap-2",children:[e("span",{className:"h-1.5 w-1.5 rounded-full bg-amber-400"}),"Ví dụ: ",r("code",{className:"ml-1 rounded bg-amber-100 px-1.5 py-0.5 font-mono text-amber-800 select-all cursor-pointer",title:"Click để copy",children:["John ","{0}"," his homework at night."]})]})]})]})]})}),r("div",{className:"space-y-2",children:[r(i,{className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(N,{className:"h-4 w-4 text-amber-500"}),"Câu hỏi ",e("span",{className:"text-red-500",children:"*"})]}),e(c,{value:D,onChange:e=>{F(e.target.value),T.sentence&&U(e=>({...e,sentence:void 0}))},placeholder:"Ví dụ: John {0} his homework at night.",className:"border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 "+(T.sentence?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),T.sentence&&e("p",{className:"text-sm text-red-600",children:T.sentence})]}),r("div",{className:"grid grid-cols-2 gap-4",children:[r("div",{className:"rounded-xl border-2 p-4 transition-all "+(O?"border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50":"border-gray-200 bg-white"),children:[r(i,{className:"flex items-center gap-2 text-sm font-semibold text-gray-700",children:[e("span",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-amber-100 text-xs font-bold text-amber-600",children:"V"}),"Gợi ý động từ ",e("span",{className:"text-red-500",children:"*"})]}),e(c,{value:O,onChange:e=>{G(e.target.value),T.verbHint&&U(e=>({...e,verbHint:void 0}))},placeholder:"Ví dụ: do",className:"mt-2 border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 "+(T.verbHint?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),T.verbHint&&e("p",{className:"mt-1 text-sm text-red-600",children:T.verbHint})]}),r("div",{className:"rounded-xl border-2 p-4 transition-all "+(W?"border-green-200 bg-gradient-to-r from-green-50 to-emerald-50":"border-gray-200 bg-white"),children:[r(i,{className:"flex items-center gap-2 text-sm font-semibold text-gray-700",children:[e(w,{className:"h-4 w-4 text-green-600"}),"Đáp án đúng ",e("span",{className:"text-red-500",children:"*"})]}),e(c,{value:W,onChange:e=>{z(e.target.value),T.correctAnswer&&U(e=>({...e,correctAnswer:void 0}))},placeholder:"Ví dụ: does",className:"mt-2 border-gray-200 bg-white transition-all focus:border-green-300 focus:ring-green-200 "+(T.correctAnswer?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),T.correctAnswer&&e("p",{className:"mt-1 text-sm text-red-600",children:T.correctAnswer})]})]}),r("div",{className:"flex items-center justify-between rounded-xl border-2 border-gray-200 bg-white p-4 transition-all hover:border-gray-300",children:[r("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gray-100",children:e(v,{className:"h-4 w-4 text-gray-600"})}),r("div",{className:"space-y-0.5",children:[e(i,{htmlFor:"caseSensitive",className:"font-semibold text-gray-800",children:"Phân biệt chữ hoa/thường"}),e("p",{className:"text-sm text-gray-500",children:'Bật nếu đáp án cần khớp chính xác (ví dụ: "Does" ≠ "does")'})]})]}),e(b,{id:"caseSensitive",checked:P,onCheckedChange:E})]}),D&&O&&r("div",{className:"rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4",children:[r("div",{className:"mb-2 flex items-center gap-2 text-xs font-semibold text-gray-600",children:[e(p,{className:"h-3.5 w-3.5"}),"Xem trước câu hỏi"]}),r("p",{className:"text-sm text-gray-700",children:[D.replace("{0}","_______")," ",r("span",{className:"font-semibold text-amber-600",children:["(",O,")"]})]}),W&&r("div",{className:"mt-2 flex items-center gap-2 rounded-lg bg-green-50 px-3 py-2",children:[e(w,{className:"h-4 w-4 text-green-500"}),r("span",{className:"text-sm text-green-700",children:["Đáp án: ",e("strong",{children:D.replace("{0}",W)})]})]})]}),k&&k.length>0&&r("div",{className:"flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4",children:[e("div",{className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100",children:e("span",{className:"text-lg",children:"❌"})}),r("div",{className:"text-sm",children:[e("p",{className:"font-semibold text-red-800",children:"Lỗi:"}),e("ul",{className:"mt-1 list-inside list-disc text-red-700",children:k.map((r,t)=>e("li",{children:r},t))})]})]})]})]}),r(d,{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(h,{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(y,{className:"h-4 w-4 text-white"})}),r("div",{children:[r("div",{className:"flex items-center gap-2",children:[e(g,{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(m,{children:e(l,{id:"explanation",value:X,onChange:e=>q(e.target.value),placeholder:"VD: Câu trả lời đúng là... vì...",rows:3,className:"min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none"})})]})]})}export function WriteCorrectVerbFormCreator(r){return e(S,{...r})}
@@ -1,2 +1 @@
1
- export * from './WriteCorrectVerbFormCreator';
2
- export * from './WriteCorrectVerbFormClient';
1
+ export*from"./WriteCorrectVerbFormCreator";export*from"./WriteCorrectVerbFormClient";
@@ -1,62 +1 @@
1
- import { WriteCorrectVerbFormCreator } from './WriteCorrectVerbFormCreator';
2
- export const writeCorrectVerbFormRegistration = {
3
- component: WriteCorrectVerbFormCreator,
4
- transformInitialData: (initialData) => {
5
- const content = initialData?.content;
6
- const answer = initialData?.answer;
7
- const points = initialData?.total_points
8
- ? parseFloat(initialData.total_points)
9
- : (initialData?.points || answer?.points || 1);
10
- let creatorData = answer;
11
- if (content && (content.sentence !== undefined || content.verbHint !== undefined)) {
12
- creatorData = {
13
- sentence: content.sentence || '',
14
- verbHint: content.verbHint || '',
15
- correctAnswer: answer?.correctAnswer || content.correctAnswer || '',
16
- caseSensitive: content.caseSensitive ?? answer?.caseSensitive ?? false,
17
- instruction: content.instruction || answer?.instruction || '',
18
- explanation: initialData?.explanation || content.explanation || '',
19
- points,
20
- };
21
- }
22
- return creatorData;
23
- },
24
- getExtraProps: (ctx) => ({
25
- onUnsavedChangesChange: ctx.onUnsavedChangesChange,
26
- validationRef: ctx.validationRef,
27
- }),
28
- wrapOnSave: (data, ctx) => ({
29
- type: ctx.questionType,
30
- points: data.points ?? ctx.state.points ?? 1,
31
- level: ctx.state.level,
32
- difficulty: ctx.state.difficulty,
33
- skill: ctx.state.skill,
34
- explanation: data.explanation || '',
35
- content: {
36
- sentence: data.sentence,
37
- verbHint: data.verbHint,
38
- instruction: data.instruction,
39
- },
40
- answer: {
41
- correctAnswer: data.correctAnswer,
42
- caseSensitive: data.caseSensitive,
43
- },
44
- }),
45
- wrapOnChange: (data, ctx) => ({
46
- type: ctx.questionType,
47
- points: data.points ?? ctx.state.points ?? 1,
48
- level: ctx.state.level,
49
- difficulty: ctx.state.difficulty,
50
- skill: ctx.state.skill,
51
- explanation: data.explanation || '',
52
- content: {
53
- sentence: data.sentence,
54
- verbHint: data.verbHint,
55
- instruction: data.instruction,
56
- },
57
- answer: {
58
- correctAnswer: data.correctAnswer,
59
- caseSensitive: data.caseSensitive,
60
- },
61
- }),
62
- };
1
+ import{WriteCorrectVerbFormCreator as e}from"./WriteCorrectVerbFormCreator";export const writeCorrectVerbFormRegistration={component:e,transformInitialData:e=>{const t=e?.content,n=e?.answer,i=e?.total_points?parseFloat(e.total_points):e?.points||n?.points||1;let s=n;return!t||void 0===t.sentence&&void 0===t.verbHint||(s={sentence:t.sentence||"",verbHint:t.verbHint||"",correctAnswer:n?.correctAnswer||t.correctAnswer||"",caseSensitive:t.caseSensitive??n?.caseSensitive??!1,instruction:t.instruction||n?.instruction||"",explanation:e?.explanation||t.explanation||"",points:i}),s},getExtraProps:e=>({onUnsavedChangesChange:e.onUnsavedChangesChange,validationRef:e.validationRef}),wrapOnSave:(e,t)=>({type:t.questionType,points:e.points??t.state.points??1,level:t.state.level,difficulty:t.state.difficulty,skill:t.state.skill,explanation:e.explanation||"",content:{sentence:e.sentence,verbHint:e.verbHint,instruction:e.instruction},answer:{correctAnswer:e.correctAnswer,caseSensitive:e.caseSensitive}}),wrapOnChange:(e,t)=>({type:t.questionType,points:e.points??t.state.points??1,level:t.state.level,difficulty:t.state.difficulty,skill:t.state.skill,explanation:e.explanation||"",content:{sentence:e.sentence,verbHint:e.verbHint,instruction:e.instruction},answer:{correctAnswer:e.correctAnswer,caseSensitive:e.caseSensitive}})};
@@ -1,17 +1 @@
1
- export const transformWriteCorrectVerbForm = (question) => {
2
- const contentData = question.content;
3
- const answerData = question.answer;
4
- if (!contentData?.sentence || !contentData?.verbHint) {
5
- return { apiContent: {}, apiCorrectAnswer: {} };
6
- }
7
- return {
8
- apiContent: {
9
- sentence: contentData.sentence,
10
- verbHint: contentData.verbHint,
11
- },
12
- apiCorrectAnswer: {
13
- correctAnswer: answerData?.correctAnswer || '',
14
- caseSensitive: answerData?.caseSensitive || false,
15
- },
16
- };
17
- };
1
+ export const transformWriteCorrectVerbForm=e=>{const n=e.content,r=e.answer;return n?.sentence&&n?.verbHint?{apiContent:{sentence:n.sentence,verbHint:n.verbHint},apiCorrectAnswer:{correctAnswer:r?.correctAnswer||"",caseSensitive:r?.caseSensitive||!1}}:{apiContent:{},apiCorrectAnswer:{}}};
@@ -1,22 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useEffect } from 'react';
4
- import { Input } from '../../../../components/ui/input';
5
- import { cn } from '../../../../shared/lib/utils';
6
- import { PenLine, Lightbulb } from 'lucide-react';
7
- export function WriteSentencesClient({ questionData, onSubmit, isReviewMode = false, userAnswer, explanation, }) {
8
- const [answer, setAnswer] = useState(typeof userAnswer === 'string' ? userAnswer : '');
9
- useEffect(() => {
10
- if (userAnswer !== undefined && typeof userAnswer === 'string') {
11
- setAnswer(userAnswer);
12
- }
13
- }, [userAnswer]);
14
- const handleAnswerChange = (value) => {
15
- if (isReviewMode)
16
- return;
17
- setAnswer(value);
18
- onSubmit?.(value);
19
- };
20
- const hasAnswered = answer.trim().length > 0;
21
- return (_jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: cn("overflow-hidden rounded-xl border bg-white shadow-sm transition-all relative", "border-gray-200"), children: _jsxs("div", { className: "p-4 sm:p-5 flex items-start gap-3", children: [_jsx("div", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-teal-500 to-emerald-600 text-white shadow-sm pointer-events-none mt-1", children: _jsx(PenLine, { className: "h-5 w-5" }) }), _jsx("div", { className: "flex-1 space-y-2", children: !isReviewMode ? (_jsx(Input, { value: answer, onChange: (e) => handleAnswerChange(e.target.value), placeholder: "B\u1EAFt \u0111\u1EA7u vi\u1EBFt c\u00E2u c\u1EE7a b\u1EA1n t\u1EA1i \u0111\u00E2y...", className: "w-full h-12 text-base font-medium px-4 placeholder:font-normal bg-gray-50 hover:bg-white focus:bg-white transition-colors", disabled: isReviewMode })) : (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("span", { className: "text-xs font-semibold text-gray-500 uppercase tracking-wider", children: "B\u00E0i vi\u1EBFt \u0111\u00E3 n\u1ED9p" }), _jsx("div", { className: "p-3 rounded-lg border leading-relaxed break-words bg-gray-50 border-gray-100 text-gray-800", children: answer || _jsx("span", { className: "text-gray-400 italic", children: "Kh\u00F4ng c\u00F3 tr\u1EA3 l\u1EDDi" }) })] })) })] }) }), isReviewMode && explanation && (_jsx("div", { className: "rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-4 shadow-sm", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 text-blue-700", children: _jsx(Lightbulb, { className: "h-4 w-4" }) }), _jsxs("div", { className: "flex-1 mt-0.5", children: [_jsx("h4", { className: "text-xs font-bold text-blue-900 uppercase tracking-wider mb-1.5", children: "Gi\u1EA3i th\u00EDch" }), _jsx("p", { className: "text-sm text-blue-800 leading-relaxed whitespace-pre-wrap", children: explanation })] })] }) }))] }));
22
- }
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{useState as t,useEffect as a}from"react";import{Input as i}from"../../../../components/ui/input";import{cn as s}from"../../../../shared/lib/utils";import{PenLine as l,Lightbulb as n}from"lucide-react";export function WriteSentencesClient({questionData:d,onSubmit:o,isReviewMode:c=!1,userAnswer:m,explanation:h}){const[u,p]=t("string"==typeof m?m:"");a(()=>{void 0!==m&&"string"==typeof m&&p(m)},[m]);u.trim().length;return r("div",{className:"space-y-4",children:[e("div",{className:s("overflow-hidden rounded-xl border bg-white shadow-sm transition-all relative","border-gray-200"),children:r("div",{className:"p-4 sm:p-5 flex items-start gap-3",children:[e("div",{className:"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-teal-500 to-emerald-600 text-white shadow-sm pointer-events-none mt-1",children:e(l,{className:"h-5 w-5"})}),e("div",{className:"flex-1 space-y-2",children:c?r("div",{className:"flex flex-col gap-2",children:[e("span",{className:"text-xs font-semibold text-gray-500 uppercase tracking-wider",children:"Bài viết đã nộp"}),e("div",{className:"p-3 rounded-lg border leading-relaxed break-words bg-gray-50 border-gray-100 text-gray-800",children:u||e("span",{className:"text-gray-400 italic",children:"Không có trả lời"})})]}):e(i,{value:u,onChange:e=>{return r=e.target.value,void(c||(p(r),o?.(r)));var r},placeholder:"Bắt đầu viết câu của bạn tại đây...",className:"w-full h-12 text-base font-medium px-4 placeholder:font-normal bg-gray-50 hover:bg-white focus:bg-white transition-colors",disabled:c})})]})}),c&&h&&e("div",{className:"rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-4 shadow-sm",children:r("div",{className:"flex items-start gap-3",children:[e("div",{className:"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 text-blue-700",children:e(n,{className:"h-4 w-4"})}),r("div",{className:"flex-1 mt-0.5",children:[e("h4",{className:"text-xs font-bold text-blue-900 uppercase tracking-wider mb-1.5",children:"Giải thích"}),e("p",{className:"text-sm text-blue-800 leading-relaxed whitespace-pre-wrap",children:h})]})]})})]})}
@@ -1,174 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, useRef, useEffect } from 'react';
4
- import { Button } from '../../../../components/ui/button';
5
- import { Input } from '../../../../components/ui/input';
6
- import { Label } from '../../../../components/ui/label';
7
- import { Textarea } from '../../../../components/ui/textarea';
8
- import { PointsInput } from '../../../../components/ui/points-input';
9
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
10
- import { Pencil, Eye, PenLine, BookOpen, Hash } from 'lucide-react';
11
- import { WriteSentencesClient } from './WriteSentencesClient';
12
- import { useDebouncedCallback } from '../../../../shared/lib/hooks';
13
- import { BasicQuestionGroupCard } from '../../groups/BasicQuestionGroupCard';
14
- import { useBasicQuestionGroup } from '../../_shared/hooks/useBasicQuestionGroup';
15
- function WriteSentencesCreatorContent({ initialData, onChange, externalErrors, validationRef, groups, questionIndexInPart, partId, }) {
16
- // ============ GROUP (shared title / content / image) ============
17
- const { isGrouped, currentGroupNumber, groupTitle, groupSubtitle, groupContent, imageUrl, setGroupTitle, setGroupSubtitle, setGroupContent, setImageUrl, } = useBasicQuestionGroup({
18
- partId,
19
- groups,
20
- questionIndexInPart,
21
- initialData: {
22
- title: (() => {
23
- const qgPayload = initialData?.questionGroup?.payload;
24
- return qgPayload?.title || initialData?.title || '';
25
- })(),
26
- subtitle: (() => {
27
- const qgPayload = initialData?.questionGroup?.payload;
28
- return qgPayload?.subtitle || '';
29
- })(),
30
- content: (() => {
31
- const qgPayload = initialData?.questionGroup?.payload;
32
- return qgPayload?.content || initialData?.groupContent || '';
33
- })(),
34
- imageUrl: (() => {
35
- const qgPayload = initialData?.questionGroup?.payload;
36
- return qgPayload?.imageUrl || initialData?.imageUrl || '';
37
- })(),
38
- },
39
- });
40
- // ============ STATE ============
41
- const [explanation, setExplanation] = useState(initialData?.explanation || '');
42
- const [points, setPoints] = useState(initialData?.points || 1);
43
- const [instruction, setInstruction] = useState(initialData?.instruction || '');
44
- const [sentenceCount, setSentenceCount] = useState(initialData?.sentenceCount);
45
- // UI State
46
- const [isClientMode, setIsClientMode] = useState(false);
47
- // Refs for initialization tracking
48
- const previousInitialDataRef = useRef(initialData);
49
- const isInitializingRef = useRef(false);
50
- const hasInitializedRef = useRef(false);
51
- const onChangeRef = useRef(onChange);
52
- const previousPayloadRef = useRef('');
53
- const [validationErrors, setValidationErrors] = useState({});
54
- useEffect(() => {
55
- onChangeRef.current = onChange;
56
- }, [onChange]);
57
- const debouncedOnChange = useDebouncedCallback((data) => {
58
- onChangeRef.current?.(data);
59
- }, 300);
60
- // Expose validation function via ref
61
- useEffect(() => {
62
- if (validationRef) {
63
- validationRef.current = {
64
- triggerValidation: async () => {
65
- const errors = {};
66
- if (!sentenceCount || sentenceCount < 1) {
67
- errors.sentenceCount = 'Vui lòng nhập số câu cần viết';
68
- }
69
- setValidationErrors(errors);
70
- return Object.keys(errors).length === 0;
71
- },
72
- getFormData: () => ({
73
- points,
74
- explanation,
75
- instruction,
76
- sentenceCount,
77
- title: groupTitle,
78
- subtitle: groupSubtitle,
79
- groupContent,
80
- imageUrl,
81
- groupNumber: currentGroupNumber,
82
- groups,
83
- }),
84
- };
85
- }
86
- }, [validationRef, points, explanation, instruction, sentenceCount, groupTitle, groupSubtitle, groupContent, imageUrl, currentGroupNumber, groups]);
87
- // Sync from initialData
88
- useEffect(() => {
89
- const initialDataChanged = JSON.stringify(previousInitialDataRef.current) !== JSON.stringify(initialData);
90
- if (initialDataChanged || !hasInitializedRef.current) {
91
- isInitializingRef.current = true;
92
- previousInitialDataRef.current = initialData;
93
- hasInitializedRef.current = true;
94
- if (initialData) {
95
- setExplanation(initialData.explanation || '');
96
- setPoints(initialData.points || 1);
97
- setInstruction(initialData.instruction || '');
98
- setSentenceCount(initialData.sentenceCount);
99
- }
100
- else {
101
- setExplanation('');
102
- setPoints(1);
103
- setInstruction('');
104
- setSentenceCount(undefined);
105
- }
106
- setTimeout(() => {
107
- isInitializingRef.current = false;
108
- }, 150);
109
- }
110
- }, [initialData]);
111
- // Call onChange when data changes
112
- useEffect(() => {
113
- if (isInitializingRef.current) {
114
- const currentPayload = JSON.stringify({
115
- points,
116
- explanation,
117
- instruction,
118
- sentenceCount,
119
- title: groupTitle,
120
- subtitle: groupSubtitle,
121
- groupContent,
122
- imageUrl,
123
- groupNumber: currentGroupNumber,
124
- groups,
125
- });
126
- previousPayloadRef.current = currentPayload;
127
- return;
128
- }
129
- const payload = {
130
- points,
131
- explanation,
132
- instruction,
133
- sentenceCount,
134
- title: groupTitle,
135
- subtitle: groupSubtitle,
136
- groupContent,
137
- imageUrl,
138
- groupNumber: currentGroupNumber,
139
- groups,
140
- };
141
- const payloadString = JSON.stringify(payload);
142
- if (previousPayloadRef.current !== payloadString) {
143
- previousPayloadRef.current = payloadString;
144
- debouncedOnChange(payload);
145
- }
146
- }, [points, explanation, instruction, sentenceCount, groupTitle, groupSubtitle, groupContent, imageUrl, currentGroupNumber, groups, debouncedOnChange]);
147
- // Client mode preview
148
- if (isClientMode) {
149
- const questionData = {
150
- points,
151
- explanation,
152
- instruction,
153
- sentenceCount,
154
- };
155
- 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: "Write Sentences (Preview)" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Ch\u1EBF \u0111\u1ED9 xem tr\u01B0\u1EDBc" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(false), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), "Quay l\u1EA1i Edit Mode"] })] }) }) }), _jsx(WriteSentencesClient, { questionData: questionData, onSubmit: () => { }, isReviewMode: true, explanation: explanation })] }));
156
- }
157
- // Creator UI
158
- return (_jsxs("div", { className: "space-y-6", children: [isGrouped && (_jsx(BasicQuestionGroupCard, { groupNumber: currentGroupNumber, title: groupTitle, subtitle: groupSubtitle, content: groupContent, imageUrl: imageUrl, onTitleChange: setGroupTitle, onSubtitleChange: setGroupSubtitle, onContentChange: setGroupContent, onImageChange: setImageUrl, uploadIdPrefix: `write-sentences-${partId ?? 'group'}` })), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50", children: [_jsx("div", { className: "h-1.5 bg-gradient-to-r from-teal-400 to-emerald-500" }), _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 bg-gradient-to-br from-teal-500 to-emerald-600 shadow-md", children: _jsx(PenLine, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold text-gray-900", children: "Vi\u1EBFt c\u00E2u" }), _jsx("p", { className: "text-sm text-gray-500", children: "H\u1ECDc sinh t\u1EF1 vi\u1EBFt m\u1ED9t c\u00E2u ho\u00E0n ch\u1EC9nh" })] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), className: "gap-2 border-emerald-200 text-emerald-600 hover:bg-emerald-50 hover:text-emerald-700", children: [_jsx(Eye, { className: "h-4 w-4" }), "Xem tr\u01B0\u1EDBc"] })] }) }), _jsxs(CardContent, { className: "space-y-6", children: [_jsxs("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2", children: [_jsx("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: _jsx(PointsInput, { defaultValue: points, onChange: (e) => setPoints(Number(e.target.value) || 1) }) }), _jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: [_jsxs(Label, { htmlFor: "sentenceCount", className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(Hash, { className: "h-4 w-4 text-teal-500" }), "S\u1ED1 c\u00E2u c\u1EA7n vi\u1EBFt ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "sentenceCount", type: "number", min: 1, max: 10, value: sentenceCount ?? '', onChange: (e) => {
159
- const raw = e.target.value;
160
- if (raw === '') {
161
- setSentenceCount(undefined);
162
- }
163
- else {
164
- const val = parseInt(raw, 10);
165
- setSentenceCount(isNaN(val) ? undefined : Math.max(1, val));
166
- }
167
- if (validationErrors.sentenceCount) {
168
- setValidationErrors((prev) => ({ ...prev, sentenceCount: undefined }));
169
- }
170
- }, placeholder: "Nh\u1EADp s\u1ED1 c\u00E2u...", className: `mt-1.5 border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200 ${validationErrors.sentenceCount ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.sentenceCount && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: validationErrors.sentenceCount }))] })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "instruction", className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(BookOpen, { className: "h-4 w-4 text-teal-500" }), "H\u01B0\u1EDBng d\u1EABn ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(T\u00F9y ch\u1ECDn)" })] }), _jsx(Textarea, { id: "instruction", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: "V\u00ED d\u1EE5: Now write two sentences about the picture.", rows: 2, className: "border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200" })] }), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100", children: _jsx("span", { className: "text-lg", children: "\u274C" }) }), _jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("ul", { className: "mt-1 list-inside list-disc text-red-700", children: externalErrors.map((error, index) => (_jsx("li", { children: error }, index))) })] })] }))] })] }), _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, { id: "explanation", value: explanation, onChange: (e) => setExplanation(e.target.value), placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0... v\u00EC...", rows: 3, className: "min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none" }) })] })] }));
171
- }
172
- export function WriteSentencesCreator(props) {
173
- return _jsx(WriteSentencesCreatorContent, { ...props });
174
- }
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useState as r,useRef as n,useEffect as i}from"react";import{Button as a}from"../../../../components/ui/button";import{Input as s}from"../../../../components/ui/input";import{Label as o}from"../../../../components/ui/label";import{Textarea as l}from"../../../../components/ui/textarea";import{PointsInput as c}from"../../../../components/ui/points-input";import{Card as d,CardContent as m,CardHeader as u,CardTitle as h}from"../../../../components/ui/card";import{Pencil as g,Eye as p,PenLine as b,BookOpen as x,Hash as f}from"lucide-react";import{WriteSentencesClient as N}from"./WriteSentencesClient";import{useDebouncedCallback as v}from"../../../../shared/lib/hooks";import{BasicQuestionGroupCard as y}from"../../groups/BasicQuestionGroupCard";import{useBasicQuestionGroup as C}from"../../_shared/hooks/useBasicQuestionGroup";function w({initialData:w,onChange:G,externalErrors:S,validationRef:I,groups:j,questionIndexInPart:U,partId:k}){const{isGrouped:q,currentGroupNumber:V,groupTitle:D,groupSubtitle:O,groupContent:T,imageUrl:J,setGroupTitle:P,setGroupSubtitle:z,setGroupContent:F,setImageUrl:M}=C({partId:k,groups:j,questionIndexInPart:U,initialData:{title:(()=>{const e=w?.questionGroup?.payload;return e?.title||w?.title||""})(),subtitle:(()=>{const e=w?.questionGroup?.payload;return e?.subtitle||""})(),content:(()=>{const e=w?.questionGroup?.payload;return e?.content||w?.groupContent||""})(),imageUrl:(()=>{const e=w?.questionGroup?.payload;return e?.imageUrl||w?.imageUrl||""})()}}),[Q,W]=r(w?.explanation||""),[B,E]=r(w?.points||1),[H,R]=r(w?.instruction||""),[K,L]=r(w?.sentenceCount),[X,$]=r(!1),_=n(w),A=n(!1),Y=n(!1),Z=n(G),ee=n(""),[te,re]=r({});i(()=>{Z.current=G},[G]);const ne=v(e=>{Z.current?.(e)},300);if(i(()=>{I&&(I.current={triggerValidation:async()=>{const e={};return(!K||K<1)&&(e.sentenceCount="Vui lòng nhập số câu cần viết"),re(e),0===Object.keys(e).length},getFormData:()=>({points:B,explanation:Q,instruction:H,sentenceCount:K,title:D,subtitle:O,groupContent:T,imageUrl:J,groupNumber:V,groups:j})})},[I,B,Q,H,K,D,O,T,J,V,j]),i(()=>{!(JSON.stringify(_.current)!==JSON.stringify(w))&&Y.current||(A.current=!0,_.current=w,Y.current=!0,w?(W(w.explanation||""),E(w.points||1),R(w.instruction||""),L(w.sentenceCount)):(W(""),E(1),R(""),L(void 0)),setTimeout(()=>{A.current=!1},150))},[w]),i(()=>{if(A.current){const e=JSON.stringify({points:B,explanation:Q,instruction:H,sentenceCount:K,title:D,subtitle:O,groupContent:T,imageUrl:J,groupNumber:V,groups:j});return void(ee.current=e)}const e={points:B,explanation:Q,instruction:H,sentenceCount:K,title:D,subtitle:O,groupContent:T,imageUrl:J,groupNumber:V,groups:j},t=JSON.stringify(e);ee.current!==t&&(ee.current=t,ne(e))},[B,Q,H,K,D,O,T,J,V,j,ne]),X){const r={points:B,explanation:Q,instruction:H,sentenceCount:K};return t("div",{className:"space-y-6",children:[e(d,{children:e(u,{children:t("div",{className:"flex items-center justify-between",children:[t("div",{children:[e(h,{children:"Write Sentences (Preview)"}),e("p",{className:"mt-1 text-sm text-gray-500",children:"Chế độ xem trước"})]}),t(a,{variant:"outline",size:"sm",onClick:()=>$(!1),children:[e(g,{className:"mr-2 h-4 w-4"}),"Quay lại Edit Mode"]})]})})}),e(N,{questionData:r,onSubmit:()=>{},isReviewMode:!0,explanation:Q})]})}return t("div",{className:"space-y-6",children:[q&&e(y,{groupNumber:V,title:D,subtitle:O,content:T,imageUrl:J,onTitleChange:P,onSubtitleChange:z,onContentChange:F,onImageChange:M,uploadIdPrefix:`write-sentences-${k??"group"}`}),t(d,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e("div",{className:"h-1.5 bg-gradient-to-r from-teal-400 to-emerald-500"}),e(u,{className:"pb-4",children:t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-teal-500 to-emerald-600 shadow-md",children:e(b,{className:"h-5 w-5 text-white"})}),t("div",{children:[e(h,{className:"text-xl font-bold text-gray-900",children:"Viết câu"}),e("p",{className:"text-sm text-gray-500",children:"Học sinh tự viết một câu hoàn chỉnh"})]})]}),t(a,{variant:"outline",size:"sm",onClick:()=>$(!0),className:"gap-2 border-emerald-200 text-emerald-600 hover:bg-emerald-50 hover:text-emerald-700",children:[e(p,{className:"h-4 w-4"}),"Xem trước"]})]})}),t(m,{className:"space-y-6",children:[t("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2",children:[e("div",{className:"rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4",children:e(c,{defaultValue:B,onChange:e=>E(Number(e.target.value)||1)})}),t("div",{className:"rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4",children:[t(o,{htmlFor:"sentenceCount",className:"flex items-center gap-2 text-sm font-semibold text-gray-800",children:[e(f,{className:"h-4 w-4 text-teal-500"}),"Số câu cần viết ",e("span",{className:"text-red-500",children:"*"})]}),e(s,{id:"sentenceCount",type:"number",min:1,max:10,value:K??"",onChange:e=>{const t=e.target.value;if(""===t)L(void 0);else{const e=parseInt(t,10);L(isNaN(e)?void 0:Math.max(1,e))}te.sentenceCount&&re(e=>({...e,sentenceCount:void 0}))},placeholder:"Nhập số câu...",className:"mt-1.5 border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200 "+(te.sentenceCount?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),te.sentenceCount&&e("p",{className:"mt-1 text-sm text-red-600",children:te.sentenceCount})]})]}),t("div",{className:"space-y-2",children:[t(o,{htmlFor:"instruction",className:"flex items-center gap-2 text-sm font-semibold text-gray-800",children:[e(x,{className:"h-4 w-4 text-teal-500"}),"Hướng dẫn ",e("span",{className:"text-xs font-normal text-gray-500",children:"(Tùy chọn)"})]}),e(l,{id:"instruction",value:H,onChange:e=>R(e.target.value),placeholder:"Ví dụ: Now write two sentences about the picture.",rows:2,className:"border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200"})]}),S&&S.length>0&&t("div",{className:"flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4",children:[e("div",{className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100",children:e("span",{className:"text-lg",children:"❌"})}),t("div",{className:"text-sm",children:[e("p",{className:"font-semibold text-red-800",children:"Lỗi:"}),e("ul",{className:"mt-1 list-inside list-disc text-red-700",children:S.map((t,r)=>e("li",{children:t},r))})]})]})]})]}),t(d,{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:t("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(x,{className:"h-4 w-4 text-white"})}),t("div",{children:[t("div",{className:"flex items-center gap-2",children:[e(h,{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(m,{children:e(l,{id:"explanation",value:Q,onChange:e=>W(e.target.value),placeholder:"VD: Câu trả lời đúng là... vì...",rows:3,className:"min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none"})})]})]})}export function WriteSentencesCreator(t){return e(w,{...t})}
@@ -1,2 +1 @@
1
- export { WriteSentencesCreator } from './WriteSentencesCreator';
2
- export { WriteSentencesClient } from './WriteSentencesClient';
1
+ export{WriteSentencesCreator}from"./WriteSentencesCreator";export{WriteSentencesClient}from"./WriteSentencesClient";