@tinyweb_dev/oe-exam-sdk 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (485) hide show
  1. package/dist/api/exam-create.js +1 -179
  2. package/dist/api/exam-create.types.js +1 -1
  3. package/dist/api/exam-questions.js +1 -98
  4. package/dist/api/exam-questions.types.js +1 -1
  5. package/dist/api/exam-taking.js +1 -73
  6. package/dist/api/exam-taking.types.js +1 -1
  7. package/dist/api/index.js +1 -6
  8. package/dist/api/result-review.js +1 -50
  9. package/dist/api/result-review.types.js +1 -1
  10. package/dist/components/CueCardEditor.js +1 -58
  11. package/dist/components/common/ResolvedImage.js +1 -15
  12. package/dist/components/exams/CreateExamInfoForm.js +1 -35
  13. package/dist/components/exams/CreateExamPageContainer.js +1 -103
  14. package/dist/components/exams/ExamCreator.js +1 -89
  15. package/dist/components/exams/ExamPartTabs.js +1 -40
  16. package/dist/components/exams/ExamQuestionGrid.js +1 -18
  17. package/dist/components/exams/ExamQuestionsPageContainer.js +1 -90
  18. package/dist/components/exams/PartAudioUploader.js +1 -9
  19. package/dist/components/exams/TemplateSelector.js +1 -66
  20. package/dist/components/exams/api.js +1 -3
  21. package/dist/components/exams/create-exam.validation.js +1 -18
  22. package/dist/components/exams/exam-template.utils.js +1 -18
  23. package/dist/components/exams/index.js +1 -10
  24. package/dist/components/exams/take/ExamTakingPageContainer.js +1 -153
  25. package/dist/components/exams/take/GenericQuestionRenderer.js +1 -14
  26. package/dist/components/exams/take/exam-taking.utils.js +1 -88
  27. package/dist/components/exams/take/hooks/useSpeakingRecorder.js +1 -24
  28. package/dist/components/exams/take/index.js +1 -2
  29. package/dist/components/exams/take/types.js +1 -1
  30. package/dist/components/index.js +1 -42
  31. package/dist/components/questions/_shared/config/question-types.config.js +1 -333
  32. package/dist/components/questions/_shared/constants/color-palette.js +1 -21
  33. package/dist/components/questions/_shared/hooks/useAnswerGroupQuestionGroup.js +1 -105
  34. package/dist/components/questions/_shared/hooks/useBasicQuestionGroup.js +1 -222
  35. package/dist/components/questions/_shared/hooks/useCompareImagesGroup.js +1 -140
  36. package/dist/components/questions/_shared/hooks/useImageQuestionGroup.js +1 -123
  37. package/dist/components/questions/_shared/hooks/useInfoExchangeGroup.js +1 -50
  38. package/dist/components/questions/_shared/hooks/useQuestionFormState.js +1 -54
  39. package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +1 -123
  40. package/dist/components/questions/_shared/hooks/useQuestionListGroup.js +1 -121
  41. package/dist/components/questions/_shared/hooks/useQuestionOnChange.js +1 -70
  42. package/dist/components/questions/_shared/hooks/useSceneQuestionGroup.js +1 -194
  43. package/dist/components/questions/_shared/index.js +1 -7
  44. package/dist/components/questions/_shared/types/answer-the-question.type.js +1 -1
  45. package/dist/components/questions/_shared/types/arrange-letters-into-words.type.js +1 -1
  46. package/dist/components/questions/_shared/types/choose-correct-adjective.type.js +1 -3
  47. package/dist/components/questions/_shared/types/color-region.config.js +1 -1
  48. package/dist/components/questions/_shared/types/fill-in-blank.type.js +1 -1
  49. package/dist/components/questions/_shared/types/fill-missing-words-in-grid.type.js +1 -1
  50. package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +1 -1
  51. package/dist/components/questions/_shared/types/index.js +1 -12
  52. package/dist/components/questions/_shared/types/label-the-picture.type.js +1 -1
  53. package/dist/components/questions/_shared/types/listen-and-choose-from-answer-group.type.js +1 -1
  54. package/dist/components/questions/_shared/types/listen-and-choose-objects-in-scene.type.js +1 -1
  55. package/dist/components/questions/_shared/types/listen-and-drag-objects-into-scene.type.js +1 -1
  56. package/dist/components/questions/_shared/types/listen-and-speak-answer.type.js +1 -1
  57. package/dist/components/questions/_shared/types/listen-and-speak-compare-images.type.js +1 -1
  58. package/dist/components/questions/_shared/types/listen-and-speak-image-group.type.js +1 -1
  59. package/dist/components/questions/_shared/types/listen-and-speak-info-exchange.type.js +1 -1
  60. package/dist/components/questions/_shared/types/listen-and-speak-odd-one-out.type.js +1 -3
  61. package/dist/components/questions/_shared/types/listen-and-speak-question-list.type.js +1 -1
  62. package/dist/components/questions/_shared/types/listen-and-speak-with-story-images.type.js +1 -3
  63. package/dist/components/questions/_shared/types/listen-and-tick-answer.type.js +1 -1
  64. package/dist/components/questions/_shared/types/listening-drawline.type.js +1 -1
  65. package/dist/components/questions/_shared/types/look-and-choose-answer.type.js +1 -1
  66. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +1 -1
  67. package/dist/components/questions/_shared/types/match-by-writing-answer.type.js +1 -1
  68. package/dist/components/questions/_shared/types/multiple-choice.type.js +1 -1
  69. package/dist/components/questions/_shared/types/question-group.type.js +1 -11
  70. package/dist/components/questions/_shared/types/read-and-color-objects.type.js +1 -1
  71. package/dist/components/questions/_shared/types/read-displayed-content.type.js +1 -1
  72. package/dist/components/questions/_shared/types/read-passage-and-answer-questions.type.js +1 -1
  73. package/dist/components/questions/_shared/types/speaking-describe-image.type.js +1 -1
  74. package/dist/components/questions/_shared/types/spontaneous-qa.type.js +1 -1
  75. package/dist/components/questions/_shared/types/word-fill-paragraph-with-options.type.js +1 -1
  76. package/dist/components/questions/_shared/types/word-fill-paragraph.type.js +1 -1
  77. package/dist/components/questions/_shared/types/word-fill-structured-form.type.js +1 -1
  78. package/dist/components/questions/_shared/types/word-ordering.type.js +1 -1
  79. package/dist/components/questions/_shared/types/write-a-short-letter.type.js +1 -1
  80. package/dist/components/questions/_shared/types/write-correct-verb-form.type.js +1 -3
  81. package/dist/components/questions/_shared/types/write-sentences.type.js +1 -1
  82. package/dist/components/questions/_shared/types/write-short-paragraph.type.js +1 -1
  83. package/dist/components/questions/_shared/utils/answer-builder.js +1 -79
  84. package/dist/components/questions/_shared/utils/createColorRegionRegistration.js +1 -39
  85. package/dist/components/questions/_shared/utils/id-generator.js +1 -3
  86. package/dist/components/questions/_shared/utils/question-validation.js +1 -21
  87. package/dist/components/questions/components/ColorNode.js +1 -85
  88. package/dist/components/questions/components/ColorRegionClient.js +1 -545
  89. package/dist/components/questions/components/ColorRegionCreator.js +1 -420
  90. package/dist/components/questions/components/ColorRegionNode.js +1 -59
  91. package/dist/components/questions/components/CreatorGuide.js +1 -103
  92. package/dist/components/questions/components/NameNode.js +1 -26
  93. package/dist/components/questions/components/QuestionBankOption.js +1 -46
  94. package/dist/components/questions/components/QuestionBasicInfoForm.js +1 -9
  95. package/dist/components/questions/components/QuestionCommonFields.js +1 -33
  96. package/dist/components/questions/components/QuestionSearchDropdown.js +1 -258
  97. package/dist/components/questions/components/QuestionTypeSelector.js +1 -32
  98. package/dist/components/questions/components/QuestionTypeSpecificForms.js +1 -6
  99. package/dist/components/questions/components/RegionNode.js +1 -94
  100. package/dist/components/questions/components/index.js +1 -14
  101. package/dist/components/questions/creator/QuestionCreator.js +1 -90
  102. package/dist/components/questions/creator/QuestionGroupCreator.js +1 -119
  103. package/dist/components/questions/creator/dedicated-component-router.js +1 -79
  104. package/dist/components/questions/creator/index.js +1 -3
  105. package/dist/components/questions/creator/question-type-registry.js +1 -80
  106. package/dist/components/questions/groups/ArticlesGroupCard.js +1 -121
  107. package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -50
  108. package/dist/components/questions/groups/DocumentGroupCard.js +1 -114
  109. package/dist/components/questions/groups/MatchAnswerGroupCard.js +1 -25
  110. package/dist/components/questions/index.js +1 -10
  111. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +1 -37
  112. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +1 -351
  113. package/dist/components/questions/types/answer-the-question/register.js +1 -67
  114. package/dist/components/questions/types/answer-the-question/transform.js +1 -36
  115. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +1 -141
  116. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +1 -118
  117. package/dist/components/questions/types/arrange-letters-into-words/index.js +1 -2
  118. package/dist/components/questions/types/arrange-letters-into-words/register.js +1 -4
  119. package/dist/components/questions/types/arrange-letters-into-words/transform.js +1 -24
  120. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -64
  121. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +1 -169
  122. package/dist/components/questions/types/choose-correct-adjective/index.js +1 -2
  123. package/dist/components/questions/types/choose-correct-adjective/register.js +1 -55
  124. package/dist/components/questions/types/choose-correct-adjective/transform.js +1 -16
  125. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +1 -131
  126. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +1 -581
  127. package/dist/components/questions/types/choose-the-correct-answer/index.js +1 -2
  128. package/dist/components/questions/types/choose-the-correct-answer/register.js +1 -101
  129. package/dist/components/questions/types/choose-the-correct-answer/transform.js +1 -43
  130. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +1 -275
  131. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +1 -448
  132. package/dist/components/questions/types/fill-in-blank/index.js +1 -2
  133. package/dist/components/questions/types/fill-in-blank/register.js +1 -138
  134. package/dist/components/questions/types/fill-in-blank/transform.js +1 -59
  135. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +1 -117
  136. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +1 -671
  137. package/dist/components/questions/types/fill-missing-words-in-grid/index.js +1 -2
  138. package/dist/components/questions/types/fill-missing-words-in-grid/register.js +1 -37
  139. package/dist/components/questions/types/fill-missing-words-in-grid/transform.js +1 -15
  140. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +1 -262
  141. package/dist/components/questions/types/gn-speaking-interview/register.js +1 -121
  142. package/dist/components/questions/types/gn-speaking-interview/transform.js +1 -48
  143. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingClient.js +1 -16
  144. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingCreator.js +1 -27
  145. package/dist/components/questions/types/image-object-matching/index.js +1 -2
  146. package/dist/components/questions/types/image-object-matching/register.js +1 -3
  147. package/dist/components/questions/types/image-object-matching/transform.js +1 -2
  148. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +1 -102
  149. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +1 -344
  150. package/dist/components/questions/types/label-the-picture/index.js +1 -2
  151. package/dist/components/questions/types/label-the-picture/register.js +1 -54
  152. package/dist/components/questions/types/label-the-picture/transform.js +1 -28
  153. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +1 -78
  154. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +1 -232
  155. package/dist/components/questions/types/listen-and-choose-from-answer-group/index.js +1 -2
  156. package/dist/components/questions/types/listen-and-choose-from-answer-group/register.js +1 -82
  157. package/dist/components/questions/types/listen-and-choose-from-answer-group/transform.js +1 -27
  158. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +1 -70
  159. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +1 -386
  160. package/dist/components/questions/types/listen-and-choose-objects-in-scene/index.js +1 -2
  161. package/dist/components/questions/types/listen-and-choose-objects-in-scene/register.js +1 -100
  162. package/dist/components/questions/types/listen-and-choose-objects-in-scene/transform.js +1 -33
  163. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +1 -106
  164. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +1 -457
  165. package/dist/components/questions/types/listen-and-drag-objects-into-scene/index.js +1 -2
  166. package/dist/components/questions/types/listen-and-drag-objects-into-scene/register.js +1 -110
  167. package/dist/components/questions/types/listen-and-drag-objects-into-scene/transform.js +1 -35
  168. package/dist/components/questions/types/listen-and-speak-answer/ArrowIndicator.js +1 -8
  169. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -22
  170. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +1 -291
  171. package/dist/components/questions/types/listen-and-speak-answer/index.js +1 -2
  172. package/dist/components/questions/types/listen-and-speak-answer/register.js +1 -84
  173. package/dist/components/questions/types/listen-and-speak-answer/transform.js +1 -27
  174. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -32
  175. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +1 -295
  176. package/dist/components/questions/types/listen-and-speak-compare-images/register.js +1 -79
  177. package/dist/components/questions/types/listen-and-speak-compare-images/transform.js +1 -27
  178. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -34
  179. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +1 -285
  180. package/dist/components/questions/types/listen-and-speak-image-group/register.js +1 -81
  181. package/dist/components/questions/types/listen-and-speak-image-group/transform.js +1 -26
  182. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -38
  183. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +1 -385
  184. package/dist/components/questions/types/listen-and-speak-info-exchange/register.js +1 -87
  185. package/dist/components/questions/types/listen-and-speak-info-exchange/transform.js +1 -32
  186. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -26
  187. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +1 -247
  188. package/dist/components/questions/types/listen-and-speak-odd-one-out/register.js +1 -59
  189. package/dist/components/questions/types/listen-and-speak-odd-one-out/transform.js +1 -18
  190. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -26
  191. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +1 -256
  192. package/dist/components/questions/types/listen-and-speak-question-list/register.js +1 -81
  193. package/dist/components/questions/types/listen-and-speak-question-list/transform.js +1 -26
  194. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -26
  195. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +1 -230
  196. package/dist/components/questions/types/listen-and-speak-with-story-images/register.js +1 -56
  197. package/dist/components/questions/types/listen-and-speak-with-story-images/transform.js +1 -16
  198. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +1 -72
  199. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +1 -230
  200. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +1 -1
  201. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +1 -66
  202. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +1 -42
  203. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +1 -96
  204. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +1 -238
  205. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +1 -2
  206. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +1 -76
  207. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +1 -19
  208. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +1 -134
  209. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +1 -617
  210. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +1 -2
  211. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +1 -82
  212. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +1 -29
  213. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +1 -38
  214. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +1 -359
  215. package/dist/components/questions/types/match-by-writing-answer/index.js +1 -3
  216. package/dist/components/questions/types/match-by-writing-answer/register.js +1 -58
  217. package/dist/components/questions/types/match-by-writing-answer/transform.js +1 -61
  218. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsClient.js +1 -16
  219. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsCreator.js +1 -23
  220. package/dist/components/questions/types/read-and-color-objects/index.js +1 -2
  221. package/dist/components/questions/types/read-and-color-objects/register.js +1 -3
  222. package/dist/components/questions/types/read-and-color-objects/transform.js +1 -36
  223. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +1 -125
  224. package/dist/components/questions/types/read-displayed-content/register.js +1 -64
  225. package/dist/components/questions/types/read-displayed-content/transform.js +1 -12
  226. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -64
  227. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +1 -511
  228. package/dist/components/questions/types/read-passage-and-answer-questions/index.js +1 -2
  229. package/dist/components/questions/types/read-passage-and-answer-questions/register.js +1 -58
  230. package/dist/components/questions/types/read-passage-and-answer-questions/transform.js +1 -28
  231. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +1 -373
  232. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +1 -41
  233. package/dist/components/questions/types/speaking-conversation/register.js +1 -110
  234. package/dist/components/questions/types/speaking-conversation/transform.js +1 -34
  235. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +1 -74
  236. package/dist/components/questions/types/speaking-cue-card/register.js +1 -52
  237. package/dist/components/questions/types/speaking-cue-card/transform.js +1 -12
  238. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -10
  239. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +1 -161
  240. package/dist/components/questions/types/speaking-describe-image/index.js +1 -2
  241. package/dist/components/questions/types/speaking-describe-image/register.js +1 -69
  242. package/dist/components/questions/types/speaking-describe-image/transform.js +1 -16
  243. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +1 -234
  244. package/dist/components/questions/types/spontaneous-qa/register.js +1 -93
  245. package/dist/components/questions/types/spontaneous-qa/transform.js +1 -34
  246. package/dist/components/questions/types/true-false/TrueFalseClient.js +1 -75
  247. package/dist/components/questions/types/true-false/TrueFalseCreator.js +1 -244
  248. package/dist/components/questions/types/true-false/index.js +1 -2
  249. package/dist/components/questions/types/true-false/register.js +1 -77
  250. package/dist/components/questions/types/true-false/transform.js +1 -32
  251. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +1 -233
  252. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +1 -631
  253. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphWrapper.js +1 -28
  254. package/dist/components/questions/types/word-fill-paragraph/index.js +1 -2
  255. package/dist/components/questions/types/word-fill-paragraph/register.js +1 -136
  256. package/dist/components/questions/types/word-fill-paragraph/transform.js +1 -56
  257. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -138
  258. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +1 -261
  259. package/dist/components/questions/types/word-fill-structured-form/index.js +1 -2
  260. package/dist/components/questions/types/word-fill-structured-form/register.js +1 -67
  261. package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -40
  262. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -84
  263. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +1 -192
  264. package/dist/components/questions/types/word-ordering/index.js +1 -2
  265. package/dist/components/questions/types/word-ordering/register.js +1 -46
  266. package/dist/components/questions/types/word-ordering/transform.js +1 -24
  267. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +1 -98
  268. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +1 -320
  269. package/dist/components/questions/types/write-a-short-letter/index.js +1 -2
  270. package/dist/components/questions/types/write-a-short-letter/register.js +1 -68
  271. package/dist/components/questions/types/write-a-short-letter/transform.js +1 -25
  272. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +1 -61
  273. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +1 -196
  274. package/dist/components/questions/types/write-correct-verb-form/index.js +1 -2
  275. package/dist/components/questions/types/write-correct-verb-form/register.js +1 -62
  276. package/dist/components/questions/types/write-correct-verb-form/transform.js +1 -17
  277. package/dist/components/questions/types/write-sentences/WriteSentencesClient.js +1 -22
  278. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +1 -174
  279. package/dist/components/questions/types/write-sentences/index.js +1 -2
  280. package/dist/components/questions/types/write-sentences/register.js +1 -58
  281. package/dist/components/questions/types/write-sentences/transform.js +1 -28
  282. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -96
  283. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +1 -161
  284. package/dist/components/questions/types/write-short-paragraph/index.js +1 -2
  285. package/dist/components/questions/types/write-short-paragraph/register.js +1 -4
  286. package/dist/components/questions/types/write-short-paragraph/transform.js +1 -21
  287. package/dist/components/questions/viewer/QuestionGroupViewer.js +1 -36
  288. package/dist/components/questions/viewer/QuestionViewer.js +1 -446
  289. package/dist/components/questions/viewer/index.js +1 -2
  290. package/dist/components/results/ResultReviewPageContainer.js +1 -98
  291. package/dist/components/results/ReviewQuestionRenderer.js +1 -72
  292. package/dist/components/results/index.js +1 -2
  293. package/dist/components/results/result-review.utils.js +1 -6
  294. package/dist/components/results/review-data.js +1 -150
  295. package/dist/components/results/review-renderer.utils.js +1 -97
  296. package/dist/components/results/review-types.js +1 -1
  297. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +1 -138
  298. package/dist/components/themes/cambridge-yle/CambridgeYleBottomNav.js +1 -12
  299. package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.js +1 -18
  300. package/dist/components/themes/cambridge-yle/CambridgeYleExampleText.js +1 -11
  301. package/dist/components/themes/cambridge-yle/CambridgeYleHeader.js +1 -11
  302. package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.js +1 -9
  303. package/dist/components/themes/cambridge-yle/CambridgeYleNavButton.js +1 -13
  304. package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.js +1 -8
  305. package/dist/components/themes/cambridge-yle/CambridgeYleProgressBar.js +1 -6
  306. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCard.js +1 -5
  307. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCardFullWidth.js +1 -9
  308. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.js +1 -41
  309. package/dist/components/themes/cambridge-yle/CambridgeYleTimer.js +1 -25
  310. package/dist/components/themes/cambridge-yle/index.js +1 -13
  311. package/dist/components/themes/english-certification/EcAnswerTheQuestion.js +1 -24
  312. package/dist/components/themes/english-certification/EcMatchByWritingAnswer.js +1 -88
  313. package/dist/components/themes/english-certification/EcWordFillParagraph.js +1 -55
  314. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -156
  315. package/dist/components/themes/english-certification/EcWritingTask.js +1 -34
  316. package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +1 -114
  317. package/dist/components/themes/english-certification/EnglishCertificationBrand.js +1 -11
  318. package/dist/components/themes/english-certification/EnglishCertificationExamLayout.js +1 -21
  319. package/dist/components/themes/english-certification/EnglishCertificationFlagButton.js +1 -15
  320. package/dist/components/themes/english-certification/EnglishCertificationFooterBar.js +1 -19
  321. package/dist/components/themes/english-certification/EnglishCertificationGroupedQuestion.js +1 -148
  322. package/dist/components/themes/english-certification/EnglishCertificationHeader.js +1 -31
  323. package/dist/components/themes/english-certification/EnglishCertificationNavigationPanel.js +1 -71
  324. package/dist/components/themes/english-certification/EnglishCertificationOptionSelector.js +1 -33
  325. package/dist/components/themes/english-certification/EnglishCertificationPassagePanel.js +1 -16
  326. package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.js +1 -22
  327. package/dist/components/themes/english-certification/EnglishCertificationQuestionItem.js +1 -37
  328. package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +1 -72
  329. package/dist/components/themes/english-certification/EnglishCertificationReadingSection.js +1 -90
  330. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestion.js +1 -18
  331. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js +1 -29
  332. package/dist/components/themes/english-certification/english-certification-theme.js +1 -58
  333. package/dist/components/themes/english-certification/grouped-question-utils.js +1 -218
  334. package/dist/components/themes/english-certification/index.js +1 -21
  335. package/dist/components/themes/index.js +1 -6
  336. package/dist/components/themes/summer-sky/SummerSkyAudioPlayer.js +1 -170
  337. package/dist/components/themes/summer-sky/SummerSkyLayout.js +1 -221
  338. package/dist/components/themes/summer-sky/SummerSkySubmitModal.js +1 -18
  339. package/dist/components/themes/summer-sky/SummerSkyWaitingRoom.js +1 -194
  340. package/dist/components/themes/types.js +1 -1
  341. package/dist/components/ui/alert-dialog.js +1 -26
  342. package/dist/components/ui/alert.js +1 -22
  343. package/dist/components/ui/avatar.js +1 -12
  344. package/dist/components/ui/badge.js +1 -38
  345. package/dist/components/ui/button.js +1 -35
  346. package/dist/components/ui/calendar.js +1 -76
  347. package/dist/components/ui/card.js +1 -16
  348. package/dist/components/ui/carousel.js +1 -91
  349. package/dist/components/ui/checkbox.js +1 -9
  350. package/dist/components/ui/collapsible.js +1 -13
  351. package/dist/components/ui/command.js +1 -34
  352. package/dist/components/ui/confirm-dialog.js +1 -49
  353. package/dist/components/ui/dialog.js +1 -23
  354. package/dist/components/ui/drawer.js +1 -35
  355. package/dist/components/ui/dropdown-menu.js +1 -33
  356. package/dist/components/ui/file-upload.js +1 -155
  357. package/dist/components/ui/form.js +1 -62
  358. package/dist/components/ui/hover-card.js +1 -10
  359. package/dist/components/ui/image-preview.js +1 -29
  360. package/dist/components/ui/input.js +1 -8
  361. package/dist/components/ui/label.js +1 -9
  362. package/dist/components/ui/language-switcher.js +1 -15
  363. package/dist/components/ui/multi-select.js +1 -90
  364. package/dist/components/ui/points-input.js +1 -33
  365. package/dist/components/ui/popover.js +1 -26
  366. package/dist/components/ui/progress.js +1 -16
  367. package/dist/components/ui/radio-group.js +1 -12
  368. package/dist/components/ui/scroll-area.js +1 -11
  369. package/dist/components/ui/select.js +1 -38
  370. package/dist/components/ui/separator.js +1 -8
  371. package/dist/components/ui/sheet.js +1 -40
  372. package/dist/components/ui/single-select.js +1 -76
  373. package/dist/components/ui/skeleton.js +1 -6
  374. package/dist/components/ui/switch.js +1 -8
  375. package/dist/components/ui/table.js +1 -20
  376. package/dist/components/ui/tabs.js +1 -17
  377. package/dist/components/ui/textarea.js +1 -8
  378. package/dist/components/ui/tooltip.js +1 -11
  379. package/dist/index.js +1 -19
  380. package/dist/mcp/config.js +1 -32
  381. package/dist/mcp/http-client.js +1 -48
  382. package/dist/mcp/index.js +1 -29
  383. package/dist/mcp/tools/_helpers.js +1 -25
  384. package/dist/mcp/tools/exams.js +1 -439
  385. package/dist/mcp/tools/index.js +1 -6
  386. package/dist/mcp/tools/results.js +1 -19
  387. package/dist/shared/constants/ApiConstant.js +1 -276
  388. package/dist/shared/constants/QueryKeyConstant.js +1 -112
  389. package/dist/shared/constants/exam-level.enum.js +1 -41
  390. package/dist/shared/constants/landings/placement-test.constants.js +1 -431
  391. package/dist/shared/constants/question-skills.js +1 -267
  392. package/dist/shared/constants/routes.js +1 -81
  393. package/dist/shared/index.js +1 -7
  394. package/dist/shared/lib/hooks/index.js +1 -10
  395. package/dist/shared/lib/hooks/useAlertDialog.js +1 -57
  396. package/dist/shared/lib/hooks/useAudioPlayCounter.js +1 -30
  397. package/dist/shared/lib/hooks/useAudioPreview.js +1 -81
  398. package/dist/shared/lib/hooks/useAudioUpload.js +1 -24
  399. package/dist/shared/lib/hooks/useConfirmDialog.js +1 -89
  400. package/dist/shared/lib/hooks/useDebouncedCallback.js +1 -32
  401. package/dist/shared/lib/hooks/useDelayedAutoPlay.js +1 -12
  402. package/dist/shared/lib/hooks/useFileUpload.js +1 -24
  403. package/dist/shared/lib/hooks/useImageUrl.js +1 -5
  404. package/dist/shared/lib/hooks/usePresignedFileUrl.js +1 -10
  405. package/dist/shared/lib/hooks/useReactHookForm.js +1 -31
  406. package/dist/shared/lib/hooks/useToast.js +1 -62
  407. package/dist/shared/lib/i18n/index.js +1 -54
  408. package/dist/shared/lib/i18n/messages/en/admin.js +1 -2104
  409. package/dist/shared/lib/i18n/messages/en/auth.js +1 -86
  410. package/dist/shared/lib/i18n/messages/en/common.js +1 -140
  411. package/dist/shared/lib/i18n/messages/en/file-upload.js +1 -18
  412. package/dist/shared/lib/i18n/messages/en/student.js +1 -133
  413. package/dist/shared/lib/i18n/messages/en/terms.js +1 -37
  414. package/dist/shared/lib/i18n/messages/en.js +1 -15
  415. package/dist/shared/lib/i18n/messages/vi/admin.js +1 -2104
  416. package/dist/shared/lib/i18n/messages/vi/auth.js +1 -86
  417. package/dist/shared/lib/i18n/messages/vi/common.js +1 -140
  418. package/dist/shared/lib/i18n/messages/vi/file-upload.js +1 -18
  419. package/dist/shared/lib/i18n/messages/vi/student.js +1 -133
  420. package/dist/shared/lib/i18n/messages/vi/terms.js +1 -37
  421. package/dist/shared/lib/i18n/messages/vi.js +1 -15
  422. package/dist/shared/lib/i18n/types.js +1 -1
  423. package/dist/shared/lib/stores/examQuestionStore.js +1 -212
  424. package/dist/shared/lib/stores/localeStore.js +1 -10
  425. package/dist/shared/lib/utils/public-page.js +1 -44
  426. package/dist/shared/lib/utils/question-transform-types.js +1 -1
  427. package/dist/shared/lib/utils/question-transform.js +1 -174
  428. package/dist/shared/lib/utils.js +1 -168
  429. package/dist/shared/lib/validation/choose-correct-answer.validation.js +1 -111
  430. package/dist/shared/lib/validation/label-the-picture.validation.js +1 -19
  431. package/dist/shared/lib/validation/listen-and-choose-from-answer-group.validation.js +1 -46
  432. package/dist/shared/lib/validation/listen-and-choose-objects-in-scene.validation.js +1 -21
  433. package/dist/shared/lib/validation/listen-and-drag-objects-into-scene.validation.js +1 -34
  434. package/dist/shared/lib/validation/listen-and-speak-answer.validation.js +1 -40
  435. package/dist/shared/lib/validation/listen-and-speak-compare-images.validation.js +1 -45
  436. package/dist/shared/lib/validation/listen-and-speak-image-group.validation.js +1 -19
  437. package/dist/shared/lib/validation/listen-and-speak-info-exchange.validation.js +1 -39
  438. package/dist/shared/lib/validation/listen-and-speak-odd-one-out.validation.js +1 -32
  439. package/dist/shared/lib/validation/listen-and-speak-question-list.validation.js +1 -19
  440. package/dist/shared/lib/validation/listen-and-speak-with-story-images.validation.js +1 -28
  441. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +1 -12
  442. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +1 -47
  443. package/dist/shared/lib/validation/read-and-color-objects.validation.js +1 -29
  444. package/dist/shared/lib/validation/read-displayed-content.validation.js +1 -28
  445. package/dist/shared/lib/validation/speaking-conversation.validation.js +1 -75
  446. package/dist/shared/lib/validation/speaking-describe-image.validation.js +1 -31
  447. package/dist/shared/lib/validation/spontaneous-qa.validation.js +1 -48
  448. package/dist/shared/lib/validation/word-fill-paragraph-default.validation.js +1 -38
  449. package/dist/shared/lib/validation/word-fill-paragraph-with-options.validation.js +1 -39
  450. package/dist/shared/lib/validation/word-fill-paragraph.validation.js +1 -43
  451. package/dist/shared/lib/validation/word-ordering.validation.js +1 -33
  452. package/dist/shared/types/branch.types.js +1 -6
  453. package/dist/shared/types/common.types.js +1 -50
  454. package/dist/shared/types/entities/exam-schedule.types.js +1 -5
  455. package/dist/shared/types/entities/exam.types.js +1 -1
  456. package/dist/shared/types/entities/question.types.js +1 -4
  457. package/dist/shared/types/entities/result.types.js +1 -4
  458. package/dist/shared/types/entities/user.types.js +1 -4
  459. package/dist/shared/types/exam-taking.types.js +1 -2
  460. package/dist/shared/types/history.types.js +1 -34
  461. package/dist/shared/types/index.js +1 -1
  462. package/dist/shared/types/questions/choose-correct-adjective.js +1 -4
  463. package/dist/shared/types/questions/choose-the-correct-answer.js +1 -9
  464. package/dist/shared/types/questions/fill-missing-words-in-grid.js +1 -11
  465. package/dist/shared/types/questions/index.js +1 -40
  466. package/dist/shared/types/questions/listen-and-speak-answer.js +1 -1
  467. package/dist/shared/types/questions/listen-and-speak-compare-images.js +1 -1
  468. package/dist/shared/types/questions/listen-and-speak-image-group.js +1 -1
  469. package/dist/shared/types/questions/listen-and-speak-info-exchange.js +1 -3
  470. package/dist/shared/types/questions/listen-and-speak-odd-one-out.js +1 -3
  471. package/dist/shared/types/questions/listen-and-speak-question-list.js +1 -1
  472. package/dist/shared/types/questions/listen-and-speak-with-story-images.js +1 -3
  473. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +1 -5
  474. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +1 -9
  475. package/dist/shared/types/questions/look-picture-fill-word-hint.js +1 -7
  476. package/dist/shared/types/questions/read-displayed-content.js +1 -1
  477. package/dist/shared/types/questions/speaking-conversation.js +1 -7
  478. package/dist/shared/types/questions/speaking-describe-image.js +1 -1
  479. package/dist/shared/types/questions/spontaneous-qa.js +1 -1
  480. package/dist/shared/types/questions/write-correct-verb-form.js +1 -12
  481. package/dist/shared/types/role.types.js +1 -1
  482. package/dist/shared/types/structure-management.js +1 -28
  483. package/dist/shared/types/student.types.js +1 -54
  484. package/dist/shared/types/teacher-dashboard-rooms.js +1 -2
  485. package/package.json +4 -2
@@ -1,58 +1 @@
1
- import { WriteSentencesCreator } from './WriteSentencesCreator';
2
- export const writeSentencesRegistration = {
3
- component: WriteSentencesCreator,
4
- transformInitialData: (initialData) => {
5
- const answer = initialData?.answer || {};
6
- const explanation = initialData?.explanation || answer?.explanation || '';
7
- const apiPoints = initialData?.total_points
8
- ? parseFloat(initialData.total_points)
9
- : (initialData?.points || 1);
10
- const questionGroup = initialData?.questionGroup || {};
11
- const groupPayload = questionGroup?.payload || {};
12
- const groupTitle = groupPayload?.title || answer?.title || '';
13
- const groupContent = groupPayload?.content || answer?.groupContent || '';
14
- const groupImageUrl = groupPayload?.imageUrl || answer?.imageUrl || '';
15
- const groupNumber = questionGroup?.groupNumber || answer?.groupNumber || 1;
16
- const content = initialData?.content || {};
17
- const instruction = answer?.instruction || content?.instruction || '';
18
- const sentenceCount = answer?.sentenceCount || content?.sentenceCount || undefined;
19
- return {
20
- points: answer?.points || apiPoints,
21
- explanation,
22
- instruction,
23
- sentenceCount,
24
- title: groupTitle,
25
- groupContent,
26
- imageUrl: groupImageUrl,
27
- groupNumber,
28
- groups: answer?.groups || initialData?.groups,
29
- };
30
- },
31
- getExtraProps: (ctx) => ({
32
- groups: ctx.groups,
33
- questionIndexInPart: ctx.questionIndexInPart,
34
- partId: ctx.partId,
35
- validationRef: ctx.validationRef,
36
- onUnsavedChangesChange: ctx.onUnsavedChangesChange,
37
- }),
38
- wrapOnSave: (data, ctx) => ({
39
- type: ctx.questionType,
40
- content: ctx.state.content,
41
- points: data?.points ?? ctx.state.points ?? 1,
42
- level: ctx.state.level,
43
- difficulty: ctx.state.difficulty,
44
- skill: ctx.state.skill,
45
- answer: data,
46
- explanation: data?.explanation || '',
47
- }),
48
- wrapOnChange: (data, ctx) => ({
49
- type: ctx.questionType,
50
- content: ctx.state.content,
51
- points: data?.points ?? ctx.state.points ?? 1,
52
- level: ctx.state.level,
53
- difficulty: ctx.state.difficulty,
54
- skill: ctx.state.skill,
55
- answer: data,
56
- explanation: data?.explanation || '',
57
- }),
58
- };
1
+ import{WriteSentencesCreator as t}from"./WriteSentencesCreator";export const writeSentencesRegistration={component:t,transformInitialData:t=>{const n=t?.answer||{},e=t?.explanation||n?.explanation||"",o=t?.total_points?parseFloat(t.total_points):t?.points||1,a=t?.questionGroup||{},i=a?.payload||{},s=i?.title||n?.title||"",r=i?.content||n?.groupContent||"",p=i?.imageUrl||n?.imageUrl||"",l=a?.groupNumber||n?.groupNumber||1,u=t?.content||{},c=n?.instruction||u?.instruction||"",g=n?.sentenceCount||u?.sentenceCount||void 0;return{points:n?.points||o,explanation:e,instruction:c,sentenceCount:g,title:s,groupContent:r,imageUrl:p,groupNumber:l,groups:n?.groups||t?.groups}},getExtraProps:t=>({groups:t.groups,questionIndexInPart:t.questionIndexInPart,partId:t.partId,validationRef:t.validationRef,onUnsavedChangesChange:t.onUnsavedChangesChange}),wrapOnSave:(t,n)=>({type:n.questionType,content:n.state.content,points:t?.points??n.state.points??1,level:n.state.level,difficulty:n.state.difficulty,skill:n.state.skill,answer:t,explanation:t?.explanation||""}),wrapOnChange:(t,n)=>({type:n.questionType,content:n.state.content,points:t?.points??n.state.points??1,level:n.state.level,difficulty:n.state.difficulty,skill:n.state.skill,answer:t,explanation:t?.explanation||""})};
@@ -1,28 +1 @@
1
- export const transformWriteSentences = (question) => {
2
- const answerData = question.answer;
3
- const hasGroupData = !!(answerData?.title || answerData?.subtitle || answerData?.groupContent || answerData?.imageUrl);
4
- const basicGroupData = hasGroupData
5
- ? {
6
- title: answerData.title || '',
7
- subtitle: answerData.subtitle || '',
8
- content: answerData.groupContent || '',
9
- imageUrl: answerData.imageUrl || '',
10
- }
11
- : undefined;
12
- const groupNumber = answerData?.groupNumber || 1;
13
- const apiContent = {};
14
- if (answerData?.instruction) {
15
- apiContent.instruction = answerData.instruction;
16
- }
17
- if (answerData?.sentenceCount) {
18
- apiContent.sentenceCount = answerData.sentenceCount;
19
- }
20
- return {
21
- apiContent,
22
- apiCorrectAnswer: {},
23
- totalPoints: answerData?.points || question.points || 1,
24
- explanation: answerData?.explanation || question.explanation || '',
25
- basicGroupData,
26
- groupNumber,
27
- };
28
- };
1
+ export const transformWriteSentences=t=>{const n=t.answer,e=!!(n?.title||n?.subtitle||n?.groupContent||n?.imageUrl)?{title:n.title||"",subtitle:n.subtitle||"",content:n.groupContent||"",imageUrl:n.imageUrl||""}:void 0,o=n?.groupNumber||1,i={};return n?.instruction&&(i.instruction=n.instruction),n?.sentenceCount&&(i.sentenceCount=n.sentenceCount),{apiContent:i,apiCorrectAnswer:{},totalPoints:n?.points||t.points||1,explanation:n?.explanation||t.explanation||"",basicGroupData:e,groupNumber:o}};
@@ -1,96 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState, useEffect, useMemo } from 'react';
4
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
5
- import { Textarea } from '../../../../components/ui/textarea';
6
- import { Label } from '../../../../components/ui/label';
7
- import { Image, Music, FileText, AlertCircle } from 'lucide-react';
8
- import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
9
- export function WriteShortParagraphClient({ questionData, onSubmit, isReviewMode = false, userAnswer, }) {
10
- const { contexts, minWords, maxWords, instruction } = questionData;
11
- // Initialize answer state
12
- const [answer, setAnswer] = useState(userAnswer || '');
13
- // Sync userAnswer when it changes (e.g., when loading from server in review mode)
14
- useEffect(() => {
15
- if (userAnswer !== undefined) {
16
- setAnswer(userAnswer);
17
- }
18
- }, [userAnswer]);
19
- // Count words
20
- const wordCount = useMemo(() => {
21
- if (!answer.trim())
22
- return 0;
23
- return answer
24
- .trim()
25
- .split(/\s+/)
26
- .filter((word) => word.length > 0).length;
27
- }, [answer]);
28
- // Check if word count is valid
29
- const isValidWordCount = useMemo(() => {
30
- if (wordCount < minWords)
31
- return false;
32
- if (maxWords && wordCount > maxWords)
33
- return false;
34
- return true;
35
- }, [wordCount, minWords, maxWords]);
36
- // Handle answer change
37
- const handleAnswerChange = (value) => {
38
- if (isReviewMode)
39
- return;
40
- setAnswer(value);
41
- onSubmit?.(value);
42
- };
43
- // Get icon for context type
44
- const getContextIcon = (type) => {
45
- switch (type) {
46
- case 'image':
47
- return _jsx(Image, { className: "h-5 w-5" });
48
- case 'audio':
49
- return _jsx(Music, { className: "h-5 w-5" });
50
- case 'text':
51
- return _jsx(FileText, { className: "h-5 w-5" });
52
- }
53
- };
54
- // Get label for context type
55
- const getContextLabel = (type) => {
56
- switch (type) {
57
- case 'image':
58
- return 'Ảnh';
59
- case 'audio':
60
- return 'Audio';
61
- case 'text':
62
- return 'Text';
63
- }
64
- };
65
- return (_jsxs("div", { className: "space-y-6", children: [instruction && (_jsx(Card, { children: _jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-lg", children: instruction }) }) })), contexts.length > 0 && (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: "Contexts" }) }), _jsx(CardContent, { children: _jsx("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3", children: contexts.map((ctx, index) => (_jsx(ContextDisplay, { context: ctx, index: index }, ctx.id))) }) })] })), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(CardTitle, { children: "Vi\u1EBFt b\u00E0i" }), _jsx("div", { className: "flex items-center gap-2", children: _jsxs("span", { className: `text-sm font-semibold ${isValidWordCount ? 'text-green-600' : 'text-red-600'}`, children: [wordCount, " / ", minWords, maxWords ? ` - ${maxWords}` : '+', " t\u1EEB"] }) })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: "writing-answer", children: "N\u1ED9i dung b\u00E0i vi\u1EBFt" }), _jsx(Textarea, { id: "writing-answer", value: answer, onChange: (e) => handleAnswerChange(e.target.value), placeholder: "Nh\u1EADp n\u1ED9i dung b\u00E0i vi\u1EBFt c\u1EE7a b\u1EA1n...", className: "mt-2 min-h-[200px]", rows: 10, disabled: isReviewMode })] }), !isReviewMode && (_jsx("div", { className: "flex items-start gap-2 rounded-lg border p-3", children: isValidWordCount ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "mt-0.5 h-5 w-5 rounded-full bg-green-500" }), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-sm font-medium text-green-700", children: "\u0110\u00E3 \u0111\u1EE7 s\u1ED1 t\u1EEB y\u00EAu c\u1EA7u" }), _jsxs("p", { className: "text-xs text-green-600", children: ["B\u1EA1n \u0111\u00E3 vi\u1EBFt ", wordCount, " t\u1EEB. Y\u00EAu c\u1EA7u: ", minWords, maxWords ? ` - ${maxWords}` : '+', " t\u1EEB."] })] })] })) : (_jsxs(_Fragment, { children: [_jsx(AlertCircle, { className: "h-5 w-5 text-amber-500" }), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-sm font-medium text-amber-700", children: "Ch\u01B0a \u0111\u1EE7 s\u1ED1 t\u1EEB y\u00EAu c\u1EA7u" }), _jsxs("p", { className: "text-xs text-amber-600", children: ["B\u1EA1n \u0111\u00E3 vi\u1EBFt ", wordCount, " t\u1EEB. C\u1EA7n \u00EDt nh\u1EA5t ", minWords, " t\u1EEB.", maxWords && wordCount > maxWords && _jsxs("span", { children: [" T\u1ED1i \u0111a ", maxWords, " t\u1EEB."] })] })] })] })) }))] })] })] }));
66
- }
67
- function ContextDisplay({ context, index }) {
68
- const { previewUrl } = usePresignedFileUrl(context.type !== 'text' ? context.url || '' : '');
69
- const getIcon = () => {
70
- switch (context.type) {
71
- case 'image':
72
- return _jsx(Image, { className: "h-5 w-5" });
73
- case 'audio':
74
- return _jsx(Music, { className: "h-5 w-5" });
75
- case 'text':
76
- return _jsx(FileText, { className: "h-5 w-5" });
77
- }
78
- };
79
- const getLabel = () => {
80
- switch (context.type) {
81
- case 'image':
82
- return 'Ảnh';
83
- case 'audio':
84
- return 'Audio';
85
- case 'text':
86
- return 'Text';
87
- }
88
- };
89
- return (_jsxs("div", { className: "rounded-lg border bg-white p-4 shadow-sm", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [getIcon(), _jsxs("span", { className: "text-sm font-semibold text-gray-700", children: [getLabel(), " ", index + 1] })] }), context.type === 'image' && (_jsx("div", { className: "relative h-48 w-full overflow-hidden rounded border bg-gray-50", children: previewUrl ? (
90
- /* eslint-disable-next-line @next/next/no-img-element */
91
- _jsx("img", { src: previewUrl, alt: `Context ${index + 1}`, className: "h-full w-full object-contain", onError: () => {
92
- console.error('Failed to load image:', context.url);
93
- } })) : (_jsx("div", { className: "flex h-full items-center justify-center text-gray-400", children: _jsx(Image, { className: "h-12 w-12" }) })) })), context.type === 'audio' && (_jsx("div", { className: "space-y-2", children: _jsx("div", { className: "rounded border bg-gray-50 p-3", children: _jsx("audio", { controls: true, className: "w-full", src: previewUrl || context.url || '', onError: () => {
94
- console.error('Failed to load audio:', context.url);
95
- }, children: "Your browser does not support the audio element." }) }) })), context.type === 'text' && (_jsx("div", { className: "rounded border bg-gray-50 p-3", children: _jsx("p", { className: "text-sm whitespace-pre-wrap text-gray-700", children: context.text }) }))] }));
96
- }
1
+ "use client";import{jsx as e,jsxs as r,Fragment as t}from"react/jsx-runtime";import{useState as i,useEffect as s,useMemo as a}from"react";import{Card as l,CardContent as n,CardHeader as c,CardTitle as d}from"../../../../components/ui/card";import{Textarea as o}from"../../../../components/ui/textarea";import{Label as m}from"../../../../components/ui/label";import{Image as h,Music as u,FileText as p,AlertCircle as x}from"lucide-react";import{usePresignedFileUrl as g}from"../../../../shared/lib/hooks";export function WriteShortParagraphClient({questionData:h,onSubmit:u,isReviewMode:p=!1,userAnswer:g}){const{contexts:f,minWords:v,maxWords:b,instruction:w}=h,[y,j]=i(g||"");s(()=>{void 0!==g&&j(g)},[g]);const C=a(()=>y.trim()?y.trim().split(/\s+/).filter(e=>e.length>0).length:0,[y]),F=a(()=>!(C<v)&&!(b&&C>b),[C,v,b]);return r("div",{className:"space-y-6",children:[w&&e(l,{children:e(c,{children:e(d,{className:"text-lg",children:w})})}),f.length>0&&r(l,{children:[e(c,{children:e(d,{children:"Contexts"})}),e(n,{children:e("div",{className:"grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3",children:f.map((r,t)=>e(N,{context:r,index:t},r.id))})})]}),r(l,{children:[e(c,{children:r("div",{className:"flex items-center justify-between",children:[e(d,{children:"Viết bài"}),e("div",{className:"flex items-center gap-2",children:r("span",{className:"text-sm font-semibold "+(F?"text-green-600":"text-red-600"),children:[C," / ",v,b?` - ${b}`:"+"," từ"]})})]})}),r(n,{className:"space-y-4",children:[r("div",{children:[e(m,{htmlFor:"writing-answer",children:"Nội dung bài viết"}),e(o,{id:"writing-answer",value:y,onChange:e=>{return r=e.target.value,void(p||(j(r),u?.(r)));var r},placeholder:"Nhập nội dung bài viết của bạn...",className:"mt-2 min-h-[200px]",rows:10,disabled:p})]}),!p&&e("div",{className:"flex items-start gap-2 rounded-lg border p-3",children:r(t,F?{children:[e("div",{className:"mt-0.5 h-5 w-5 rounded-full bg-green-500"}),r("div",{className:"flex-1",children:[e("p",{className:"text-sm font-medium text-green-700",children:"Đã đủ số từ yêu cầu"}),r("p",{className:"text-xs text-green-600",children:["Bạn đã viết ",C," từ. Yêu cầu: ",v,b?` - ${b}`:"+"," từ."]})]})]}:{children:[e(x,{className:"h-5 w-5 text-amber-500"}),r("div",{className:"flex-1",children:[e("p",{className:"text-sm font-medium text-amber-700",children:"Chưa đủ số từ yêu cầu"}),r("p",{className:"text-xs text-amber-600",children:["Bạn đã viết ",C," từ. Cần ít nhất ",v," từ.",b&&C>b&&r("span",{children:[" Tối đa ",b," từ."]})]})]})]})})]})]})]})}function N({context:t,index:i}){const{previewUrl:s}=g("text"!==t.type&&t.url||"");return r("div",{className:"rounded-lg border bg-white p-4 shadow-sm",children:[r("div",{className:"mb-2 flex items-center gap-2",children:[(()=>{switch(t.type){case"image":return e(h,{className:"h-5 w-5"});case"audio":return e(u,{className:"h-5 w-5"});case"text":return e(p,{className:"h-5 w-5"})}})(),r("span",{className:"text-sm font-semibold text-gray-700",children:[(()=>{switch(t.type){case"image":return"Ảnh";case"audio":return"Audio";case"text":return"Text"}})()," ",i+1]})]}),"image"===t.type&&e("div",{className:"relative h-48 w-full overflow-hidden rounded border bg-gray-50",children:s?e("img",{src:s,alt:`Context ${i+1}`,className:"h-full w-full object-contain",onError:()=>{console.error("Failed to load image:",t.url)}}):e("div",{className:"flex h-full items-center justify-center text-gray-400",children:e(h,{className:"h-12 w-12"})})}),"audio"===t.type&&e("div",{className:"space-y-2",children:e("div",{className:"rounded border bg-gray-50 p-3",children:e("audio",{controls:!0,className:"w-full",src:s||t.url||"",onError:()=>{console.error("Failed to load audio:",t.url)},children:"Your browser does not support the audio element."})})}),"text"===t.type&&e("div",{className:"rounded border bg-gray-50 p-3",children:e("p",{className:"text-sm whitespace-pre-wrap text-gray-700",children:t.text})})]})}
@@ -1,161 +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 { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
9
- import { Pencil, Eye, X, Image, Music, FileText } from 'lucide-react';
10
- import { FileUpload } from '../../../../components/ui/file-upload';
11
- import { WriteShortParagraphClient } from './WriteShortParagraphClient';
12
- import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
13
- function WriteShortParagraphCreatorContent({ initialData, onSave, // eslint-disable-line @typescript-eslint/no-unused-vars
14
- onCancel, // eslint-disable-line @typescript-eslint/no-unused-vars
15
- onChange, }) {
16
- // State
17
- const [contexts, setContexts] = useState(initialData?.contexts || []);
18
- const [minWords, setMinWords] = useState(initialData?.minWords || 20);
19
- const [maxWords, setMaxWords] = useState(initialData?.maxWords || undefined);
20
- const [instruction, setInstruction] = useState(initialData?.instruction || '');
21
- // Track previous initialData to detect changes
22
- const previousInitialDataRef = useRef(initialData);
23
- const isInitializingRef = useRef(false);
24
- const hasInitializedRef = useRef(false);
25
- // Sync state from initialData when it changes
26
- useEffect(() => {
27
- const initialDataChanged = JSON.stringify(previousInitialDataRef.current) !== JSON.stringify(initialData);
28
- if (initialDataChanged) {
29
- isInitializingRef.current = true;
30
- previousInitialDataRef.current = initialData;
31
- if (initialData) {
32
- if (initialData.contexts !== undefined)
33
- setContexts(initialData.contexts);
34
- if (initialData.minWords !== undefined)
35
- setMinWords(initialData.minWords);
36
- if (initialData.maxWords !== undefined)
37
- setMaxWords(initialData.maxWords);
38
- if (initialData.instruction !== undefined)
39
- setInstruction(initialData.instruction);
40
- hasInitializedRef.current = true;
41
- }
42
- else {
43
- // Only reset if not the first mount
44
- if (hasInitializedRef.current) {
45
- setContexts([]);
46
- setMinWords(20);
47
- setMaxWords(undefined);
48
- setInstruction('');
49
- }
50
- }
51
- setTimeout(() => {
52
- isInitializingRef.current = false;
53
- }, 100);
54
- }
55
- }, [initialData]);
56
- // Track previous data to avoid unnecessary onChange calls
57
- const previousDataRef = useRef(undefined);
58
- const onChangeRef = useRef(onChange);
59
- // Keep onChange ref up to date
60
- useEffect(() => {
61
- onChangeRef.current = onChange;
62
- }, [onChange]);
63
- useEffect(() => {
64
- if (isInitializingRef.current)
65
- return;
66
- const currentData = {
67
- contexts,
68
- minWords,
69
- maxWords,
70
- instruction,
71
- };
72
- // Only call onChange if data actually changed
73
- const dataChanged = JSON.stringify(previousDataRef.current) !== JSON.stringify(currentData);
74
- if (dataChanged) {
75
- previousDataRef.current = currentData;
76
- // Only call onChange if there's meaningful data (at least 1 context with valid data)
77
- const hasValidContext = contexts.some((ctx) => {
78
- if (ctx.type === 'text') {
79
- return ctx.text && ctx.text.trim().length > 0;
80
- }
81
- return ctx.url && ctx.url.trim().length > 0;
82
- });
83
- if (hasValidContext) {
84
- onChangeRef.current?.(currentData);
85
- }
86
- }
87
- }, [contexts, minWords, maxWords, instruction]);
88
- // UI State
89
- const [isClientMode, setIsClientMode] = useState(false);
90
- // Add new context
91
- const addContext = (type) => {
92
- const newContext = {
93
- id: `context-${Date.now()}-${Math.random()}`,
94
- type,
95
- url: type !== 'text' ? '' : undefined,
96
- text: type === 'text' ? '' : undefined,
97
- };
98
- setContexts([...contexts, newContext]);
99
- };
100
- // Remove context
101
- const removeContext = (id) => {
102
- setContexts(contexts.filter((ctx) => ctx.id !== id));
103
- };
104
- // Update context
105
- const updateContext = (id, field, value) => {
106
- setContexts(contexts.map((ctx) => (ctx.id === id ? { ...ctx, [field]: value } : ctx)));
107
- };
108
- // If in client mode, render the Client component
109
- if (isClientMode) {
110
- const questionData = {
111
- contexts,
112
- minWords,
113
- maxWords,
114
- instruction,
115
- };
116
- 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-blue-500 via-indigo-500 to-purple-500" }), _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold tracking-tight text-gray-900", children: "Write Short Paragraph (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(WriteShortParagraphClient, { questionData: questionData, onSubmit: () => { } })] }));
117
- }
118
- 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-blue-500 via-indigo-500 to-purple-500" }), _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold tracking-tight text-gray-900", children: "Write Short Paragraph" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "T\u1EA1o b\u00E0i t\u1EADp vi\u1EBFt \u0111o\u1EA1n v\u0103n v\u1EDBi nhi\u1EC1u content (\u1EA3nh, audio, text)" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: contexts.length === 0 || minWords < 1, children: [_jsx(Eye, { className: "mr-2 h-4 w-4" }), "Client Mode"] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "instruction", className: "text-sm font-semibold text-gray-700", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Textarea, { id: "instruction", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: "V\u00ED d\u1EE5: Look at the three pictures. Write about this story.", className: "mt-1", rows: 2 })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "minWords", className: "text-sm font-semibold text-gray-700", children: "S\u1ED1 t\u1EEB t\u1ED1i thi\u1EC3u *" }), _jsx(Input, { id: "minWords", type: "number", min: "1", value: minWords, onChange: (e) => setMinWords(parseInt(e.target.value) || 1), placeholder: "20", className: "mt-1" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "maxWords", className: "text-sm font-semibold text-gray-700", children: "S\u1ED1 t\u1EEB t\u1ED1i \u0111a (t\u00F9y ch\u1ECDn)" }), _jsx(Input, { id: "maxWords", type: "number", min: minWords, value: maxWords || '', onChange: (e) => setMaxWords(e.target.value ? parseInt(e.target.value) : undefined), placeholder: "Kh\u00F4ng gi\u1EDBi h\u1EA1n", className: "mt-1" })] })] })] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-xl shadow-blue-100/40 ring-1 ring-gray-100", children: [_jsx(CardHeader, { className: "bg-gradient-to-br from-indigo-50/50 via-white to-blue-50/30 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-blue-500 to-indigo-600 shadow-md", children: _jsx(FileText, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-lg font-bold text-gray-900", children: "Contexts" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Th\u00EAm c\u00E1c context (\u1EA3nh, audio, ho\u1EB7c text) \u0111\u1EC3 h\u1ECDc sinh xem khi vi\u1EBFt" })] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => addContext('image'), className: "flex items-center gap-2 border-indigo-200 bg-indigo-50 text-indigo-700 hover:bg-indigo-100 hover:text-indigo-800", children: [_jsx(Image, { className: "h-4 w-4" }), "Th\u00EAm \u1EA3nh"] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => addContext('audio'), className: "flex items-center gap-2 border-purple-200 bg-purple-50 text-purple-700 hover:bg-purple-100 hover:text-purple-800", children: [_jsx(Music, { className: "h-4 w-4" }), "Th\u00EAm audio"] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => addContext('text'), className: "flex items-center gap-2 border-blue-200 bg-blue-50 text-blue-700 hover:bg-blue-100 hover:text-blue-800", children: [_jsx(FileText, { className: "h-4 w-4" }), "Th\u00EAm text"] })] })] }) }), _jsx(CardContent, { className: "space-y-4 pt-6", children: contexts.length > 0 ? (contexts.map((ctx, index) => (_jsx(ContextItem, { context: ctx, index: index, onUpdate: updateContext, onRemove: removeContext }, ctx.id)))) : (_jsxs("div", { className: "flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-gray-300 bg-gray-50 p-12", children: [_jsx("div", { className: "mb-4 rounded-full bg-gray-200 p-4", children: _jsx(Image, { className: "h-8 w-8 text-gray-400" }) }), _jsx("p", { className: "mb-2 text-sm font-medium text-gray-700", children: "Ch\u01B0a c\u00F3 context n\u00E0o" }), _jsx("p", { className: "mb-4 text-xs text-gray-500", children: "Nh\u1EA5n c\u00E1c n\u00FAt b\u00EAn tr\u00EAn \u0111\u1EC3 th\u00EAm context" })] })) })] })] }));
119
- }
120
- function ContextItem({ context, index, onUpdate, onRemove }) {
121
- const { previewUrl } = usePresignedFileUrl(context.type !== 'text' ? context.url || '' : '');
122
- const getIcon = () => {
123
- switch (context.type) {
124
- case 'image':
125
- return _jsx(Image, { className: "h-5 w-5" });
126
- case 'audio':
127
- return _jsx(Music, { className: "h-5 w-5" });
128
- case 'text':
129
- return _jsx(FileText, { className: "h-5 w-5" });
130
- }
131
- };
132
- const getLabel = () => {
133
- switch (context.type) {
134
- case 'image':
135
- return 'Ảnh';
136
- case 'audio':
137
- return 'Audio';
138
- case 'text':
139
- return 'Text';
140
- }
141
- };
142
- return (_jsx("div", { className: "rounded-lg border bg-white p-4 shadow-sm transition-shadow hover:shadow-md", children: _jsxs("div", { className: "flex items-start gap-4", children: [_jsx("div", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-white shadow-md", children: _jsx("span", { className: "text-sm font-bold", children: index + 1 }) }), _jsxs("div", { className: "flex-1 space-y-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [getIcon(), _jsxs(Label, { className: "text-sm font-semibold text-gray-700", children: ["Context ", index + 1, " - ", getLabel()] })] }), context.type === 'text' ? (_jsxs("div", { children: [_jsx(Label, { className: "text-sm font-semibold text-gray-700", children: "N\u1ED9i dung text *" }), _jsx(Textarea, { value: context.text || '', onChange: (e) => onUpdate(context.id, 'text', e.target.value), placeholder: "Nh\u1EADp n\u1ED9i dung text...", className: "mt-1", rows: 3 })] })) : (_jsx("div", { children: _jsx(FileUpload, { id: `context-${context.id}`, label: context.type === 'image' ? 'URL ảnh *' : 'URL audio *', accept: context.type === 'image' ? 'image/*' : 'audio/*', value: context.url || '', onChange: (url) => onUpdate(context.id, 'url', url), maxSize: context.type === 'image' ? 5 : 10, placeholder: "Paste URL ho\u1EB7c upload file", autoUpload: true, prefix: "questions" }) })), context.type === 'image' && previewUrl && (_jsx("div", { className: "relative h-48 w-full overflow-hidden rounded border", children: _jsx("img", { src: previewUrl, alt: `Context ${index + 1} preview`, className: "h-full w-full object-contain", onError: () => {
143
- console.error('Failed to load image:', context.url);
144
- } }) })), context.type === 'audio' && context.url && (_jsx("div", { className: "rounded border bg-gray-50 p-3", children: _jsx("audio", { controls: true, className: "w-full", src: previewUrl || context.url, children: "Your browser does not support the audio element." }) }))] }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => onRemove(context.id), className: "flex-shrink-0 text-gray-400 hover:text-red-500", children: _jsx(X, { className: "h-5 w-5" }) })] }) }));
145
- }
146
- export function WriteShortParagraphCreator({ initialData, onSave, onCancel, onChange, }) {
147
- const handleSave = () => {
148
- // Validation
149
- if (!initialData?.contexts || initialData.contexts.length === 0) {
150
- return;
151
- }
152
- if (!initialData.minWords || initialData.minWords < 1) {
153
- return;
154
- }
155
- onSave?.(initialData);
156
- };
157
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(WriteShortParagraphCreatorContent, { initialData: initialData, onSave: handleSave, onCancel: onCancel, onChange: onChange }), _jsxs("div", { className: "flex justify-end gap-3", children: [_jsx(Button, { variant: "outline", onClick: onCancel, children: "H\u1EE7y" }), _jsx(Button, { onClick: handleSave, className: "bg-blue-500 hover:bg-blue-600", disabled: !initialData?.contexts ||
158
- initialData.contexts.length === 0 ||
159
- !initialData.minWords ||
160
- initialData.minWords < 1, children: "L\u01B0u c\u00E2u h\u1ECFi" })] })] }));
161
- }
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useState as i,useRef as r,useEffect as a}from"react";import{Button as n}from"../../../../components/ui/button";import{Label as s}from"../../../../components/ui/label";import{Input as o}from"../../../../components/ui/input";import{Textarea as l}from"../../../../components/ui/textarea";import{Card as c,CardContent as d,CardHeader as m,CardTitle as h}from"../../../../components/ui/card";import{Pencil as u,Eye as g,X as x,Image as p,Music as v,FileText as b}from"lucide-react";import{FileUpload as f}from"../../../../components/ui/file-upload";import{WriteShortParagraphClient as N}from"./WriteShortParagraphClient";import{usePresignedFileUrl as y}from"../../../../shared/lib/hooks";function w({initialData:x,onSave:f,onCancel:y,onChange:w}){const[W,k]=i(x?.contexts||[]),[S,j]=i(x?.minWords||20),[z,T]=i(x?.maxWords||void 0),[U,P]=i(x?.instruction||""),D=r(x),L=r(!1),R=r(!1);a(()=>{JSON.stringify(D.current)!==JSON.stringify(x)&&(L.current=!0,D.current=x,x?(void 0!==x.contexts&&k(x.contexts),void 0!==x.minWords&&j(x.minWords),void 0!==x.maxWords&&T(x.maxWords),void 0!==x.instruction&&P(x.instruction),R.current=!0):R.current&&(k([]),j(20),T(void 0),P("")),setTimeout(()=>{L.current=!1},100))},[x]);const F=r(void 0),J=r(w);a(()=>{J.current=w},[w]),a(()=>{if(L.current)return;const e={contexts:W,minWords:S,maxWords:z,instruction:U};if(JSON.stringify(F.current)!==JSON.stringify(e)){F.current=e;W.some(e=>"text"===e.type?e.text&&e.text.trim().length>0:e.url&&e.url.trim().length>0)&&J.current?.(e)}},[W,S,z,U]);const[O,$]=i(!1),M=e=>{const t={id:`context-${Date.now()}-${Math.random()}`,type:e,url:"text"!==e?"":void 0,text:"text"===e?"":void 0};k([...W,t])},q=e=>{k(W.filter(t=>t.id!==e))},E=(e,t,i)=>{k(W.map(r=>r.id===e?{...r,[t]:i}:r))};if(O){const i={contexts:W,minWords:S,maxWords:z,instruction:U};return t("div",{className:"space-y-6",children:[t(c,{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-blue-500 via-indigo-500 to-purple-500"}),e(m,{children:t("div",{className:"flex items-center justify-between",children:[t("div",{children:[e(h,{className:"text-xl font-bold tracking-tight text-gray-900",children:"Write Short Paragraph (Preview)"}),e("p",{className:"mt-1 text-sm text-gray-500",children:"Chế độ xem trước"})]}),t(n,{variant:"outline",size:"sm",onClick:()=>$(!1),children:[e(u,{className:"mr-2 h-4 w-4"}),"Quay lại Edit Mode"]})]})})]}),e(N,{questionData:i,onSubmit:()=>{}})]})}return t("div",{className:"space-y-6",children:[t(c,{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-blue-500 via-indigo-500 to-purple-500"}),e(m,{children:t("div",{className:"flex items-center justify-between",children:[t("div",{children:[e(h,{className:"text-xl font-bold tracking-tight text-gray-900",children:"Write Short Paragraph"}),e("p",{className:"mt-1 text-sm text-gray-500",children:"Tạo bài tập viết đoạn văn với nhiều content (ảnh, audio, text)"})]}),t(n,{variant:"outline",size:"sm",onClick:()=>$(!0),disabled:0===W.length||S<1,children:[e(g,{className:"mr-2 h-4 w-4"}),"Client Mode"]})]})}),t(d,{className:"space-y-4",children:[t("div",{className:"space-y-1.5",children:[e(s,{htmlFor:"instruction",className:"text-sm font-semibold text-gray-700",children:"Hướng dẫn"}),e(l,{id:"instruction",value:U,onChange:e=>P(e.target.value),placeholder:"Ví dụ: Look at the three pictures. Write about this story.",className:"mt-1",rows:2})]}),t("div",{className:"grid grid-cols-2 gap-4",children:[t("div",{className:"space-y-1.5",children:[e(s,{htmlFor:"minWords",className:"text-sm font-semibold text-gray-700",children:"Số từ tối thiểu *"}),e(o,{id:"minWords",type:"number",min:"1",value:S,onChange:e=>j(parseInt(e.target.value)||1),placeholder:"20",className:"mt-1"})]}),t("div",{className:"space-y-1.5",children:[e(s,{htmlFor:"maxWords",className:"text-sm font-semibold text-gray-700",children:"Số từ tối đa (tùy chọn)"}),e(o,{id:"maxWords",type:"number",min:S,value:z||"",onChange:e=>T(e.target.value?parseInt(e.target.value):void 0),placeholder:"Không giới hạn",className:"mt-1"})]})]})]})]}),t(c,{className:"overflow-hidden border-0 bg-white shadow-xl shadow-blue-100/40 ring-1 ring-gray-100",children:[e(m,{className:"bg-gradient-to-br from-indigo-50/50 via-white to-blue-50/30 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-blue-500 to-indigo-600 shadow-md",children:e(b,{className:"h-5 w-5 text-white"})}),t("div",{children:[e(h,{className:"text-lg font-bold text-gray-900",children:"Contexts"}),e("p",{className:"mt-1 text-sm text-gray-500",children:"Thêm các context (ảnh, audio, hoặc text) để học sinh xem khi viết"})]})]}),t("div",{className:"flex gap-2",children:[t(n,{variant:"outline",size:"sm",onClick:()=>M("image"),className:"flex items-center gap-2 border-indigo-200 bg-indigo-50 text-indigo-700 hover:bg-indigo-100 hover:text-indigo-800",children:[e(p,{className:"h-4 w-4"}),"Thêm ảnh"]}),t(n,{variant:"outline",size:"sm",onClick:()=>M("audio"),className:"flex items-center gap-2 border-purple-200 bg-purple-50 text-purple-700 hover:bg-purple-100 hover:text-purple-800",children:[e(v,{className:"h-4 w-4"}),"Thêm audio"]}),t(n,{variant:"outline",size:"sm",onClick:()=>M("text"),className:"flex items-center gap-2 border-blue-200 bg-blue-50 text-blue-700 hover:bg-blue-100 hover:text-blue-800",children:[e(b,{className:"h-4 w-4"}),"Thêm text"]})]})]})}),e(d,{className:"space-y-4 pt-6",children:W.length>0?W.map((t,i)=>e(C,{context:t,index:i,onUpdate:E,onRemove:q},t.id)):t("div",{className:"flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-gray-300 bg-gray-50 p-12",children:[e("div",{className:"mb-4 rounded-full bg-gray-200 p-4",children:e(p,{className:"h-8 w-8 text-gray-400"})}),e("p",{className:"mb-2 text-sm font-medium text-gray-700",children:"Chưa có context nào"}),e("p",{className:"mb-4 text-xs text-gray-500",children:"Nhấn các nút bên trên để thêm context"})]})})]})]})}function C({context:i,index:r,onUpdate:a,onRemove:o}){const{previewUrl:c}=y("text"!==i.type&&i.url||"");return e("div",{className:"rounded-lg border bg-white p-4 shadow-sm transition-shadow hover:shadow-md",children:t("div",{className:"flex items-start gap-4",children:[e("div",{className:"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-white shadow-md",children:e("span",{className:"text-sm font-bold",children:r+1})}),t("div",{className:"flex-1 space-y-3",children:[t("div",{className:"flex items-center gap-2",children:[(()=>{switch(i.type){case"image":return e(p,{className:"h-5 w-5"});case"audio":return e(v,{className:"h-5 w-5"});case"text":return e(b,{className:"h-5 w-5"})}})(),t(s,{className:"text-sm font-semibold text-gray-700",children:["Context ",r+1," - ",(()=>{switch(i.type){case"image":return"Ảnh";case"audio":return"Audio";case"text":return"Text"}})()]})]}),"text"===i.type?t("div",{children:[e(s,{className:"text-sm font-semibold text-gray-700",children:"Nội dung text *"}),e(l,{value:i.text||"",onChange:e=>a(i.id,"text",e.target.value),placeholder:"Nhập nội dung text...",className:"mt-1",rows:3})]}):e("div",{children:e(f,{id:`context-${i.id}`,label:"image"===i.type?"URL ảnh *":"URL audio *",accept:"image"===i.type?"image/*":"audio/*",value:i.url||"",onChange:e=>a(i.id,"url",e),maxSize:"image"===i.type?5:10,placeholder:"Paste URL hoặc upload file",autoUpload:!0,prefix:"questions"})}),"image"===i.type&&c&&e("div",{className:"relative h-48 w-full overflow-hidden rounded border",children:e("img",{src:c,alt:`Context ${r+1} preview`,className:"h-full w-full object-contain",onError:()=>{console.error("Failed to load image:",i.url)}})}),"audio"===i.type&&i.url&&e("div",{className:"rounded border bg-gray-50 p-3",children:e("audio",{controls:!0,className:"w-full",src:c||i.url,children:"Your browser does not support the audio element."})})]}),e(n,{type:"button",variant:"ghost",size:"icon",onClick:()=>o(i.id),className:"flex-shrink-0 text-gray-400 hover:text-red-500",children:e(x,{className:"h-5 w-5"})})]})})}export function WriteShortParagraphCreator({initialData:i,onSave:r,onCancel:a,onChange:s}){const o=()=>{i?.contexts&&0!==i.contexts.length&&(!i.minWords||i.minWords<1||r?.(i))};return t("div",{className:"space-y-6",children:[e(w,{initialData:i,onSave:o,onCancel:a,onChange:s}),t("div",{className:"flex justify-end gap-3",children:[e(n,{variant:"outline",onClick:a,children:"Hủy"}),e(n,{onClick:o,className:"bg-blue-500 hover:bg-blue-600",disabled:!i?.contexts||0===i.contexts.length||!i.minWords||i.minWords<1,children:"Lưu câu hỏi"})]})]})}
@@ -1,2 +1 @@
1
- export * from './WriteShortParagraphCreator';
2
- export * from './WriteShortParagraphClient';
1
+ export*from"./WriteShortParagraphCreator";export*from"./WriteShortParagraphClient";
@@ -1,4 +1 @@
1
- import { WriteShortParagraphCreator } from './WriteShortParagraphCreator';
2
- export const writeShortParagraphRegistration = {
3
- component: WriteShortParagraphCreator,
4
- };
1
+ import{WriteShortParagraphCreator as r}from"./WriteShortParagraphCreator";export const writeShortParagraphRegistration={component:r};
@@ -1,21 +1 @@
1
- export const transformWriteShortParagraph = (question) => {
2
- const answerData = question.answer;
3
- const contexts = (answerData?.contexts || []).map((ctx) => {
4
- if (ctx.type === 'text') {
5
- return { type: ctx.type, text: ctx.text || '' };
6
- }
7
- return { type: ctx.type, url: ctx.url || '' };
8
- });
9
- return {
10
- apiContent: {
11
- contexts,
12
- minWords: answerData?.minWords || 0,
13
- maxWords: answerData?.maxWords,
14
- instruction: answerData?.instruction,
15
- },
16
- apiCorrectAnswer: {
17
- minWords: answerData?.minWords || 0,
18
- maxWords: answerData?.maxWords || 100,
19
- },
20
- };
21
- };
1
+ export const transformWriteShortParagraph=t=>{const r=t.answer;return{apiContent:{contexts:(r?.contexts||[]).map(t=>"text"===t.type?{type:t.type,text:t.text||""}:{type:t.type,url:t.url||""}),minWords:r?.minWords||0,maxWords:r?.maxWords,instruction:r?.instruction},apiCorrectAnswer:{minWords:r?.minWords||0,maxWords:r?.maxWords||100}}};
@@ -1,36 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import Image from 'next/image';
4
- import { Badge } from '../../../components/ui/badge';
5
- import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../../components/ui/card';
6
- import { FileText, Music, Video, Image as ImageIcon, Volume2, Play } from 'lucide-react';
7
- export function QuestionGroupViewer({ group, showQuestions = true, compact = false, }) {
8
- const getGroupTypeIcon = () => {
9
- switch (group.type) {
10
- case 'READING_PASSAGE':
11
- return _jsx(FileText, { className: "h-5 w-5 text-blue-600" });
12
- case 'LISTENING_AUDIO':
13
- return _jsx(Music, { className: "h-5 w-5 text-purple-600" });
14
- case 'LISTENING_VIDEO':
15
- return _jsx(Video, { className: "h-5 w-5 text-red-600" });
16
- case 'IMAGE_BASED':
17
- return _jsx(ImageIcon, { className: "h-5 w-5 text-green-600" });
18
- default:
19
- return _jsx(FileText, { className: "h-5 w-5 text-gray-600" });
20
- }
21
- };
22
- const getGroupTypeLabel = () => {
23
- const labels = {
24
- READING_PASSAGE: 'Đọc hiểu - Đoạn văn',
25
- LISTENING_AUDIO: 'Nghe - Audio/Hội thoại',
26
- LISTENING_VIDEO: 'Nghe - Video',
27
- IMAGE_BASED: 'Dựa vào hình ảnh',
28
- MIXED: 'Kết hợp nhiều loại',
29
- };
30
- return labels[group.type] || group.type;
31
- };
32
- if (compact) {
33
- return (_jsxs("div", { className: "rounded-lg border border-gray-200 p-4 hover:bg-gray-50", children: [_jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "flex items-start gap-3", children: [getGroupTypeIcon(), _jsxs("div", { children: [_jsx("h3", { className: "font-semibold", children: group.name }), _jsx("p", { className: "text-sm text-gray-500", children: getGroupTypeLabel() })] })] }), _jsxs("div", { className: "flex flex-col items-end gap-1", children: [_jsxs(Badge, { className: "bg-blue-100 text-blue-700", children: [group.questionCount, " c\u00E2u h\u1ECFi"] }), _jsxs(Badge, { className: "bg-green-100 text-green-700", children: [group.totalPoints, " \u0111i\u1EC3m"] })] })] }), group.description && _jsx("p", { className: "mt-2 text-sm text-gray-600", children: group.description }), _jsxs("div", { className: "mt-3 flex flex-wrap gap-2", children: [_jsx(Badge, { variant: "outline", children: group.level }), _jsx(Badge, { variant: "outline", children: group.skill }), group.category && _jsx(Badge, { variant: "secondary", children: group.category }), group.tags?.map((tag) => (_jsx(Badge, { variant: "secondary", className: "bg-gray-100", children: tag }, tag)))] })] }));
34
- }
35
- return (_jsxs("div", { className: "space-y-6", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "flex items-start gap-3", children: [getGroupTypeIcon(), _jsxs("div", { children: [_jsx(CardTitle, { children: group.name }), _jsx(CardDescription, { className: "mt-1", children: getGroupTypeLabel() })] })] }), _jsxs("div", { className: "flex flex-col items-end gap-2", children: [_jsxs(Badge, { className: "bg-blue-100 text-blue-700", children: [group.questionCount, " c\u00E2u h\u1ECFi"] }), _jsxs(Badge, { className: "bg-green-100 text-green-700", children: ["T\u1ED5ng ", group.totalPoints, " \u0111i\u1EC3m"] })] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [group.description && _jsx("p", { className: "text-gray-700", children: group.description }), _jsxs("div", { className: "flex flex-wrap gap-2", children: [_jsx(Badge, { variant: "outline", children: group.level }), _jsx(Badge, { variant: "outline", children: group.skill }), group.category && _jsx(Badge, { variant: "secondary", children: group.category }), group.tags?.map((tag) => (_jsx(Badge, { variant: "secondary", className: "bg-gray-100", children: tag }, tag)))] }), group.allowShuffle && (_jsx("div", { className: "rounded-lg border border-yellow-200 bg-yellow-50 p-3", children: _jsx("p", { className: "text-sm text-yellow-800", children: "\u2139\uFE0F C\u00E2u h\u1ECFi trong nh\u00F3m n\u00E0y c\u00F3 th\u1EC3 \u0111\u01B0\u1EE3c x\u00E1o tr\u1ED9n th\u1EE9 t\u1EF1" }) }))] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsx(CardTitle, { children: "N\u1ED9i dung chung" }), _jsx(CardDescription, { children: "Context m\u00E0 t\u1EA5t c\u1EA3 c\u00E2u h\u1ECFi trong nh\u00F3m s\u1EBD s\u1EED d\u1EE5ng" })] }), _jsxs(CardContent, { className: "space-y-4", children: [group.context.instructions && (_jsxs("div", { className: "rounded-lg border border-blue-200 bg-blue-50 p-4", children: [_jsx("p", { className: "font-medium text-blue-900", children: "H\u01B0\u1EDBng d\u1EABn:" }), _jsx("p", { className: "mt-1 text-blue-800", children: group.context.instructions })] })), group.context.passage && (_jsxs("div", { className: "rounded-lg border p-4", children: [group.context.passageTitle && (_jsx("h4", { className: "mb-2 text-lg font-semibold", children: group.context.passageTitle })), _jsx("div", { className: "prose max-w-none", children: _jsx("p", { className: "whitespace-pre-wrap text-gray-700", children: group.context.passage }) })] })), group.context.audioUrl && (_jsxs("div", { className: "rounded-lg border p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(Volume2, { className: "h-5 w-5 text-purple-600" }), _jsx("h4", { className: "font-semibold", children: "Audio" })] }), _jsx("audio", { controls: true, className: "w-full", src: group.context.audioUrl, children: "Your browser does not support the audio element." }), group.context.audioScript && (_jsxs("div", { className: "mt-3", children: [_jsx("p", { className: "text-sm font-medium text-gray-600", children: "Transcript:" }), _jsx("p", { className: "mt-1 text-sm whitespace-pre-wrap text-gray-700", children: group.context.audioScript })] }))] })), group.context.videoUrl && (_jsxs("div", { className: "rounded-lg border p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(Play, { className: "h-5 w-5 text-red-600" }), _jsx("h4", { className: "font-semibold", children: "Video" })] }), _jsx("video", { controls: true, className: "w-full rounded", src: group.context.videoUrl, children: "Your browser does not support the video element." }), group.context.videoScript && (_jsxs("div", { className: "mt-3", children: [_jsx("p", { className: "text-sm font-medium text-gray-600", children: "Transcript:" }), _jsx("p", { className: "mt-1 text-sm whitespace-pre-wrap text-gray-700", children: group.context.videoScript })] }))] })), group.context.imageUrl && (_jsxs("div", { className: "rounded-lg border p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx(ImageIcon, { className: "h-5 w-5 text-green-600" }), _jsx("h4", { className: "font-semibold", children: "H\u00ECnh \u1EA3nh" })] }), _jsx("div", { className: "relative h-auto w-full", children: _jsx(Image, { src: group.context.imageUrl, alt: group.context.imageCaption || 'Group image', width: 800, height: 600, className: "max-w-full rounded", style: { width: '100%', height: 'auto' } }) }), group.context.imageCaption && (_jsx("p", { className: "mt-2 text-sm text-gray-600", children: group.context.imageCaption }))] }))] })] }), showQuestions && group.questions && group.questions.length > 0 && (_jsxs(Card, { children: [_jsxs(CardHeader, { children: [_jsxs(CardTitle, { children: ["C\u00E2u h\u1ECFi trong nh\u00F3m (", group.questions.length, ")"] }), _jsx(CardDescription, { children: "C\u00E1c c\u00E2u h\u1ECFi s\u1EBD s\u1EED d\u1EE5ng context chung b\u00EAn tr\u00EAn" })] }), _jsx(CardContent, { children: _jsx("div", { className: "space-y-3", children: group.questions.map((question, index) => (_jsx("div", { className: "rounded-lg border bg-white p-4", children: _jsx("div", { className: "flex items-start justify-between", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("span", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-blue-100 text-sm font-semibold text-blue-700", children: index + 1 }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "mb-2 flex flex-wrap gap-2", children: [_jsx(Badge, { variant: "outline", children: question.type }), _jsx(Badge, { variant: "outline", children: question.difficulty }), _jsxs(Badge, { className: "bg-green-100 text-green-700", children: [question.points, " \u0111i\u1EC3m"] })] }), _jsx("p", { className: "text-gray-800", children: question.content }), question.explanation && (_jsxs("p", { className: "mt-2 text-sm text-gray-500", children: ["\uD83D\uDCA1 ", question.explanation] }))] })] }) }) }, question.id))) }) })] }))] }));
36
- }
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import a from"next/image";import{Badge as l}from"../../../components/ui/badge";import{Card as s,CardContent as i,CardDescription as r,CardHeader as n,CardTitle as c}from"../../../components/ui/card";import{FileText as d,Music as o,Video as m,Image as h,Volume2 as p,Play as u}from"lucide-react";export function QuestionGroupViewer({group:g,showQuestions:x=!0,compact:N=!1}){const b=()=>{switch(g.type){case"READING_PASSAGE":return e(d,{className:"h-5 w-5 text-blue-600"});case"LISTENING_AUDIO":return e(o,{className:"h-5 w-5 text-purple-600"});case"LISTENING_VIDEO":return e(m,{className:"h-5 w-5 text-red-600"});case"IMAGE_BASED":return e(h,{className:"h-5 w-5 text-green-600"});default:return e(d,{className:"h-5 w-5 text-gray-600"})}},v=()=>({READING_PASSAGE:"Đọc hiểu - Đoạn văn",LISTENING_AUDIO:"Nghe - Audio/Hội thoại",LISTENING_VIDEO:"Nghe - Video",IMAGE_BASED:"Dựa vào hình ảnh",MIXED:"Kết hợp nhiều loại"}[g.type]||g.type);return t("div",N?{className:"rounded-lg border border-gray-200 p-4 hover:bg-gray-50",children:[t("div",{className:"flex items-start justify-between",children:[t("div",{className:"flex items-start gap-3",children:[b(),t("div",{children:[e("h3",{className:"font-semibold",children:g.name}),e("p",{className:"text-sm text-gray-500",children:v()})]})]}),t("div",{className:"flex flex-col items-end gap-1",children:[t(l,{className:"bg-blue-100 text-blue-700",children:[g.questionCount," câu hỏi"]}),t(l,{className:"bg-green-100 text-green-700",children:[g.totalPoints," điểm"]})]})]}),g.description&&e("p",{className:"mt-2 text-sm text-gray-600",children:g.description}),t("div",{className:"mt-3 flex flex-wrap gap-2",children:[e(l,{variant:"outline",children:g.level}),e(l,{variant:"outline",children:g.skill}),g.category&&e(l,{variant:"secondary",children:g.category}),g.tags?.map(t=>e(l,{variant:"secondary",className:"bg-gray-100",children:t},t))]})]}:{className:"space-y-6",children:[t(s,{children:[e(n,{children:t("div",{className:"flex items-start justify-between",children:[t("div",{className:"flex items-start gap-3",children:[b(),t("div",{children:[e(c,{children:g.name}),e(r,{className:"mt-1",children:v()})]})]}),t("div",{className:"flex flex-col items-end gap-2",children:[t(l,{className:"bg-blue-100 text-blue-700",children:[g.questionCount," câu hỏi"]}),t(l,{className:"bg-green-100 text-green-700",children:["Tổng ",g.totalPoints," điểm"]})]})]})}),t(i,{className:"space-y-4",children:[g.description&&e("p",{className:"text-gray-700",children:g.description}),t("div",{className:"flex flex-wrap gap-2",children:[e(l,{variant:"outline",children:g.level}),e(l,{variant:"outline",children:g.skill}),g.category&&e(l,{variant:"secondary",children:g.category}),g.tags?.map(t=>e(l,{variant:"secondary",className:"bg-gray-100",children:t},t))]}),g.allowShuffle&&e("div",{className:"rounded-lg border border-yellow-200 bg-yellow-50 p-3",children:e("p",{className:"text-sm text-yellow-800",children:"ℹ️ Câu hỏi trong nhóm này có thể được xáo trộn thứ tự"})})]})]}),t(s,{children:[t(n,{children:[e(c,{children:"Nội dung chung"}),e(r,{children:"Context mà tất cả câu hỏi trong nhóm sẽ sử dụng"})]}),t(i,{className:"space-y-4",children:[g.context.instructions&&t("div",{className:"rounded-lg border border-blue-200 bg-blue-50 p-4",children:[e("p",{className:"font-medium text-blue-900",children:"Hướng dẫn:"}),e("p",{className:"mt-1 text-blue-800",children:g.context.instructions})]}),g.context.passage&&t("div",{className:"rounded-lg border p-4",children:[g.context.passageTitle&&e("h4",{className:"mb-2 text-lg font-semibold",children:g.context.passageTitle}),e("div",{className:"prose max-w-none",children:e("p",{className:"whitespace-pre-wrap text-gray-700",children:g.context.passage})})]}),g.context.audioUrl&&t("div",{className:"rounded-lg border p-4",children:[t("div",{className:"mb-2 flex items-center gap-2",children:[e(p,{className:"h-5 w-5 text-purple-600"}),e("h4",{className:"font-semibold",children:"Audio"})]}),e("audio",{controls:!0,className:"w-full",src:g.context.audioUrl,children:"Your browser does not support the audio element."}),g.context.audioScript&&t("div",{className:"mt-3",children:[e("p",{className:"text-sm font-medium text-gray-600",children:"Transcript:"}),e("p",{className:"mt-1 text-sm whitespace-pre-wrap text-gray-700",children:g.context.audioScript})]})]}),g.context.videoUrl&&t("div",{className:"rounded-lg border p-4",children:[t("div",{className:"mb-2 flex items-center gap-2",children:[e(u,{className:"h-5 w-5 text-red-600"}),e("h4",{className:"font-semibold",children:"Video"})]}),e("video",{controls:!0,className:"w-full rounded",src:g.context.videoUrl,children:"Your browser does not support the video element."}),g.context.videoScript&&t("div",{className:"mt-3",children:[e("p",{className:"text-sm font-medium text-gray-600",children:"Transcript:"}),e("p",{className:"mt-1 text-sm whitespace-pre-wrap text-gray-700",children:g.context.videoScript})]})]}),g.context.imageUrl&&t("div",{className:"rounded-lg border p-4",children:[t("div",{className:"mb-2 flex items-center gap-2",children:[e(h,{className:"h-5 w-5 text-green-600"}),e("h4",{className:"font-semibold",children:"Hình ảnh"})]}),e("div",{className:"relative h-auto w-full",children:e(a,{src:g.context.imageUrl,alt:g.context.imageCaption||"Group image",width:800,height:600,className:"max-w-full rounded",style:{width:"100%",height:"auto"}})}),g.context.imageCaption&&e("p",{className:"mt-2 text-sm text-gray-600",children:g.context.imageCaption})]})]})]}),x&&g.questions&&g.questions.length>0&&t(s,{children:[t(n,{children:[t(c,{children:["Câu hỏi trong nhóm (",g.questions.length,")"]}),e(r,{children:"Các câu hỏi sẽ sử dụng context chung bên trên"})]}),e(i,{children:e("div",{className:"space-y-3",children:g.questions.map((a,s)=>e("div",{className:"rounded-lg border bg-white p-4",children:e("div",{className:"flex items-start justify-between",children:t("div",{className:"flex items-start gap-3",children:[e("span",{className:"flex h-8 w-8 items-center justify-center rounded-full bg-blue-100 text-sm font-semibold text-blue-700",children:s+1}),t("div",{className:"flex-1",children:[t("div",{className:"mb-2 flex flex-wrap gap-2",children:[e(l,{variant:"outline",children:a.type}),e(l,{variant:"outline",children:a.difficulty}),t(l,{className:"bg-green-100 text-green-700",children:[a.points," điểm"]})]}),e("p",{className:"text-gray-800",children:a.content}),a.explanation&&t("p",{className:"mt-2 text-sm text-gray-500",children:["💡 ",a.explanation]})]})]})})},a.id))})})]})]})}