@tinyweb_dev/oe-exam-sdk 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (514) hide show
  1. package/dist/api/exam-create.js +1 -179
  2. package/dist/api/exam-create.types.js +1 -1
  3. package/dist/api/exam-questions.d.ts +1 -1
  4. package/dist/api/exam-questions.js +1 -98
  5. package/dist/api/exam-questions.types.d.ts +45 -1
  6. package/dist/api/exam-questions.types.js +1 -1
  7. package/dist/api/exam-taking.js +1 -73
  8. package/dist/api/exam-taking.types.js +1 -1
  9. package/dist/api/index.js +1 -6
  10. package/dist/api/result-review.js +1 -50
  11. package/dist/api/result-review.types.js +1 -1
  12. package/dist/components/CueCardEditor.js +1 -58
  13. package/dist/components/common/ResolvedImage.js +1 -15
  14. package/dist/components/exams/CompletionDialog.d.ts +8 -0
  15. package/dist/components/exams/CompletionDialog.js +1 -0
  16. package/dist/components/exams/CreateExamInfoForm.d.ts +4 -1
  17. package/dist/components/exams/CreateExamInfoForm.js +1 -35
  18. package/dist/components/exams/CreateExamPageContainer.d.ts +3 -0
  19. package/dist/components/exams/CreateExamPageContainer.js +1 -103
  20. package/dist/components/exams/ExamCreator.d.ts +9 -3
  21. package/dist/components/exams/ExamCreator.js +1 -89
  22. package/dist/components/exams/ExamPartTabs.d.ts +1 -1
  23. package/dist/components/exams/ExamPartTabs.js +1 -40
  24. package/dist/components/exams/ExamPreviewDialog.d.ts +20 -0
  25. package/dist/components/exams/ExamPreviewDialog.js +1 -0
  26. package/dist/components/exams/ExamQuestionGrid.d.ts +1 -1
  27. package/dist/components/exams/ExamQuestionGrid.js +1 -18
  28. package/dist/components/exams/ExamQuestionsPageContainer.d.ts +3 -0
  29. package/dist/components/exams/ExamQuestionsPageContainer.js +1 -90
  30. package/dist/components/exams/ExamViewDialog.d.ts +24 -0
  31. package/dist/components/exams/ExamViewDialog.js +1 -0
  32. package/dist/components/exams/PartAudioUploader.js +1 -9
  33. package/dist/components/exams/TemplateSelector.js +1 -66
  34. package/dist/components/exams/UnsavedChangesDialog.d.ts +8 -0
  35. package/dist/components/exams/UnsavedChangesDialog.js +1 -0
  36. package/dist/components/exams/api.d.ts +1 -1
  37. package/dist/components/exams/api.js +1 -3
  38. package/dist/components/exams/create-exam.texts.d.ts +58 -0
  39. package/dist/components/exams/create-exam.texts.js +1 -0
  40. package/dist/components/exams/create-exam.validation.js +1 -18
  41. package/dist/components/exams/exam-question-status.d.ts +9 -0
  42. package/dist/components/exams/exam-question-status.js +1 -0
  43. package/dist/components/exams/exam-template.utils.js +1 -18
  44. package/dist/components/exams/index.d.ts +11 -1
  45. package/dist/components/exams/index.js +1 -10
  46. package/dist/components/exams/take/ExamTakingPageContainer.js +1 -153
  47. package/dist/components/exams/take/GenericQuestionRenderer.js +1 -14
  48. package/dist/components/exams/take/exam-taking.utils.js +1 -88
  49. package/dist/components/exams/take/hooks/useSpeakingRecorder.js +1 -24
  50. package/dist/components/exams/take/index.js +1 -2
  51. package/dist/components/exams/take/types.js +1 -1
  52. package/dist/components/index.js +1 -42
  53. package/dist/components/questions/_shared/config/question-types.config.js +1 -333
  54. package/dist/components/questions/_shared/constants/color-palette.js +1 -21
  55. package/dist/components/questions/_shared/hooks/useAnswerGroupQuestionGroup.js +1 -105
  56. package/dist/components/questions/_shared/hooks/useBasicQuestionGroup.js +1 -222
  57. package/dist/components/questions/_shared/hooks/useCompareImagesGroup.js +1 -140
  58. package/dist/components/questions/_shared/hooks/useImageQuestionGroup.js +1 -123
  59. package/dist/components/questions/_shared/hooks/useInfoExchangeGroup.js +1 -50
  60. package/dist/components/questions/_shared/hooks/useQuestionFormState.js +1 -54
  61. package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +1 -123
  62. package/dist/components/questions/_shared/hooks/useQuestionListGroup.js +1 -121
  63. package/dist/components/questions/_shared/hooks/useQuestionOnChange.js +1 -70
  64. package/dist/components/questions/_shared/hooks/useSceneQuestionGroup.js +1 -194
  65. package/dist/components/questions/_shared/index.js +1 -7
  66. package/dist/components/questions/_shared/types/answer-the-question.type.js +1 -1
  67. package/dist/components/questions/_shared/types/arrange-letters-into-words.type.js +1 -1
  68. package/dist/components/questions/_shared/types/choose-correct-adjective.type.js +1 -3
  69. package/dist/components/questions/_shared/types/color-region.config.js +1 -1
  70. package/dist/components/questions/_shared/types/fill-in-blank.type.js +1 -1
  71. package/dist/components/questions/_shared/types/fill-missing-words-in-grid.type.js +1 -1
  72. package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +1 -1
  73. package/dist/components/questions/_shared/types/index.js +1 -12
  74. package/dist/components/questions/_shared/types/label-the-picture.type.js +1 -1
  75. package/dist/components/questions/_shared/types/listen-and-choose-from-answer-group.type.js +1 -1
  76. package/dist/components/questions/_shared/types/listen-and-choose-objects-in-scene.type.js +1 -1
  77. package/dist/components/questions/_shared/types/listen-and-drag-objects-into-scene.type.js +1 -1
  78. package/dist/components/questions/_shared/types/listen-and-speak-answer.type.js +1 -1
  79. package/dist/components/questions/_shared/types/listen-and-speak-compare-images.type.js +1 -1
  80. package/dist/components/questions/_shared/types/listen-and-speak-image-group.type.js +1 -1
  81. package/dist/components/questions/_shared/types/listen-and-speak-info-exchange.type.js +1 -1
  82. package/dist/components/questions/_shared/types/listen-and-speak-odd-one-out.type.js +1 -3
  83. package/dist/components/questions/_shared/types/listen-and-speak-question-list.type.js +1 -1
  84. package/dist/components/questions/_shared/types/listen-and-speak-with-story-images.type.js +1 -3
  85. package/dist/components/questions/_shared/types/listen-and-tick-answer.type.js +1 -1
  86. package/dist/components/questions/_shared/types/listening-drawline.type.js +1 -1
  87. package/dist/components/questions/_shared/types/look-and-choose-answer.type.js +1 -1
  88. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +1 -1
  89. package/dist/components/questions/_shared/types/match-by-writing-answer.type.js +1 -1
  90. package/dist/components/questions/_shared/types/multiple-choice.type.js +1 -1
  91. package/dist/components/questions/_shared/types/question-group.type.js +1 -11
  92. package/dist/components/questions/_shared/types/read-and-color-objects.type.js +1 -1
  93. package/dist/components/questions/_shared/types/read-displayed-content.type.js +1 -1
  94. package/dist/components/questions/_shared/types/read-passage-and-answer-questions.type.js +1 -1
  95. package/dist/components/questions/_shared/types/speaking-describe-image.type.js +1 -1
  96. package/dist/components/questions/_shared/types/spontaneous-qa.type.js +1 -1
  97. package/dist/components/questions/_shared/types/word-fill-paragraph-with-options.type.js +1 -1
  98. package/dist/components/questions/_shared/types/word-fill-paragraph.type.js +1 -1
  99. package/dist/components/questions/_shared/types/word-fill-structured-form.type.js +1 -1
  100. package/dist/components/questions/_shared/types/word-ordering.type.js +1 -1
  101. package/dist/components/questions/_shared/types/write-a-short-letter.type.js +1 -1
  102. package/dist/components/questions/_shared/types/write-correct-verb-form.type.js +1 -3
  103. package/dist/components/questions/_shared/types/write-sentences.type.js +1 -1
  104. package/dist/components/questions/_shared/types/write-short-paragraph.type.js +1 -1
  105. package/dist/components/questions/_shared/utils/answer-builder.js +1 -79
  106. package/dist/components/questions/_shared/utils/createColorRegionRegistration.js +1 -39
  107. package/dist/components/questions/_shared/utils/id-generator.js +1 -3
  108. package/dist/components/questions/_shared/utils/question-validation.js +1 -21
  109. package/dist/components/questions/components/ColorNode.js +1 -85
  110. package/dist/components/questions/components/ColorRegionClient.js +1 -545
  111. package/dist/components/questions/components/ColorRegionCreator.js +1 -420
  112. package/dist/components/questions/components/ColorRegionNode.js +1 -59
  113. package/dist/components/questions/components/CreatorGuide.js +1 -103
  114. package/dist/components/questions/components/NameNode.js +1 -26
  115. package/dist/components/questions/components/QuestionBankOption.js +1 -46
  116. package/dist/components/questions/components/QuestionBasicInfoForm.js +1 -9
  117. package/dist/components/questions/components/QuestionCommonFields.js +1 -33
  118. package/dist/components/questions/components/QuestionSearchDropdown.js +1 -258
  119. package/dist/components/questions/components/QuestionTypeSelector.js +1 -32
  120. package/dist/components/questions/components/QuestionTypeSpecificForms.js +1 -6
  121. package/dist/components/questions/components/RegionNode.js +1 -94
  122. package/dist/components/questions/components/index.js +1 -14
  123. package/dist/components/questions/creator/QuestionCreator.js +1 -90
  124. package/dist/components/questions/creator/QuestionGroupCreator.js +1 -119
  125. package/dist/components/questions/creator/dedicated-component-router.js +1 -79
  126. package/dist/components/questions/creator/index.js +1 -3
  127. package/dist/components/questions/creator/question-type-registry.js +1 -80
  128. package/dist/components/questions/groups/ArticlesGroupCard.js +1 -121
  129. package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -50
  130. package/dist/components/questions/groups/DocumentGroupCard.js +1 -114
  131. package/dist/components/questions/groups/MatchAnswerGroupCard.js +1 -25
  132. package/dist/components/questions/index.js +1 -10
  133. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +1 -37
  134. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +1 -351
  135. package/dist/components/questions/types/answer-the-question/register.js +1 -67
  136. package/dist/components/questions/types/answer-the-question/transform.js +1 -36
  137. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +1 -141
  138. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +1 -118
  139. package/dist/components/questions/types/arrange-letters-into-words/index.js +1 -2
  140. package/dist/components/questions/types/arrange-letters-into-words/register.js +1 -4
  141. package/dist/components/questions/types/arrange-letters-into-words/transform.js +1 -24
  142. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -64
  143. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +1 -169
  144. package/dist/components/questions/types/choose-correct-adjective/index.js +1 -2
  145. package/dist/components/questions/types/choose-correct-adjective/register.js +1 -55
  146. package/dist/components/questions/types/choose-correct-adjective/transform.js +1 -16
  147. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +1 -131
  148. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +1 -581
  149. package/dist/components/questions/types/choose-the-correct-answer/index.js +1 -2
  150. package/dist/components/questions/types/choose-the-correct-answer/register.js +1 -101
  151. package/dist/components/questions/types/choose-the-correct-answer/transform.js +1 -43
  152. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +1 -275
  153. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +1 -448
  154. package/dist/components/questions/types/fill-in-blank/index.js +1 -2
  155. package/dist/components/questions/types/fill-in-blank/register.js +1 -138
  156. package/dist/components/questions/types/fill-in-blank/transform.js +1 -59
  157. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +1 -117
  158. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +1 -671
  159. package/dist/components/questions/types/fill-missing-words-in-grid/index.js +1 -2
  160. package/dist/components/questions/types/fill-missing-words-in-grid/register.js +1 -37
  161. package/dist/components/questions/types/fill-missing-words-in-grid/transform.js +1 -15
  162. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +1 -262
  163. package/dist/components/questions/types/gn-speaking-interview/register.js +1 -121
  164. package/dist/components/questions/types/gn-speaking-interview/transform.js +1 -48
  165. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingClient.js +1 -16
  166. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingCreator.js +1 -27
  167. package/dist/components/questions/types/image-object-matching/index.js +1 -2
  168. package/dist/components/questions/types/image-object-matching/register.js +1 -3
  169. package/dist/components/questions/types/image-object-matching/transform.js +1 -2
  170. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +1 -102
  171. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +1 -344
  172. package/dist/components/questions/types/label-the-picture/index.js +1 -2
  173. package/dist/components/questions/types/label-the-picture/register.js +1 -54
  174. package/dist/components/questions/types/label-the-picture/transform.js +1 -28
  175. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +1 -78
  176. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +1 -232
  177. package/dist/components/questions/types/listen-and-choose-from-answer-group/index.js +1 -2
  178. package/dist/components/questions/types/listen-and-choose-from-answer-group/register.js +1 -82
  179. package/dist/components/questions/types/listen-and-choose-from-answer-group/transform.js +1 -27
  180. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +1 -70
  181. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +1 -386
  182. package/dist/components/questions/types/listen-and-choose-objects-in-scene/index.js +1 -2
  183. package/dist/components/questions/types/listen-and-choose-objects-in-scene/register.js +1 -100
  184. package/dist/components/questions/types/listen-and-choose-objects-in-scene/transform.js +1 -33
  185. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +1 -106
  186. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +1 -457
  187. package/dist/components/questions/types/listen-and-drag-objects-into-scene/index.js +1 -2
  188. package/dist/components/questions/types/listen-and-drag-objects-into-scene/register.js +1 -110
  189. package/dist/components/questions/types/listen-and-drag-objects-into-scene/transform.js +1 -35
  190. package/dist/components/questions/types/listen-and-speak-answer/ArrowIndicator.js +1 -8
  191. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -22
  192. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +1 -291
  193. package/dist/components/questions/types/listen-and-speak-answer/index.js +1 -2
  194. package/dist/components/questions/types/listen-and-speak-answer/register.js +1 -84
  195. package/dist/components/questions/types/listen-and-speak-answer/transform.js +1 -27
  196. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -32
  197. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +1 -295
  198. package/dist/components/questions/types/listen-and-speak-compare-images/register.js +1 -79
  199. package/dist/components/questions/types/listen-and-speak-compare-images/transform.js +1 -27
  200. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -34
  201. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +1 -285
  202. package/dist/components/questions/types/listen-and-speak-image-group/register.js +1 -81
  203. package/dist/components/questions/types/listen-and-speak-image-group/transform.js +1 -26
  204. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -38
  205. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +1 -385
  206. package/dist/components/questions/types/listen-and-speak-info-exchange/register.js +1 -87
  207. package/dist/components/questions/types/listen-and-speak-info-exchange/transform.js +1 -32
  208. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -26
  209. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +1 -247
  210. package/dist/components/questions/types/listen-and-speak-odd-one-out/register.js +1 -59
  211. package/dist/components/questions/types/listen-and-speak-odd-one-out/transform.js +1 -18
  212. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -26
  213. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +1 -256
  214. package/dist/components/questions/types/listen-and-speak-question-list/register.js +1 -81
  215. package/dist/components/questions/types/listen-and-speak-question-list/transform.js +1 -26
  216. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -26
  217. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +1 -230
  218. package/dist/components/questions/types/listen-and-speak-with-story-images/register.js +1 -56
  219. package/dist/components/questions/types/listen-and-speak-with-story-images/transform.js +1 -16
  220. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +1 -72
  221. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +1 -230
  222. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +1 -1
  223. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +1 -66
  224. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +1 -42
  225. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +1 -96
  226. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +1 -238
  227. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +1 -2
  228. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +1 -76
  229. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +1 -19
  230. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +1 -134
  231. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +1 -617
  232. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +1 -2
  233. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +1 -82
  234. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +1 -29
  235. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +1 -38
  236. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +1 -359
  237. package/dist/components/questions/types/match-by-writing-answer/index.js +1 -3
  238. package/dist/components/questions/types/match-by-writing-answer/register.js +1 -58
  239. package/dist/components/questions/types/match-by-writing-answer/transform.js +1 -61
  240. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsClient.js +1 -16
  241. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsCreator.js +1 -23
  242. package/dist/components/questions/types/read-and-color-objects/index.js +1 -2
  243. package/dist/components/questions/types/read-and-color-objects/register.js +1 -3
  244. package/dist/components/questions/types/read-and-color-objects/transform.js +1 -36
  245. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +1 -125
  246. package/dist/components/questions/types/read-displayed-content/register.js +1 -64
  247. package/dist/components/questions/types/read-displayed-content/transform.js +1 -12
  248. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -64
  249. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +1 -511
  250. package/dist/components/questions/types/read-passage-and-answer-questions/index.js +1 -2
  251. package/dist/components/questions/types/read-passage-and-answer-questions/register.js +1 -58
  252. package/dist/components/questions/types/read-passage-and-answer-questions/transform.js +1 -28
  253. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +1 -373
  254. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +1 -41
  255. package/dist/components/questions/types/speaking-conversation/register.js +1 -110
  256. package/dist/components/questions/types/speaking-conversation/transform.js +1 -34
  257. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +1 -74
  258. package/dist/components/questions/types/speaking-cue-card/register.js +1 -52
  259. package/dist/components/questions/types/speaking-cue-card/transform.js +1 -12
  260. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -10
  261. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +1 -161
  262. package/dist/components/questions/types/speaking-describe-image/index.js +1 -2
  263. package/dist/components/questions/types/speaking-describe-image/register.js +1 -69
  264. package/dist/components/questions/types/speaking-describe-image/transform.js +1 -16
  265. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +1 -234
  266. package/dist/components/questions/types/spontaneous-qa/register.js +1 -93
  267. package/dist/components/questions/types/spontaneous-qa/transform.js +1 -34
  268. package/dist/components/questions/types/true-false/TrueFalseClient.js +1 -75
  269. package/dist/components/questions/types/true-false/TrueFalseCreator.js +1 -244
  270. package/dist/components/questions/types/true-false/index.js +1 -2
  271. package/dist/components/questions/types/true-false/register.js +1 -77
  272. package/dist/components/questions/types/true-false/transform.js +1 -32
  273. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +1 -233
  274. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +1 -631
  275. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphWrapper.js +1 -28
  276. package/dist/components/questions/types/word-fill-paragraph/index.js +1 -2
  277. package/dist/components/questions/types/word-fill-paragraph/register.js +1 -136
  278. package/dist/components/questions/types/word-fill-paragraph/transform.js +1 -56
  279. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -138
  280. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +1 -261
  281. package/dist/components/questions/types/word-fill-structured-form/index.js +1 -2
  282. package/dist/components/questions/types/word-fill-structured-form/register.js +1 -67
  283. package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -40
  284. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -84
  285. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +1 -192
  286. package/dist/components/questions/types/word-ordering/index.js +1 -2
  287. package/dist/components/questions/types/word-ordering/register.js +1 -46
  288. package/dist/components/questions/types/word-ordering/transform.js +1 -24
  289. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +1 -98
  290. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +1 -320
  291. package/dist/components/questions/types/write-a-short-letter/index.js +1 -2
  292. package/dist/components/questions/types/write-a-short-letter/register.js +1 -68
  293. package/dist/components/questions/types/write-a-short-letter/transform.js +1 -25
  294. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +1 -61
  295. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +1 -196
  296. package/dist/components/questions/types/write-correct-verb-form/index.js +1 -2
  297. package/dist/components/questions/types/write-correct-verb-form/register.js +1 -62
  298. package/dist/components/questions/types/write-correct-verb-form/transform.js +1 -17
  299. package/dist/components/questions/types/write-sentences/WriteSentencesClient.js +1 -22
  300. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +1 -174
  301. package/dist/components/questions/types/write-sentences/index.js +1 -2
  302. package/dist/components/questions/types/write-sentences/register.js +1 -58
  303. package/dist/components/questions/types/write-sentences/transform.js +1 -28
  304. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -96
  305. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +1 -161
  306. package/dist/components/questions/types/write-short-paragraph/index.js +1 -2
  307. package/dist/components/questions/types/write-short-paragraph/register.js +1 -4
  308. package/dist/components/questions/types/write-short-paragraph/transform.js +1 -21
  309. package/dist/components/questions/viewer/QuestionGroupViewer.js +1 -36
  310. package/dist/components/questions/viewer/QuestionViewer.js +1 -446
  311. package/dist/components/questions/viewer/index.js +1 -2
  312. package/dist/components/results/ResultReviewPageContainer.js +1 -98
  313. package/dist/components/results/ReviewQuestionRenderer.js +1 -72
  314. package/dist/components/results/index.js +1 -2
  315. package/dist/components/results/result-review.utils.js +1 -6
  316. package/dist/components/results/review-data.js +1 -150
  317. package/dist/components/results/review-renderer.utils.js +1 -97
  318. package/dist/components/results/review-types.js +1 -1
  319. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +1 -138
  320. package/dist/components/themes/cambridge-yle/CambridgeYleBottomNav.js +1 -12
  321. package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.js +1 -18
  322. package/dist/components/themes/cambridge-yle/CambridgeYleExampleText.js +1 -11
  323. package/dist/components/themes/cambridge-yle/CambridgeYleHeader.js +1 -11
  324. package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.js +1 -9
  325. package/dist/components/themes/cambridge-yle/CambridgeYleNavButton.js +1 -13
  326. package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.js +1 -8
  327. package/dist/components/themes/cambridge-yle/CambridgeYleProgressBar.js +1 -6
  328. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCard.js +1 -5
  329. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCardFullWidth.js +1 -9
  330. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.js +1 -41
  331. package/dist/components/themes/cambridge-yle/CambridgeYleTimer.js +1 -25
  332. package/dist/components/themes/cambridge-yle/index.js +1 -13
  333. package/dist/components/themes/english-certification/EcAnswerTheQuestion.js +1 -24
  334. package/dist/components/themes/english-certification/EcMatchByWritingAnswer.js +1 -88
  335. package/dist/components/themes/english-certification/EcWordFillParagraph.js +1 -55
  336. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -156
  337. package/dist/components/themes/english-certification/EcWritingTask.js +1 -34
  338. package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +1 -114
  339. package/dist/components/themes/english-certification/EnglishCertificationBrand.js +1 -11
  340. package/dist/components/themes/english-certification/EnglishCertificationExamLayout.js +1 -21
  341. package/dist/components/themes/english-certification/EnglishCertificationFlagButton.js +1 -15
  342. package/dist/components/themes/english-certification/EnglishCertificationFooterBar.js +1 -19
  343. package/dist/components/themes/english-certification/EnglishCertificationGroupedQuestion.js +1 -148
  344. package/dist/components/themes/english-certification/EnglishCertificationHeader.js +1 -31
  345. package/dist/components/themes/english-certification/EnglishCertificationNavigationPanel.js +1 -71
  346. package/dist/components/themes/english-certification/EnglishCertificationOptionSelector.js +1 -33
  347. package/dist/components/themes/english-certification/EnglishCertificationPassagePanel.js +1 -16
  348. package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.js +1 -22
  349. package/dist/components/themes/english-certification/EnglishCertificationQuestionItem.js +1 -37
  350. package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +1 -72
  351. package/dist/components/themes/english-certification/EnglishCertificationReadingSection.js +1 -90
  352. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestion.js +1 -18
  353. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js +1 -29
  354. package/dist/components/themes/english-certification/english-certification-theme.js +1 -58
  355. package/dist/components/themes/english-certification/grouped-question-utils.js +1 -218
  356. package/dist/components/themes/english-certification/index.js +1 -21
  357. package/dist/components/themes/index.js +1 -6
  358. package/dist/components/themes/summer-sky/SummerSkyAudioPlayer.js +1 -170
  359. package/dist/components/themes/summer-sky/SummerSkyLayout.js +1 -221
  360. package/dist/components/themes/summer-sky/SummerSkySubmitModal.js +1 -18
  361. package/dist/components/themes/summer-sky/SummerSkyWaitingRoom.js +1 -194
  362. package/dist/components/themes/types.js +1 -1
  363. package/dist/components/ui/alert-dialog.js +1 -26
  364. package/dist/components/ui/alert.js +1 -22
  365. package/dist/components/ui/avatar.js +1 -12
  366. package/dist/components/ui/badge.js +1 -38
  367. package/dist/components/ui/button.js +1 -35
  368. package/dist/components/ui/calendar.js +1 -76
  369. package/dist/components/ui/card.js +1 -16
  370. package/dist/components/ui/carousel.js +1 -91
  371. package/dist/components/ui/checkbox.js +1 -9
  372. package/dist/components/ui/collapsible.js +1 -13
  373. package/dist/components/ui/command.js +1 -34
  374. package/dist/components/ui/confirm-dialog.js +1 -49
  375. package/dist/components/ui/dialog.js +1 -23
  376. package/dist/components/ui/drawer.js +1 -35
  377. package/dist/components/ui/dropdown-menu.js +1 -33
  378. package/dist/components/ui/file-upload.js +1 -155
  379. package/dist/components/ui/form.js +1 -62
  380. package/dist/components/ui/hover-card.js +1 -10
  381. package/dist/components/ui/image-preview.js +1 -29
  382. package/dist/components/ui/input.js +1 -8
  383. package/dist/components/ui/label.js +1 -9
  384. package/dist/components/ui/language-switcher.js +1 -15
  385. package/dist/components/ui/multi-select.js +1 -90
  386. package/dist/components/ui/points-input.js +1 -33
  387. package/dist/components/ui/popover.js +1 -26
  388. package/dist/components/ui/progress.js +1 -16
  389. package/dist/components/ui/radio-group.js +1 -12
  390. package/dist/components/ui/scroll-area.js +1 -11
  391. package/dist/components/ui/select.js +1 -38
  392. package/dist/components/ui/separator.js +1 -8
  393. package/dist/components/ui/sheet.js +1 -40
  394. package/dist/components/ui/single-select.js +1 -76
  395. package/dist/components/ui/skeleton.js +1 -6
  396. package/dist/components/ui/switch.js +1 -8
  397. package/dist/components/ui/table.js +1 -20
  398. package/dist/components/ui/tabs.js +1 -17
  399. package/dist/components/ui/textarea.js +1 -8
  400. package/dist/components/ui/tooltip.js +1 -11
  401. package/dist/index.d.ts +3 -3
  402. package/dist/index.js +1 -19
  403. package/dist/mcp/config.js +1 -32
  404. package/dist/mcp/http-client.js +1 -48
  405. package/dist/mcp/index.js +1 -29
  406. package/dist/mcp/tools/_helpers.js +1 -25
  407. package/dist/mcp/tools/exams.js +1 -439
  408. package/dist/mcp/tools/index.js +1 -6
  409. package/dist/mcp/tools/results.js +1 -19
  410. package/dist/shared/constants/ApiConstant.js +1 -276
  411. package/dist/shared/constants/QueryKeyConstant.js +1 -112
  412. package/dist/shared/constants/exam-level.enum.js +1 -41
  413. package/dist/shared/constants/landings/placement-test.constants.js +1 -431
  414. package/dist/shared/constants/question-skills.js +1 -267
  415. package/dist/shared/constants/routes.js +1 -81
  416. package/dist/shared/index.d.ts +3 -0
  417. package/dist/shared/index.js +1 -7
  418. package/dist/shared/lib/hooks/index.js +1 -10
  419. package/dist/shared/lib/hooks/useAlertDialog.js +1 -57
  420. package/dist/shared/lib/hooks/useAudioPlayCounter.js +1 -30
  421. package/dist/shared/lib/hooks/useAudioPreview.js +1 -81
  422. package/dist/shared/lib/hooks/useAudioUpload.js +1 -24
  423. package/dist/shared/lib/hooks/useConfirmDialog.js +1 -89
  424. package/dist/shared/lib/hooks/useDebouncedCallback.js +1 -32
  425. package/dist/shared/lib/hooks/useDelayedAutoPlay.js +1 -12
  426. package/dist/shared/lib/hooks/useFileUpload.js +1 -24
  427. package/dist/shared/lib/hooks/useImageUrl.js +1 -5
  428. package/dist/shared/lib/hooks/usePresignedFileUrl.js +1 -10
  429. package/dist/shared/lib/hooks/useReactHookForm.js +1 -31
  430. package/dist/shared/lib/hooks/useToast.js +1 -62
  431. package/dist/shared/lib/i18n/index.js +1 -54
  432. package/dist/shared/lib/i18n/messages/en/admin.js +1 -2104
  433. package/dist/shared/lib/i18n/messages/en/auth.js +1 -86
  434. package/dist/shared/lib/i18n/messages/en/common.js +1 -140
  435. package/dist/shared/lib/i18n/messages/en/file-upload.js +1 -18
  436. package/dist/shared/lib/i18n/messages/en/student.js +1 -133
  437. package/dist/shared/lib/i18n/messages/en/terms.js +1 -37
  438. package/dist/shared/lib/i18n/messages/en.js +1 -15
  439. package/dist/shared/lib/i18n/messages/vi/admin.js +1 -2104
  440. package/dist/shared/lib/i18n/messages/vi/auth.js +1 -86
  441. package/dist/shared/lib/i18n/messages/vi/common.js +1 -140
  442. package/dist/shared/lib/i18n/messages/vi/file-upload.js +1 -18
  443. package/dist/shared/lib/i18n/messages/vi/student.js +1 -133
  444. package/dist/shared/lib/i18n/messages/vi/terms.js +1 -37
  445. package/dist/shared/lib/i18n/messages/vi.js +1 -15
  446. package/dist/shared/lib/i18n/types.js +1 -1
  447. package/dist/shared/lib/stores/examQuestionStore.js +1 -212
  448. package/dist/shared/lib/stores/localeStore.js +1 -10
  449. package/dist/shared/lib/utils/public-page.js +1 -44
  450. package/dist/shared/lib/utils/question-reverse-transform.d.ts +6 -0
  451. package/dist/shared/lib/utils/question-reverse-transform.js +1 -0
  452. package/dist/shared/lib/utils/question-transform-types.js +1 -1
  453. package/dist/shared/lib/utils/question-transform-validation.d.ts +6 -0
  454. package/dist/shared/lib/utils/question-transform-validation.js +1 -0
  455. package/dist/shared/lib/utils/question-transform.d.ts +6 -2
  456. package/dist/shared/lib/utils/question-transform.js +1 -174
  457. package/dist/shared/lib/utils.js +1 -168
  458. package/dist/shared/lib/validation/choose-correct-answer.validation.js +1 -111
  459. package/dist/shared/lib/validation/label-the-picture.validation.js +1 -19
  460. package/dist/shared/lib/validation/listen-and-choose-from-answer-group.validation.js +1 -46
  461. package/dist/shared/lib/validation/listen-and-choose-objects-in-scene.validation.js +1 -21
  462. package/dist/shared/lib/validation/listen-and-drag-objects-into-scene.validation.js +1 -34
  463. package/dist/shared/lib/validation/listen-and-speak-answer.validation.js +1 -40
  464. package/dist/shared/lib/validation/listen-and-speak-compare-images.validation.js +1 -45
  465. package/dist/shared/lib/validation/listen-and-speak-image-group.validation.js +1 -19
  466. package/dist/shared/lib/validation/listen-and-speak-info-exchange.validation.js +1 -39
  467. package/dist/shared/lib/validation/listen-and-speak-odd-one-out.validation.js +1 -32
  468. package/dist/shared/lib/validation/listen-and-speak-question-list.validation.js +1 -19
  469. package/dist/shared/lib/validation/listen-and-speak-with-story-images.validation.js +1 -28
  470. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +1 -12
  471. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +1 -47
  472. package/dist/shared/lib/validation/read-and-color-objects.validation.js +1 -29
  473. package/dist/shared/lib/validation/read-displayed-content.validation.js +1 -28
  474. package/dist/shared/lib/validation/speaking-conversation.validation.js +1 -75
  475. package/dist/shared/lib/validation/speaking-describe-image.validation.js +1 -31
  476. package/dist/shared/lib/validation/spontaneous-qa.validation.js +1 -48
  477. package/dist/shared/lib/validation/word-fill-paragraph-default.validation.js +1 -38
  478. package/dist/shared/lib/validation/word-fill-paragraph-with-options.validation.js +1 -39
  479. package/dist/shared/lib/validation/word-fill-paragraph.validation.js +1 -43
  480. package/dist/shared/lib/validation/word-ordering.validation.js +1 -33
  481. package/dist/shared/types/branch.types.js +1 -6
  482. package/dist/shared/types/common.types.js +1 -50
  483. package/dist/shared/types/entities/exam-schedule.types.js +1 -5
  484. package/dist/shared/types/entities/exam.types.js +1 -1
  485. package/dist/shared/types/entities/question.types.js +1 -4
  486. package/dist/shared/types/entities/result.types.js +1 -4
  487. package/dist/shared/types/entities/user.types.js +1 -4
  488. package/dist/shared/types/exam-taking.types.js +1 -2
  489. package/dist/shared/types/history.types.js +1 -34
  490. package/dist/shared/types/index.js +1 -1
  491. package/dist/shared/types/questions/choose-correct-adjective.js +1 -4
  492. package/dist/shared/types/questions/choose-the-correct-answer.js +1 -9
  493. package/dist/shared/types/questions/fill-missing-words-in-grid.js +1 -11
  494. package/dist/shared/types/questions/index.js +1 -40
  495. package/dist/shared/types/questions/listen-and-speak-answer.js +1 -1
  496. package/dist/shared/types/questions/listen-and-speak-compare-images.js +1 -1
  497. package/dist/shared/types/questions/listen-and-speak-image-group.js +1 -1
  498. package/dist/shared/types/questions/listen-and-speak-info-exchange.js +1 -3
  499. package/dist/shared/types/questions/listen-and-speak-odd-one-out.js +1 -3
  500. package/dist/shared/types/questions/listen-and-speak-question-list.js +1 -1
  501. package/dist/shared/types/questions/listen-and-speak-with-story-images.js +1 -3
  502. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +1 -5
  503. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +1 -9
  504. package/dist/shared/types/questions/look-picture-fill-word-hint.js +1 -7
  505. package/dist/shared/types/questions/read-displayed-content.js +1 -1
  506. package/dist/shared/types/questions/speaking-conversation.js +1 -7
  507. package/dist/shared/types/questions/speaking-describe-image.js +1 -1
  508. package/dist/shared/types/questions/spontaneous-qa.js +1 -1
  509. package/dist/shared/types/questions/write-correct-verb-form.js +1 -12
  510. package/dist/shared/types/role.types.js +1 -1
  511. package/dist/shared/types/structure-management.js +1 -28
  512. package/dist/shared/types/student.types.js +1 -54
  513. package/dist/shared/types/teacher-dashboard-rooms.js +1 -2
  514. package/package.json +12 -3
@@ -1,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";