@tinyweb_dev/oe-exam-sdk 1.0.6 → 1.0.8

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 (239) hide show
  1. package/dist/components/exams/ExamCreator.js +1 -16
  2. package/dist/components/exams/take/ExamTakingPageContainer.js +0 -12
  3. package/dist/components/exams/take/components/QuestionRenderer.js +2 -15
  4. package/dist/components/exams/take/components/question-renderers/index.d.ts +0 -3
  5. package/dist/components/exams/take/components/question-renderers/index.js +0 -3
  6. package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionRenderer.js +0 -4
  7. package/dist/components/exams/take/types/speaking.types.d.ts +0 -31
  8. package/dist/components/exams/take/types.d.ts +0 -65
  9. package/dist/components/exams/take/utils/answer-transformers.js +6 -60
  10. package/dist/components/exams/take/utils/question-transformers.d.ts +2 -15
  11. package/dist/components/exams/take/utils/question-transformers.js +0 -143
  12. package/dist/components/questions/_shared/components/compact/CompactCreatorHeader.d.ts +23 -0
  13. package/dist/components/questions/_shared/components/compact/CompactCreatorHeader.js +9 -0
  14. package/dist/components/questions/_shared/components/compact/CompactExampleToggle.d.ts +6 -0
  15. package/dist/components/questions/_shared/components/compact/CompactExampleToggle.js +8 -0
  16. package/dist/components/questions/_shared/components/compact/CompactExplanationToggle.d.ts +7 -0
  17. package/dist/components/questions/_shared/components/compact/CompactExplanationToggle.js +14 -0
  18. package/dist/components/questions/_shared/components/compact/CompactFieldCard.d.ts +9 -0
  19. package/dist/components/questions/_shared/components/compact/CompactFieldCard.js +7 -0
  20. package/dist/components/questions/_shared/components/compact/CompactPreviewBanner.d.ts +7 -0
  21. package/dist/components/questions/_shared/components/compact/CompactPreviewBanner.js +7 -0
  22. package/dist/components/questions/_shared/components/compact/CompactPreviewButton.d.ts +6 -0
  23. package/dist/components/questions/_shared/components/compact/CompactPreviewButton.js +7 -0
  24. package/dist/components/questions/_shared/components/compact/index.d.ts +6 -0
  25. package/dist/components/questions/_shared/components/compact/index.js +6 -0
  26. package/dist/components/questions/_shared/config/question-types.config.js +0 -24
  27. package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +0 -9
  28. package/dist/components/questions/_shared/types/choose-the-correct-answer-group.type.d.ts +2 -2
  29. package/dist/components/questions/_shared/types/choose-the-correct-answer-group.type.js +5 -1
  30. package/dist/components/questions/_shared/types/index.d.ts +0 -1
  31. package/dist/components/questions/_shared/types/index.js +0 -1
  32. package/dist/components/questions/_shared/types/look-and-choose-answer.type.d.ts +0 -13
  33. package/dist/components/questions/_shared/utils/answer-builder.js +0 -11
  34. package/dist/components/questions/components/ColorRegionCreator.js +66 -63
  35. package/dist/components/questions/components/QuestionSearchDropdown.js +0 -8
  36. package/dist/components/questions/components/QuestionTypeSelector.js +0 -2
  37. package/dist/components/questions/components/index.d.ts +1 -0
  38. package/dist/components/questions/components/index.js +1 -0
  39. package/dist/components/questions/creator/question-type-registry.js +0 -8
  40. package/dist/components/questions/groups/ArticlesGroupCard.js +2 -2
  41. package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -1
  42. package/dist/components/questions/groups/DocumentGroupCard.js +1 -1
  43. package/dist/components/questions/groups/ImageLabelGroupCard.js +1 -1
  44. package/dist/components/questions/groups/MatchAnswerGroupCard.js +2 -2
  45. package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +0 -3
  46. package/dist/components/questions/question-bank/QuestionEditorRenderer.js +1 -10
  47. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.d.ts +0 -2
  48. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.js +0 -41
  49. package/dist/components/questions/question-bank/editor-adapters/index.d.ts +2 -2
  50. package/dist/components/questions/question-bank/editor-adapters/index.js +2 -2
  51. package/dist/components/questions/question-bank/editor-adapters/media-adapters.d.ts +0 -1
  52. package/dist/components/questions/question-bank/editor-adapters/media-adapters.js +0 -28
  53. package/dist/components/questions/question-bank/question-bank-picker.utils.js +0 -7
  54. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +16 -16
  55. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +36 -30
  56. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupClient.js +1 -1
  57. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupCreator.js +7 -6
  58. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +2 -2
  59. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +11 -7
  60. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -1
  61. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +11 -5
  62. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +5 -5
  63. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +34 -28
  64. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseAnswerGroupImageUpload.d.ts +7 -0
  65. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseAnswerGroupImageUpload.js +5 -2
  66. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +9 -5
  67. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupCreator.js +65 -62
  68. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupItemEditor.d.ts +6 -0
  69. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupItemEditor.js +145 -0
  70. package/dist/components/questions/types/choose-the-correct-answer-group/map-choose-the-correct-answer-group-data.js +6 -15
  71. package/dist/components/questions/types/choose-the-correct-answer-group/transform.js +5 -4
  72. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupClient.js +7 -7
  73. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupCreator.js +8 -5
  74. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +2 -2
  75. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +20 -16
  76. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.js +1 -1
  77. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleCreator.js +66 -29
  78. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +5 -5
  79. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +45 -37
  80. package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupCreator.js +24 -20
  81. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +2 -2
  82. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +68 -65
  83. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixClient.js +1 -1
  84. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixCreator.js +45 -5
  85. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +2 -2
  86. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +18 -14
  87. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +2 -2
  88. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +30 -30
  89. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +2 -2
  90. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +24 -31
  91. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +2 -2
  92. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +29 -32
  93. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -1
  94. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +27 -29
  95. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -1
  96. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +11 -15
  97. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -1
  98. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +22 -24
  99. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -1
  100. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +17 -24
  101. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -1
  102. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +31 -31
  103. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -1
  104. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +11 -15
  105. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -1
  106. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +14 -15
  107. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +2 -2
  108. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +27 -25
  109. package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesClient.js +12 -12
  110. package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesCreator.js +8 -5
  111. package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureClient.js +8 -8
  112. package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureCreator.js +25 -20
  113. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupClient.js +1 -1
  114. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupCreator.js +13 -10
  115. package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupCreator.js +8 -5
  116. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +7 -11
  117. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -1
  118. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +15 -12
  119. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesClient.js +1 -1
  120. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesCreator.js +5 -5
  121. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +35 -39
  122. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +2 -2
  123. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardClient.js +1 -1
  124. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +7 -8
  125. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -1
  126. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +30 -23
  127. package/dist/components/questions/types/spontaneous-qa/SpontaneousQAClient.js +1 -1
  128. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +26 -19
  129. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.d.ts +1 -1
  130. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.js +79 -3
  131. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.js +13 -11
  132. package/dist/components/questions/types/spontaneous-qa-group/index.d.ts +1 -0
  133. package/dist/components/questions/types/spontaneous-qa-group/index.js +1 -0
  134. package/dist/components/questions/types/spontaneous-qa-group/map-spontaneous-qa-group-data.d.ts +2 -0
  135. package/dist/components/questions/types/spontaneous-qa-group/map-spontaneous-qa-group-data.js +74 -0
  136. package/dist/components/questions/types/true-false/TrueFalseClient.js +2 -2
  137. package/dist/components/questions/types/true-false/TrueFalseCreator.js +35 -29
  138. package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupClient.js +1 -1
  139. package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupCreator.js +11 -7
  140. package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +1 -1
  141. package/dist/components/questions/types/true-false-group/TrueFalseGroupCreator.js +11 -7
  142. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +2 -2
  143. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +21 -56
  144. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -1
  145. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +30 -24
  146. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.js +3 -3
  147. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupCreator.js +12 -14
  148. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -1
  149. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +27 -18
  150. package/dist/components/questions/types/word-ordering-group/WordOrderingGroupClient.js +7 -7
  151. package/dist/components/questions/types/word-ordering-group/WordOrderingGroupCreator.js +16 -13
  152. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +2 -2
  153. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +34 -28
  154. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +2 -2
  155. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +22 -22
  156. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +21 -18
  157. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -1
  158. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +7 -4
  159. package/dist/components/questions/viewer/QuestionViewer.js +0 -98
  160. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.js +4 -79
  161. package/dist/components/results/renderers/review-question-body-movers.js +2 -26
  162. package/dist/components/results/review-data.js +0 -4
  163. package/dist/components/ui/points-input.d.ts +1 -0
  164. package/dist/components/ui/points-input.js +12 -2
  165. package/dist/shared/constants/question-skills.js +0 -11
  166. package/dist/shared/lib/i18n/messages/en/admin.d.ts +0 -3
  167. package/dist/shared/lib/i18n/messages/en/admin.js +0 -3
  168. package/dist/shared/lib/i18n/messages/en.d.ts +0 -3
  169. package/dist/shared/lib/i18n/messages/vi/admin.d.ts +0 -3
  170. package/dist/shared/lib/i18n/messages/vi/admin.js +0 -3
  171. package/dist/shared/lib/i18n/messages/vi.d.ts +0 -3
  172. package/dist/shared/lib/utils/question-reverse-transform.js +4 -23
  173. package/dist/shared/lib/utils/question-transform-types.d.ts +1 -23
  174. package/dist/shared/lib/utils/question-transform-validation.js +0 -40
  175. package/dist/shared/lib/utils/question-transform.js +0 -9
  176. package/dist/shared/types/common.types.d.ts +1 -1
  177. package/dist/shared/types/entities/question.types.d.ts +1 -11
  178. package/dist/shared/types/questions/choose-the-correct-answer-group.d.ts +1 -1
  179. package/dist/shared/types/questions/index.d.ts +0 -3
  180. package/dist/shared/types/questions/index.js +0 -6
  181. package/package.json +1 -1
  182. package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.d.ts +0 -2
  183. package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.js +0 -131
  184. package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.d.ts +0 -2
  185. package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.js +0 -76
  186. package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.d.ts +0 -2
  187. package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.js +0 -68
  188. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.d.ts +0 -16
  189. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.js +0 -212
  190. package/dist/components/questions/_shared/types/gn-speaking-interview.type.d.ts +0 -32
  191. package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +0 -1
  192. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.d.ts +0 -47
  193. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +0 -1
  194. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.d.ts +0 -2
  195. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +0 -262
  196. package/dist/components/questions/types/gn-speaking-interview/register.d.ts +0 -2
  197. package/dist/components/questions/types/gn-speaking-interview/register.js +0 -121
  198. package/dist/components/questions/types/gn-speaking-interview/transform.d.ts +0 -2
  199. package/dist/components/questions/types/gn-speaking-interview/transform.js +0 -48
  200. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.d.ts +0 -20
  201. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +0 -72
  202. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.d.ts +0 -15
  203. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +0 -230
  204. package/dist/components/questions/types/look-picture-choose-correct-answer/index.d.ts +0 -1
  205. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +0 -1
  206. package/dist/components/questions/types/look-picture-choose-correct-answer/register.d.ts +0 -2
  207. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +0 -66
  208. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.d.ts +0 -2
  209. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +0 -42
  210. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.d.ts +0 -6
  211. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +0 -96
  212. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.d.ts +0 -15
  213. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +0 -238
  214. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.d.ts +0 -2
  215. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +0 -2
  216. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.d.ts +0 -2
  217. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +0 -76
  218. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.d.ts +0 -2
  219. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +0 -19
  220. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.d.ts +0 -7
  221. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +0 -134
  222. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.d.ts +0 -3
  223. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +0 -617
  224. package/dist/components/questions/types/look-picture-fill-word-hint/index.d.ts +0 -2
  225. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +0 -2
  226. package/dist/components/questions/types/look-picture-fill-word-hint/register.d.ts +0 -2
  227. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +0 -82
  228. package/dist/components/questions/types/look-picture-fill-word-hint/transform.d.ts +0 -2
  229. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +0 -29
  230. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.d.ts +0 -9
  231. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +0 -12
  232. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.d.ts +0 -17
  233. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +0 -47
  234. package/dist/shared/types/questions/look-picture-choose-correct-answer.d.ts +0 -38
  235. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +0 -5
  236. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.d.ts +0 -63
  237. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +0 -9
  238. package/dist/shared/types/questions/look-picture-fill-word-hint.d.ts +0 -57
  239. package/dist/shared/types/questions/look-picture-fill-word-hint.js +0 -7
@@ -1,15 +1,13 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useMemo, useRef, useState } from 'react';
4
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
5
4
  import { Label } from '../../../../components/ui/label';
6
5
  import { Button } from '../../../../components/ui/button';
7
6
  import { Input } from '../../../../components/ui/input';
8
- import { Textarea } from '../../../../components/ui/textarea';
9
7
  import { Form, FormControl, FormField, FormItem, FormMessage } from '../../../../components/ui/form';
10
8
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../../../components/ui/select';
11
9
  import { FileUpload } from '../../../../components/ui/file-upload';
12
- import { PointsInput } from '../../../../components/ui/points-input';
10
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
13
11
  // Stores & hooks
14
12
  import { useQuestionListGroup } from '../../_shared/hooks/useQuestionListGroup';
15
13
  import { generateObjectId } from '../../_shared/utils/id-generator';
@@ -17,10 +15,9 @@ import { generateObjectId } from '../../_shared/utils/id-generator';
17
15
  import { ListenAndSpeakQuestionListClient } from './ListenAndSpeakQuestionListClient';
18
16
  import { listenAndSpeakQuestionListFormSchema } from '../../../../shared/lib/validation/listen-and-speak-question-list.validation';
19
17
  // Utilities & Icons
20
- import { Plus, Trash2, Mic, Volume2, ListChecks, MessageSquareText, Info, BookOpen, Eye, Pencil } from 'lucide-react';
18
+ import { Plus, Trash2, Mic, Volume2, ListChecks, MessageSquareText, Info } from 'lucide-react';
21
19
  import { useReactHookForm } from '../../../../shared/lib/hooks/useReactHookForm';
22
20
  import { useDebouncedCallback } from '../../../../shared/lib/hooks/useDebouncedCallback';
23
- import { CreatorGuide } from '../../components/CreatorGuide';
24
21
  export function ListenAndSpeakQuestionListCreator({ questionIndex, initialData, onChange, validationRef, isFirstInGroup: isFirstInGroupProp, partId, groups, questionIndexInPart, }) {
25
22
  const [isClientMode, setIsClientMode] = useState(false);
26
23
  const isGrouped = Boolean(groups && groups.length > 0);
@@ -241,16 +238,15 @@ export function ListenAndSpeakQuestionListCreator({ questionIndex, initialData,
241
238
  questions,
242
239
  groupNumber: isGrouped ? currentGroupNumber : undefined,
243
240
  };
244
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Nghe & Tr\u1EA3 l\u1EDDi theo danh s\u00E1ch c\u00E2u h\u1ECFi (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(ListenAndSpeakQuestionListClient, { questionData: questionData, isReviewMode: true })] }));
241
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(ListenAndSpeakQuestionListClient, { questionData: questionData, isReviewMode: true })] }));
245
242
  }
246
243
  // ─── Creator UI ────────────────────────────────────────────────────────────
247
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(CreatorGuide, { colorScheme: "blue", steps: [
248
- { text: 'Nhập danh sách câu hỏi cho nhóm — câu hỏi đầu tiên sẽ quản lý danh sách chung' },
249
- { text: 'Upload audio câu hỏi cho từng câu trong nhóm' },
250
- { text: 'Chọn câu hỏi đang active (active question) tương ứng với câu đang hỏi', highlight: true },
251
- { text: 'Nhập các đáp án mong muốn cho mỗi câu' },
252
- { text: 'Thêm giải thích nếu cần thiết' },
253
- ], tips: ['Câu hỏi nên ngắn gọn, rõ ràng và phù hợp với trình độ học sinh'] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-blue-500 via-indigo-500 to-purple-500" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 shadow-sm", children: _jsx(ListChecks, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-lg font-bold text-gray-900", children: "Nh\u00F3m \u0111\u00E1p \u00E1n" }), _jsxs("p", { className: "text-sm text-gray-500", children: ["Nh\u00F3m ", currentGroupNumber, " \u2014 D\u00F9ng chung cho t\u1EA5t c\u1EA3 c\u00E2u h\u1ECFi"] })] })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: !watchedAudioUrl || watchedExpectedAnswers.filter((a) => a.trim()).length === 0, className: "gap-2 border-blue-200 text-blue-600 hover:bg-blue-50 disabled:opacity-50", children: [_jsx(Eye, { className: "h-4 w-4" }), "Preview"] })] }) })] }), isFirstInGroup ? (_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-cyan-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-cyan-400 to-sky-400" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 shrink-0 items-center justify-center rounded-md bg-cyan-100", children: _jsx(ListChecks, { className: "h-3.5 w-3.5 text-cyan-600" }) }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-lg font-bold text-gray-900", children: "Danh s\u00E1ch c\u00E2u h\u1ECFi" }), _jsxs("span", { className: "inline-flex items-center rounded-full border border-indigo-300 bg-indigo-100 px-2.5 py-0.5 text-xs font-semibold text-indigo-700", children: ["Nh\u00F3m ", currentGroupNumber] })] }), _jsx("p", { className: "text-sm text-gray-500", children: "D\u00F9ng chung cho t\u1EA5t c\u1EA3 c\u00E2u h\u1ECFi trong nh\u00F3m \u2014 Th\u00EAm c\u00E1c c\u00E2u h\u1ECFi text \u0111\u1EC3 h\u1ECDc sinh ch\u1ECDn v\u00E0 tr\u1EA3 l\u1EDDi" })] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsx("div", { className: "space-y-3", children: questions.map((q, index) => (_jsxs("div", { className: "flex gap-3 items-center", children: [_jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-indigo-100 text-indigo-700 font-semibold text-sm shadow-sm", children: index + 1 }), _jsx(Input, { value: q.text, onChange: (e) => handleUpdateListQuestion(index, e.target.value), placeholder: "VD: Now, how old are you?", className: "flex-1 border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => handleRemoveListQuestion(index), className: "shrink-0 text-gray-400 hover:text-red-600 hover:bg-red-50", "aria-label": `Xóa câu hỏi ${index + 1}`, children: _jsx(Trash2, { className: "h-4 w-4" }) })] }, q.id))) }), questions.length === 0 && (_jsxs("div", { className: "flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-indigo-200 bg-indigo-50/50 py-8 px-4 text-center", children: [_jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-indigo-100 mb-3", children: _jsx(ListChecks, { className: "h-6 w-6 text-indigo-500" }) }), _jsx("p", { className: "text-sm font-medium text-gray-700", children: "Ch\u01B0a c\u00F3 c\u00E2u h\u1ECFi n\u00E0o" }), _jsx("p", { className: "text-xs text-gray-500 mt-1", children: "Vui l\u00F2ng th\u00EAm \u00EDt nh\u1EA5t m\u1ED9t c\u00E2u h\u1ECFi cho nh\u00F3m n\u00E0y" })] })), _jsxs(Button, { type: "button", variant: "outline", onClick: handleAddListQuestion, className: "w-full gap-2 border-indigo-200 text-indigo-600 hover:bg-indigo-50 hover:border-indigo-300", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm c\u00E2u h\u1ECFi"] })] })] })) : (
254
- /* Read-only view of the shared question list for non-first questions in the group */
255
- _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow shadow-indigo-100/30", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-indigo-400 via-blue-400 to-cyan-400" }), _jsx(CardHeader, { className: "pb-3", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-indigo-100 shadow-sm", children: _jsx(ListChecks, { className: "h-4 w-4 text-indigo-600" }) }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-base font-semibold text-gray-800", children: "Danh s\u00E1ch c\u00E2u h\u1ECFi" }), _jsxs("span", { className: "inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-500", children: ["Nh\u00F3m ", currentGroupNumber, " \u2014 Ch\u1EC9 \u0111\u1ECDc"] })] }), _jsx("p", { className: "text-xs text-gray-400 mt-0.5", children: "\u0110\u01B0\u1EE3c qu\u1EA3n l\u00FD b\u1EDFi c\u00E2u h\u1ECFi \u0111\u1EA7u ti\u00EAn trong nh\u00F3m" })] })] }) }), _jsx(CardContent, { className: "pt-0", children: questions.length > 0 ? (_jsx("div", { className: "space-y-2", children: questions.map((q, index) => (_jsxs("div", { className: "flex gap-2.5 items-center px-3 py-2 rounded-lg bg-gray-50 border border-gray-100", children: [_jsx("div", { className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-md bg-indigo-50 text-indigo-600 font-semibold text-xs", children: index + 1 }), _jsx("span", { className: "text-sm text-gray-700", children: q.text || _jsx("span", { className: "italic text-gray-400", children: "(Ch\u01B0a nh\u1EADp n\u1ED9i dung)" }) })] }, q.id))) })) : (_jsx("div", { className: "text-center py-4 text-sm text-gray-400 italic", children: "Ch\u01B0a c\u00F3 c\u00E2u h\u1ECFi n\u00E0o trong nh\u00F3m" })) })] })), _jsxs(Form, { ...form, children: [_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-indigo-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-indigo-500 via-purple-500 to-pink-500" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-indigo-500 to-purple-600 shadow-sm", children: _jsx(Mic, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-lg font-bold text-gray-900", children: "C\u00E2u h\u1ECFi Nghe & Tr\u1EA3 l\u1EDDi" }), _jsx("p", { className: "text-sm text-gray-500", children: "H\u1ECDc sinh nghe \u00E2m thanh v\u00E0 n\u00F3i c\u00E2u tr\u1EA3 l\u1EDDi" })] })] }) }), _jsxs(CardContent, { className: "space-y-6", children: [_jsx("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: _jsx("div", { className: "flex flex-col gap-4", children: _jsx("div", { className: "w-full md:w-1/2", children: _jsx(PointsInput, { ...form.register('points', { valueAsNumber: true }), error: form.formState.errors?.points?.message }) }) }) }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(MessageSquareText, { className: "h-4 w-4 text-indigo-500" }), "C\u00E2u h\u1ECFi \u0111\u01B0\u1EE3c h\u1ECFi trong audio ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-blue-100 bg-blue-50/60 px-3 py-2.5", children: [_jsx(Info, { className: "h-4 w-4 text-blue-500 mt-0.5 flex-shrink-0" }), _jsxs("div", { className: "text-xs text-blue-700 space-y-1", children: [_jsx("p", { children: "Ch\u1ECDn c\u00E2u h\u1ECFi m\u00E0 h\u1ECDc sinh s\u1EBD nghe trong \u0111o\u1EA1n audio n\u00E0y." }), _jsxs("p", { children: [_jsx("span", { className: "font-semibold", children: "\u2022 M\u1ED7i audio t\u01B0\u01A1ng \u1EE9ng m\u1ED9t c\u00E2u h\u1ECFi:" }), " X\u00E1c \u0111\u1ECBnh c\u00E2u h\u1ECFi n\u00E0o t\u1EEB danh s\u00E1ch \u0111ang \u0111\u01B0\u1EE3c h\u1ECFi trong audio."] }), _jsxs("p", { children: [_jsx("span", { className: "font-semibold", children: "\u2022 Hi\u1EC3n th\u1ECB cho h\u1ECDc sinh:" }), " H\u1ECDc sinh s\u1EBD th\u1EA5y danh s\u00E1ch c\u00E2u h\u1ECFi v\u00E0 c\u1EA7n ch\u1ECDn \u0111\u00FAng c\u00E2u h\u1ECFi kh\u1EDBp v\u1EDBi audio \u0111\u1EC3 tr\u1EA3 l\u1EDDi."] })] })] }), _jsx(FormField, { control: control, name: "activeQuestionId", render: ({ field }) => (_jsxs(FormItem, { children: [_jsxs(Select, { value: field.value || '', onValueChange: field.onChange, children: [_jsx(FormControl, { children: _jsx(SelectTrigger, { className: "w-full bg-white border-gray-200 transition-all focus:border-indigo-300 focus:ring-indigo-200", children: _jsx(SelectValue, { placeholder: "Ch\u1ECDn c\u00E2u h\u1ECFi t\u1EEB danh s\u00E1ch" }) }) }), _jsxs(SelectContent, { children: [questions.map((q, idx) => (_jsxs(SelectItem, { value: q.id, children: [idx + 1, ". ", q.text || '(Chưa nhập nội dung)'] }, q.id))), questions.length === 0 && (_jsx("div", { className: "p-3 text-sm text-gray-500 italic text-center", children: "Vui l\u00F2ng t\u1EA1o c\u00E2u h\u1ECFi \u1EDF nh\u00F3m tr\u00EAn tr\u01B0\u1EDBc" }))] })] }), _jsx(FormMessage, {})] })) })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(Volume2, { className: "h-4 w-4 text-indigo-500" }), "\u00C2m thanh c\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(FormField, { control: control, name: "audioUrl", render: ({ field }) => (_jsxs(FormItem, { children: [_jsx(FormControl, { children: _jsx(FileUpload, { id: `audio-question-${displayQuestionNumber}`, label: "", accept: "audio/*", value: field.value, onChange: (url) => { field.onChange(url); clearErrors('audioUrl'); }, maxSize: 10, placeholder: "T\u1EA3i l\u00EAn file \u00E2m thanh (mp3, wav...)", autoUpload: true, prefix: "listen-and-speak" }) }), _jsx(FormMessage, {})] })) })] })] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-green-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-green-400 to-emerald-500" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-lg bg-gradient-to-br from-green-500 to-emerald-600 shadow-sm", children: _jsx(MessageSquareText, { className: "h-4 w-4 text-white" }) }), _jsx("div", { children: _jsxs(CardTitle, { className: "text-lg font-semibold text-gray-800", children: ["\u0110\u00E1p \u00E1n mong mu\u1ED1n (", watchedExpectedAnswers.filter((a) => a.trim()).length, ")"] }) })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addExpectedAnswer, className: "gap-2 border-green-200 text-green-600 hover:bg-green-50", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm \u0111\u00E1p \u00E1n"] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-blue-100 bg-blue-50/60 px-3 py-2.5", children: [_jsx(Info, { className: "h-4 w-4 text-blue-500 mt-0.5 flex-shrink-0" }), _jsxs("div", { className: "text-xs text-blue-700 space-y-1", children: [_jsx("p", { children: "Nh\u1EADp c\u00E1c c\u00E2u tr\u1EA3 l\u1EDDi mong mu\u1ED1n. C\u00F3 th\u1EC3 th\u00EAm nhi\u1EC1u \u0111\u00E1p \u00E1n n\u1EBFu c\u00F3 nhi\u1EC1u c\u00E1ch tr\u1EA3 l\u1EDDi \u0111\u00FAng." }), _jsxs("p", { children: [_jsx("span", { className: "font-semibold", children: "\u2022 Ch\u1EA5m th\u1EE7 c\u00F4ng:" }), " Gi\u00E1o vi\u00EAn d\u00F9ng c\u00E1c \u0111\u00E1p \u00E1n n\u00E0y l\u00E0m tham kh\u1EA3o \u0111\u1EC3 ch\u1EA5m \u0111i\u1EC3m."] }), _jsxs("p", { children: [_jsx("span", { className: "font-semibold", children: "\u2022 Ch\u1EA5m t\u1EF1 \u0111\u1ED9ng (AI):" }), " H\u1EC7 th\u1ED1ng d\u00F9ng AI so s\u00E1nh gi\u1ECDng n\u00F3i c\u1EE7a h\u1ECDc sinh v\u1EDBi c\u00E1c \u0111\u00E1p \u00E1n \u0111\u1EC3 t\u1EF1 \u0111\u1ED9ng ch\u1EA5m \u0111i\u1EC3m."] })] })] }), _jsx(FormField, { control: control, name: "expectedAnswers", render: () => (_jsxs(FormItem, { children: [_jsx("div", { className: "space-y-3", children: watchedExpectedAnswers.map((answer, index) => (_jsxs("div", { className: "flex items-start gap-3 group/answer", children: [_jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-green-100 text-green-700 font-semibold shadow-sm text-sm", children: index + 1 }), _jsxs("div", { className: "flex-1", children: [_jsx(Input, { placeholder: `Ví dụ: ${index === 0 ? 'I am doing well' : 'I feel great'}`, value: answer || '', onChange: (e) => handleExpectedAnswerChange(index, e.target.value), className: "border-gray-200 focus:border-green-300 focus:ring-green-200" }), form.formState.errors?.expectedAnswers?.[index]?.message && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: form.formState.errors.expectedAnswers[index]?.message }))] }), watchedExpectedAnswers.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeExpectedAnswer(index), className: "text-red-400 hover:text-red-600 hover:bg-red-50 opacity-0 group-hover/answer:opacity-100 transition-opacity", "aria-label": `Xóa đáp án ${index + 1}`, children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }, index))) }), form.formState.errors?.expectedAnswers && !Array.isArray(form.formState.errors.expectedAnswers) && (_jsx("p", { className: "text-sm text-red-500", children: form.formState.errors.expectedAnswers.message }))] })) })] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-amber-400 to-orange-400" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm", children: _jsx(BookOpen, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-lg font-semibold text-gray-800", children: "Gi\u1EA3i th\u00EDch" }), _jsx("span", { className: "inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-600", children: "Kh\u00F4ng b\u1EAFt bu\u1ED9c" })] }), _jsx("p", { className: "text-sm text-gray-500 mt-0.5", children: "Gi\u1EA3i th\u00EDch cho \u0111\u00E1p \u00E1n \u0111\u00FAng \u2014 hi\u1EC3n th\u1ECB sau khi h\u1ECDc sinh ho\u00E0n th\u00E0nh c\u00E2u h\u1ECFi" })] })] }) }), _jsx(CardContent, { children: _jsx(Textarea, { ...form.register('explanation'), placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0 'I am 7 years old' v\u00EC c\u00E2u h\u1ECFi h\u1ECFi v\u1EC1 tu\u1ED5i...", className: "min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none", rows: 3 }) })] })] })] }));
244
+ return (_jsxs("div", { className: "space-y-3", children: [_jsx(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(ListChecks, { className: "h-3.5 w-3.5" }), title: "Nh\u00F3m \u0111\u00E1p \u00E1n", extraActions: _jsxs("span", { className: "inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-600", children: ["Nh\u00F3m ", currentGroupNumber] }), points: {
245
+ value: watchedPoints,
246
+ onValueChange: (value) => form.setValue('points', value, { shouldValidate: true }),
247
+ error: form.formState.errors?.points?.message,
248
+ }, preview: {
249
+ onClick: () => setIsClientMode(true),
250
+ disabled: !watchedAudioUrl || watchedExpectedAnswers.filter((a) => a.trim()).length === 0,
251
+ } }), contentClassName: "hidden", children: null }), isFirstInGroup ? (_jsxs(CompactFieldCard, { header: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-gray-100", children: _jsx(ListChecks, { className: "h-3.5 w-3.5 text-gray-600" }) }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("p", { className: "text-sm font-semibold text-gray-800", children: "Danh s\u00E1ch c\u00E2u h\u1ECFi" }), _jsxs("span", { className: "inline-flex items-center rounded-full border border-indigo-300 bg-indigo-100 px-2.5 py-0.5 text-xs font-semibold text-indigo-700", children: ["Nh\u00F3m ", currentGroupNumber] })] }), _jsx("p", { className: "text-sm text-gray-500", children: "D\u00F9ng chung cho t\u1EA5t c\u1EA3 c\u00E2u h\u1ECFi trong nh\u00F3m \u2014 Th\u00EAm c\u00E1c c\u00E2u h\u1ECFi text \u0111\u1EC3 h\u1ECDc sinh ch\u1ECDn v\u00E0 tr\u1EA3 l\u1EDDi" })] })] }), children: [_jsx("div", { className: "space-y-3", children: questions.map((q, index) => (_jsxs("div", { className: "flex gap-3 items-center", children: [_jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-indigo-100 text-indigo-700 font-semibold text-sm shadow-sm", children: index + 1 }), _jsx(Input, { value: q.text, onChange: (e) => handleUpdateListQuestion(index, e.target.value), placeholder: "VD: Now, how old are you?", className: "flex-1 border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => handleRemoveListQuestion(index), className: "shrink-0 text-gray-400 hover:text-red-600 hover:bg-red-50", "aria-label": `Xóa câu hỏi ${index + 1}`, children: _jsx(Trash2, { className: "h-4 w-4" }) })] }, q.id))) }), questions.length === 0 && (_jsxs("div", { className: "flex flex-col items-center justify-center rounded-xl border-2 border-dashed border-indigo-200 bg-indigo-50/50 py-8 px-4 text-center", children: [_jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-indigo-100 mb-3", children: _jsx(ListChecks, { className: "h-6 w-6 text-indigo-500" }) }), _jsx("p", { className: "text-sm font-medium text-gray-700", children: "Ch\u01B0a c\u00F3 c\u00E2u h\u1ECFi n\u00E0o" }), _jsx("p", { className: "text-xs text-gray-500 mt-1", children: "Vui l\u00F2ng th\u00EAm \u00EDt nh\u1EA5t m\u1ED9t c\u00E2u h\u1ECFi cho nh\u00F3m n\u00E0y" })] })), _jsxs(Button, { type: "button", variant: "outline", onClick: handleAddListQuestion, className: "w-full gap-2 border-indigo-200 text-indigo-600 hover:bg-indigo-50 hover:border-indigo-300", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm c\u00E2u h\u1ECFi"] })] })) : (_jsx(CompactFieldCard, { header: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-gray-100", children: _jsx(ListChecks, { className: "h-3.5 w-3.5 text-gray-600" }) }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("p", { className: "text-sm font-semibold text-gray-800", children: "Danh s\u00E1ch c\u00E2u h\u1ECFi" }), _jsxs("span", { className: "inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-500", children: ["Nh\u00F3m ", currentGroupNumber, " \u2014 Ch\u1EC9 \u0111\u1ECDc"] })] }), _jsx("p", { className: "text-xs text-gray-400 mt-0.5", children: "\u0110\u01B0\u1EE3c qu\u1EA3n l\u00FD b\u1EDFi c\u00E2u h\u1ECFi \u0111\u1EA7u ti\u00EAn trong nh\u00F3m" })] })] }), children: questions.length > 0 ? (_jsx("div", { className: "space-y-2", children: questions.map((q, index) => (_jsxs("div", { className: "flex gap-2.5 items-center px-3 py-2 rounded-lg bg-gray-50 border border-gray-100", children: [_jsx("div", { className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-md bg-indigo-50 text-indigo-600 font-semibold text-xs", children: index + 1 }), _jsx("span", { className: "text-sm text-gray-700", children: q.text || _jsx("span", { className: "italic text-gray-400", children: "(Ch\u01B0a nh\u1EADp n\u1ED9i dung)" }) })] }, q.id))) })) : (_jsx("div", { className: "text-center py-4 text-sm text-gray-400 italic", children: "Ch\u01B0a c\u00F3 c\u00E2u h\u1ECFi n\u00E0o trong nh\u00F3m" })) })), _jsxs(Form, { ...form, children: [_jsxs(CompactFieldCard, { header: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-gray-100", children: _jsx(Mic, { className: "h-3.5 w-3.5 text-gray-600" }) }), _jsx("p", { className: "text-sm font-semibold text-gray-800", children: "C\u00E2u h\u1ECFi Nghe & Tr\u1EA3 l\u1EDDi" })] }), children: [_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(MessageSquareText, { className: "h-4 w-4 text-indigo-500" }), "C\u00E2u h\u1ECFi \u0111\u01B0\u1EE3c h\u1ECFi trong audio ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-blue-100 bg-blue-50/60 px-3 py-2.5", children: [_jsx(Info, { className: "h-4 w-4 text-blue-500 mt-0.5 flex-shrink-0" }), _jsxs("div", { className: "text-xs text-blue-700 space-y-1", children: [_jsx("p", { children: "Ch\u1ECDn c\u00E2u h\u1ECFi m\u00E0 h\u1ECDc sinh s\u1EBD nghe trong \u0111o\u1EA1n audio n\u00E0y." }), _jsxs("p", { children: [_jsx("span", { className: "font-semibold", children: "\u2022 M\u1ED7i audio t\u01B0\u01A1ng \u1EE9ng m\u1ED9t c\u00E2u h\u1ECFi:" }), " X\u00E1c \u0111\u1ECBnh c\u00E2u h\u1ECFi n\u00E0o t\u1EEB danh s\u00E1ch \u0111ang \u0111\u01B0\u1EE3c h\u1ECFi trong audio."] }), _jsxs("p", { children: [_jsx("span", { className: "font-semibold", children: "\u2022 Hi\u1EC3n th\u1ECB cho h\u1ECDc sinh:" }), " H\u1ECDc sinh s\u1EBD th\u1EA5y danh s\u00E1ch c\u00E2u h\u1ECFi v\u00E0 c\u1EA7n ch\u1ECDn \u0111\u00FAng c\u00E2u h\u1ECFi kh\u1EDBp v\u1EDBi audio \u0111\u1EC3 tr\u1EA3 l\u1EDDi."] })] })] }), _jsx(FormField, { control: control, name: "activeQuestionId", render: ({ field }) => (_jsxs(FormItem, { children: [_jsxs(Select, { value: field.value || '', onValueChange: field.onChange, children: [_jsx(FormControl, { children: _jsx(SelectTrigger, { className: "w-full bg-white border-gray-200 transition-all focus:border-indigo-300 focus:ring-indigo-200", children: _jsx(SelectValue, { placeholder: "Ch\u1ECDn c\u00E2u h\u1ECFi t\u1EEB danh s\u00E1ch" }) }) }), _jsxs(SelectContent, { children: [questions.map((q, idx) => (_jsxs(SelectItem, { value: q.id, children: [idx + 1, ". ", q.text || '(Chưa nhập nội dung)'] }, q.id))), questions.length === 0 && (_jsx("div", { className: "p-3 text-sm text-gray-500 italic text-center", children: "Vui l\u00F2ng t\u1EA1o c\u00E2u h\u1ECFi \u1EDF nh\u00F3m tr\u00EAn tr\u01B0\u1EDBc" }))] })] }), _jsx(FormMessage, {})] })) })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(Volume2, { className: "h-4 w-4 text-indigo-500" }), "\u00C2m thanh c\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(FormField, { control: control, name: "audioUrl", render: ({ field }) => (_jsxs(FormItem, { children: [_jsx(FormControl, { children: _jsx(FileUpload, { id: `audio-question-${displayQuestionNumber}`, label: "", accept: "audio/*", value: field.value, onChange: (url) => { field.onChange(url); clearErrors('audioUrl'); }, maxSize: 10, placeholder: "T\u1EA3i l\u00EAn file \u00E2m thanh (mp3, wav...)", autoUpload: true, prefix: "listen-and-speak" }) }), _jsx(FormMessage, {})] })) })] })] }), _jsxs(CompactFieldCard, { header: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100", children: _jsx(MessageSquareText, { className: "h-3.5 w-3.5 text-gray-600" }) }), _jsxs("p", { className: "text-sm font-semibold text-gray-800", children: ["\u0110\u00E1p \u00E1n mong mu\u1ED1n (", watchedExpectedAnswers.filter((a) => a.trim()).length, ")"] })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addExpectedAnswer, className: "gap-2 border-green-200 text-green-600 hover:bg-green-50", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm \u0111\u00E1p \u00E1n"] })] }), children: [_jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-blue-100 bg-blue-50/60 px-3 py-2.5", children: [_jsx(Info, { className: "h-4 w-4 text-blue-500 mt-0.5 flex-shrink-0" }), _jsxs("div", { className: "text-xs text-blue-700 space-y-1", children: [_jsx("p", { children: "Nh\u1EADp c\u00E1c c\u00E2u tr\u1EA3 l\u1EDDi mong mu\u1ED1n. C\u00F3 th\u1EC3 th\u00EAm nhi\u1EC1u \u0111\u00E1p \u00E1n n\u1EBFu c\u00F3 nhi\u1EC1u c\u00E1ch tr\u1EA3 l\u1EDDi \u0111\u00FAng." }), _jsxs("p", { children: [_jsx("span", { className: "font-semibold", children: "\u2022 Ch\u1EA5m th\u1EE7 c\u00F4ng:" }), " Gi\u00E1o vi\u00EAn d\u00F9ng c\u00E1c \u0111\u00E1p \u00E1n n\u00E0y l\u00E0m tham kh\u1EA3o \u0111\u1EC3 ch\u1EA5m \u0111i\u1EC3m."] }), _jsxs("p", { children: [_jsx("span", { className: "font-semibold", children: "\u2022 Ch\u1EA5m t\u1EF1 \u0111\u1ED9ng (AI):" }), " H\u1EC7 th\u1ED1ng d\u00F9ng AI so s\u00E1nh gi\u1ECDng n\u00F3i c\u1EE7a h\u1ECDc sinh v\u1EDBi c\u00E1c \u0111\u00E1p \u00E1n \u0111\u1EC3 t\u1EF1 \u0111\u1ED9ng ch\u1EA5m \u0111i\u1EC3m."] })] })] }), _jsx(FormField, { control: control, name: "expectedAnswers", render: () => (_jsxs(FormItem, { children: [_jsx("div", { className: "space-y-3", children: watchedExpectedAnswers.map((answer, index) => (_jsxs("div", { className: "flex items-start gap-3 group/answer", children: [_jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-green-100 text-green-700 font-semibold shadow-sm text-sm", children: index + 1 }), _jsxs("div", { className: "flex-1", children: [_jsx(Input, { placeholder: `Ví dụ: ${index === 0 ? 'I am doing well' : 'I feel great'}`, value: answer || '', onChange: (e) => handleExpectedAnswerChange(index, e.target.value), className: "border-gray-200 focus:border-green-300 focus:ring-green-200" }), form.formState.errors?.expectedAnswers?.[index]?.message && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: form.formState.errors.expectedAnswers[index]?.message }))] }), watchedExpectedAnswers.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeExpectedAnswer(index), className: "text-red-400 hover:text-red-600 hover:bg-red-50 opacity-0 group-hover/answer:opacity-100 transition-opacity", "aria-label": `Xóa đáp án ${index + 1}`, children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }, index))) }), form.formState.errors?.expectedAnswers && !Array.isArray(form.formState.errors.expectedAnswers) && (_jsx("p", { className: "text-sm text-red-500", children: form.formState.errors.expectedAnswers.message }))] })) })] }), _jsx(CompactExplanationToggle, { value: watchedExplanation || '', onValueChange: (value) => form.setValue('explanation', value, { shouldValidate: true }), defaultVisible: Boolean(watchedExplanation) })] })] }));
256
252
  }
@@ -20,7 +20,7 @@ function StoryImagePreview({ imageUrl, index }) {
20
20
  // ─── Main Client Component ───────────────────────────────────────────────────
21
21
  export function ListenAndSpeakWithStoryImagesClient({ questionData, isReviewMode = false, }) {
22
22
  const { audioUrl, images, expectedAnswers = [], explanation } = questionData;
23
- return (_jsxs("div", { className: "space-y-4", children: [questionData.isBackup && (_jsxs("div", { className: "inline-flex self-center items-center gap-1.5 px-3 py-1.5 rounded-md bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold shadow-sm", children: [_jsx("span", { children: "\uD83D\uDD04" }), " C\u00E2u h\u1ECFi d\u1EF1 ph\u00F2ng"] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsx("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-indigo-50 to-blue-50 px-5 py-3", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-indigo-500 to-blue-600 text-white shadow-sm", children: _jsx(BookImage, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Nghe v\u00E0 n\u00F3i (K\u1EC3 chuy\u1EC7n theo h\u00ECnh)" })] }) }), _jsxs("div", { className: "p-5 space-y-5", children: [audioUrl ? (_jsx(QuestionAudioPlayer, { audioUrl: audioUrl })) : (_jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-4 py-3", children: [_jsx(Volume2, { className: "h-4 w-4 text-gray-400" }), _jsx("span", { className: "text-xs text-gray-400", children: "Ch\u01B0a c\u00F3 audio c\u00E2u h\u1ECFi" })] })), images && images.length > 0 && (_jsx("div", { className: "bg-gradient-to-br from-purple-50/80 to-fuchsia-50/60 rounded-2xl p-6 border border-purple-100/80 shadow-sm", children: _jsx("div", { className: "grid gap-4", style: {
23
+ return (_jsxs("div", { className: "space-y-4", children: [questionData.isBackup && (_jsxs("div", { className: "inline-flex self-center items-center gap-1.5 px-3 py-1.5 rounded-md bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold shadow-sm", children: [_jsx("span", { children: "\uD83D\uDD04" }), " C\u00E2u h\u1ECFi d\u1EF1 ph\u00F2ng"] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsx("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gray-50 px-3 py-2", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100 text-gray-600", children: _jsx(BookImage, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Nghe v\u00E0 n\u00F3i (K\u1EC3 chuy\u1EC7n theo h\u00ECnh)" })] }) }), _jsxs("div", { className: "p-3 space-y-3", children: [audioUrl ? (_jsx(QuestionAudioPlayer, { audioUrl: audioUrl })) : (_jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-4 py-3", children: [_jsx(Volume2, { className: "h-4 w-4 text-gray-400" }), _jsx("span", { className: "text-xs text-gray-400", children: "Ch\u01B0a c\u00F3 audio c\u00E2u h\u1ECFi" })] })), images && images.length > 0 && (_jsx("div", { className: "bg-gradient-to-br from-purple-50/80 to-fuchsia-50/60 rounded-2xl p-6 border border-purple-100/80 shadow-sm", children: _jsx("div", { className: "grid gap-4", style: {
24
24
  gridTemplateColumns: `repeat(${Math.min(images.length, 4)}, 1fr)`,
25
25
  }, children: images.map((imgUrl, index) => (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "flex items-center gap-1.5", children: _jsx("span", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-purple-200 text-purple-800 text-xs font-bold", children: index + 1 }) }), _jsx("div", { className: "relative aspect-square rounded-xl overflow-hidden border-[3px] border-purple-300 bg-white shadow-md ring-2 ring-purple-100", children: imgUrl ? (_jsx(StoryImagePreview, { imageUrl: imgUrl, index: index })) : (_jsx("div", { className: "w-full h-full flex items-center justify-center text-gray-400", children: _jsx(ImageIcon, { className: "h-6 w-6" }) })) })] }, index))) }) })), _jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-dashed border-gray-300 bg-gray-50/80 px-4 py-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-indigo-50 ring-2 ring-indigo-100/50", children: _jsx(Mic, { className: "w-4 h-4 text-indigo-400" }) }), _jsx("p", { className: "text-xs text-gray-400", children: "H\u1ECDc sinh s\u1EBD nh\u1EA5n n\u00FAt ghi \u00E2m v\u00E0 n\u00F3i c\u00E2u tr\u1EA3 l\u1EDDi t\u1EA1i \u0111\u00E2y" })] }), isReviewMode && expectedAnswers.length > 0 && (_jsxs("div", { className: "rounded-xl border border-green-200 bg-green-50/80 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-3", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), _jsx("h4", { className: "text-sm font-semibold text-green-800", children: "C\u00E1c \u0111\u00E1p \u00E1n mong mu\u1ED1n" })] }), _jsx("div", { className: "space-y-2", children: expectedAnswers.map((answer, index) => (_jsxs("div", { className: "flex items-center gap-2.5 rounded-lg bg-white/70 border border-green-100 px-3 py-2", children: [_jsx("span", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-green-100 text-green-700 text-xs font-bold", children: index + 1 }), _jsx("span", { className: "text-sm text-green-800", children: answer })] }, index))) })] })), isReviewMode && explanation && (_jsxs("div", { className: "rounded-xl border border-indigo-200 bg-indigo-50 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(Info, { className: "h-4 w-4 text-indigo-600" }), _jsx("p", { className: "text-sm font-semibold text-indigo-800", children: "Gi\u1EA3i th\u00EDch" })] }), _jsx("p", { className: "text-sm text-indigo-700 leading-relaxed", children: explanation })] }))] })] })] }));
26
26
  }
@@ -1,21 +1,19 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from 'react';
4
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
4
+ import { Card, CardContent, CardHeader } from '../../../../components/ui/card';
5
5
  import { Label } from '../../../../components/ui/label';
6
6
  import { Button } from '../../../../components/ui/button';
7
7
  import { Input } from '../../../../components/ui/input';
8
- import { Textarea } from '../../../../components/ui/textarea';
9
8
  import { Form, FormControl, FormField, FormItem, FormMessage } from '../../../../components/ui/form';
10
9
  import { FileUpload } from '../../../../components/ui/file-upload';
11
- import { PointsInput } from '../../../../components/ui/points-input';
10
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
12
11
  // Hooks
13
12
  import { useReactHookForm } from '../../../../shared/lib/hooks/useReactHookForm';
14
13
  import { useDebouncedCallback, usePresignedFileUrl } from '../../../../shared/lib/hooks';
15
14
  import { listenAndSpeakWithStoryImagesSchema, } from '../../../../shared/lib/validation/listen-and-speak-with-story-images.validation';
16
15
  // Icons
17
- import { Plus, Trash2, Volume2, ImageIcon, MessageSquareText, Mic, BookOpen, Eye, Pencil, Info, ListChecks } from 'lucide-react';
18
- import { CreatorGuide } from '../../components/CreatorGuide';
16
+ import { Plus, Trash2, Volume2, ImageIcon, MessageSquareText, Mic, BookOpen, Info, ListChecks } from 'lucide-react';
19
17
  import { ListenAndSpeakWithStoryImagesClient } from './ListenAndSpeakWithStoryImagesClient';
20
18
  // ─── Sub-component: Image preview with presigned URL ─────────────────────────
21
19
  function ImagePreview({ imageUrl, index }) {
@@ -216,15 +214,16 @@ export function ListenAndSpeakWithStoryImagesCreator({ initialData, onChange, on
216
214
  points: watchedPoints,
217
215
  isBackup: watchedIsBackup,
218
216
  };
219
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Nghe v\u00E0 n\u00F3i \u2014 K\u1EC3 c\u00E2u chuy\u1EC7n (Preview)" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Preview mode" })] }), _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(ListenAndSpeakWithStoryImagesClient, { questionData: questionData, isReviewMode: true })] }));
217
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(ListenAndSpeakWithStoryImagesClient, { questionData: questionData, isReviewMode: true })] }));
220
218
  }
221
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(CreatorGuide, { colorScheme: "blue", steps: [
222
- { text: 'Upload các hình ảnh câu chuyện theo thứ tự — học sinh sẽ kể lại câu chuyện dựa trên hình' },
223
- { text: 'Có thể thêm hoặc xóa hình ảnh linh hoạt' },
224
- { text: 'Upload audio câu hỏi cho từng câu — giám khảo sẽ hỏi về nội dung câu chuyện' },
225
- { text: 'Nhập các đáp án mong muốn cho mỗi câu hỏi', highlight: true },
226
- { text: 'Thêm giải thích nếu cần' },
227
- ], tips: ['Hình ảnh nên sắp xếp theo trình tự thời gian rõ ràng, mỗi hình thể hiện một sự kiện'] }), _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, { className: "pb-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-blue-500 to-indigo-600 shadow-md", children: _jsx(ListChecks, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold text-gray-900", children: "K\u1EC3 c\u00E2u chuy\u1EC7n theo h\u00ECnh" }), _jsx("p", { className: "text-sm text-gray-500 mt-0.5", children: "H\u1ECDc sinh nh\u00ECn h\u00ECnh \u1EA3nh v\u00E0 k\u1EC3 l\u1EA1i c\u00E2u chuy\u1EC7n" })] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: !watchedAudioUrl || watchedExpectedAnswers.filter((a) => a.trim()).length === 0, className: "gap-2 border-blue-200 text-blue-600 hover:bg-blue-50 hover:text-blue-700", children: [_jsx(Eye, { className: "h-4 w-4" }), "Preview"] })] }) })] }), _jsxs(Form, { ...form, children: [_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-purple-100/50", children: [_jsx("div", { className: "h-1.5 bg-gradient-to-r from-purple-500 via-fuchsia-500 to-pink-500" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-purple-500 to-fuchsia-600 shadow-md", children: _jsx(BookOpen, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-lg font-bold text-gray-900", children: "H\u00ECnh \u1EA3nh c\u00E2u chuy\u1EC7n" }), _jsx("p", { className: "text-sm text-gray-500", children: "Th\u00EAm h\u00ECnh \u1EA3nh theo th\u1EE9 t\u1EF1 \u2014 H\u1ECDc sinh s\u1EBD nh\u00ECn h\u00ECnh v\u00E0 k\u1EC3 l\u1EA1i c\u00E2u chuy\u1EC7n" })] })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("span", { className: `inline-flex items-center rounded-full px-3 py-1 text-xs font-bold ${uploadedCount > 0
228
- ? 'bg-green-100 text-green-700 border border-green-200'
229
- : 'bg-orange-100 text-orange-600 border border-orange-200'}`, children: [uploadedCount, " \u1EA3nh"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addImage, className: "gap-2 border-purple-200 text-purple-600 hover:bg-purple-50", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm \u1EA3nh"] })] })] }) }), _jsx(CardContent, { children: _jsx(FormField, { control: control, name: "images", render: () => (_jsxs(FormItem, { children: [_jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: watchedImages.map((imgUrl, index) => (_jsxs(Card, { className: "border border-gray-200", children: [_jsx(CardHeader, { className: "pb-2 pt-3 px-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("span", { className: "text-sm font-semibold text-gray-600 flex items-center gap-2", children: [_jsx("span", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-purple-100 text-purple-700 text-xs font-bold", children: index + 1 }), "\u1EA2nh ", index + 1] }), _jsxs("div", { className: "flex items-center gap-1", children: [imgUrl && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => clearImage(index), className: "h-7 w-7 rounded-full text-orange-400 hover:text-orange-600 hover:bg-orange-50", title: "X\u00F3a \u1EA3nh", children: _jsx(ImageIcon, { className: "h-3.5 w-3.5" }) })), watchedImages.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeImage(index), className: "h-7 w-7 rounded-full text-red-400 hover:text-red-600 hover:bg-red-50", title: "X\u00F3a slot", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] })] }) }), _jsxs(CardContent, { className: "space-y-2 px-4 pb-4", children: [imgUrl ? (_jsx(ImagePreview, { imageUrl: imgUrl, index: index })) : (_jsx(FileUpload, { id: `story-img-${index}`, label: "", accept: "image/*", value: "", onChange: (url) => handleImageChange(index, url), placeholder: `Upload ảnh ${index + 1}`, maxSize: 5, autoUpload: true, prefix: "questions/story-images" })), form.formState.errors?.images?.[index] && (_jsxs("p", { className: "text-sm font-medium text-destructive", children: ["Vui l\u00F2ng t\u1EA3i l\u00EAn \u1EA3nh ", index + 1] }))] })] }, index))) }), form.formState.errors?.images?.message && (_jsx(FormMessage, {}))] })) }) })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-indigo-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-indigo-400 to-purple-400" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-indigo-500 to-purple-600 shadow-sm", children: _jsx(Mic, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-lg font-semibold text-gray-800", children: "C\u00E2u h\u1ECFi: K\u1EC3 c\u00E2u chuy\u1EC7n" }), _jsx("p", { className: "text-sm text-gray-500", children: "H\u1ECDc sinh nghe audio, nh\u00ECn c\u00E1c h\u00ECnh \u1EA3nh v\u00E0 m\u00F4 t\u1EA3 c\u00E2u chuy\u1EC7n b\u1EB1ng l\u1EDDi" })] })] }) }), _jsxs(CardContent, { className: "space-y-6", children: [_jsx("div", { className: "rounded-xl border border-gray-200 bg-gray-50 p-4", children: _jsxs("div", { className: "flex flex-col md:flex-row gap-6", children: [_jsx("div", { className: "flex-1", children: _jsx(PointsInput, { ...form.register('points', { valueAsNumber: true }), error: form.formState.errors?.points?.message }) }), _jsx("div", { className: "flex-1 flex flex-col justify-center pt-2" })] }) }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(Volume2, { className: "h-4 w-4 text-indigo-500" }), "File Audio c\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(FormField, { control: control, name: "audioUrl", render: ({ field }) => (_jsxs(FormItem, { children: [_jsx(FormControl, { children: _jsx(FileUpload, { id: "audio-story-images", label: "", accept: "audio/*", value: field.value, onChange: (url) => { field.onChange(url); clearErrors('audioUrl'); }, maxSize: 10, placeholder: "T\u1EA3i l\u00EAn file \u00E2m thanh (mp3, wav...)", autoUpload: true, prefix: "listen-and-speak" }) }), _jsx(FormMessage, {})] })) })] })] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-green-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-green-400 to-emerald-500" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-lg bg-gradient-to-br from-green-500 to-emerald-600 shadow-sm", children: _jsx(MessageSquareText, { className: "h-4 w-4 text-white" }) }), _jsx("div", { children: _jsxs(CardTitle, { className: "text-lg font-semibold text-gray-800", children: ["\u0110\u00E1p \u00E1n mong mu\u1ED1n (", watchedExpectedAnswers.filter((a) => a.trim()).length, ")"] }) })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addExpectedAnswer, className: "gap-2 border-green-200 text-green-600 hover:bg-green-50", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm \u0111\u00E1p \u00E1n"] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-blue-100 bg-blue-50/60 px-3 py-2.5", children: [_jsx(Info, { className: "h-4 w-4 text-blue-500 mt-0.5 flex-shrink-0" }), _jsxs("div", { className: "text-xs text-blue-700 space-y-1", children: [_jsx("p", { children: "Nh\u1EADp c\u00E1c c\u00E2u tr\u1EA3 l\u1EDDi mong mu\u1ED1n. C\u00F3 th\u1EC3 th\u00EAm nhi\u1EC1u \u0111\u00E1p \u00E1n n\u1EBFu c\u00F3 nhi\u1EC1u c\u00E1ch tr\u1EA3 l\u1EDDi \u0111\u00FAng." }), _jsxs("p", { children: [_jsx("span", { className: "font-semibold", children: "\u2022 Ch\u1EA5m th\u1EE7 c\u00F4ng:" }), " Gi\u00E1o vi\u00EAn d\u00F9ng c\u00E1c \u0111\u00E1p \u00E1n n\u00E0y l\u00E0m tham kh\u1EA3o \u0111\u1EC3 ch\u1EA5m \u0111i\u1EC3m."] }), _jsxs("p", { children: [_jsx("span", { className: "font-semibold", children: "\u2022 Ch\u1EA5m t\u1EF1 \u0111\u1ED9ng (AI):" }), " H\u1EC7 th\u1ED1ng d\u00F9ng AI so s\u00E1nh gi\u1ECDng n\u00F3i c\u1EE7a h\u1ECDc sinh v\u1EDBi c\u00E1c \u0111\u00E1p \u00E1n \u0111\u1EC3 t\u1EF1 \u0111\u1ED9ng ch\u1EA5m \u0111i\u1EC3m."] })] })] }), _jsxs("div", { className: "space-y-3", children: [watchedExpectedAnswers.map((answer, index) => (_jsxs("div", { className: "flex items-start gap-3 group/answer", children: [_jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-green-100 text-green-700 font-semibold shadow-sm text-sm", children: index + 1 }), _jsxs("div", { className: "flex-1", children: [_jsx(Input, { placeholder: `VD: ${index === 0 ? 'First, the boy went to the park.' : 'Then he saw a dog and played with it.'}`, value: answer || '', onChange: (e) => handleExpectedAnswerChange(index, e.target.value), className: "border-gray-200 focus:border-green-300 focus:ring-green-200" }), form.formState.errors?.expectedAnswers?.[index]?.message && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: form.formState.errors.expectedAnswers[index]?.message }))] }), watchedExpectedAnswers.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeExpectedAnswer(index), className: "text-red-400 hover:text-red-600 hover:bg-red-50 opacity-0 group-hover/answer:opacity-100 transition-opacity", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }, index))), form.formState.errors?.expectedAnswers && !Array.isArray(form.formState.errors.expectedAnswers) && (_jsx("p", { className: "text-sm text-red-500", children: form.formState.errors.expectedAnswers.message }))] })] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-amber-400 to-orange-400" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm", children: _jsx(BookOpen, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-lg font-semibold text-gray-800", children: "Gi\u1EA3i th\u00EDch" }), _jsx("span", { className: "inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-600", children: "Kh\u00F4ng b\u1EAFt bu\u1ED9c" })] }), _jsx("p", { className: "text-sm text-gray-500 mt-0.5", children: "Gi\u1EA3i th\u00EDch cho \u0111\u00E1p \u00E1n \u0111\u00FAng \u2014 hi\u1EC3n th\u1ECB sau khi h\u1ECDc sinh ho\u00E0n th\u00E0nh c\u00E2u h\u1ECFi" })] })] }) }), _jsx(CardContent, { children: _jsx(Textarea, { ...form.register('explanation'), placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0... v\u00EC...", className: "min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none", rows: 3 }) })] })] })] }));
219
+ return (_jsxs("div", { className: "space-y-3", children: [_jsx(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(ListChecks, { className: "h-3.5 w-3.5" }), title: "K\u1EC3 c\u00E2u chuy\u1EC7n theo h\u00ECnh", points: {
220
+ value: watchedPoints,
221
+ onValueChange: (value) => form.setValue('points', value, { shouldValidate: true }),
222
+ error: form.formState.errors?.points?.message,
223
+ }, preview: {
224
+ onClick: () => setIsClientMode(true),
225
+ disabled: !watchedAudioUrl || watchedExpectedAnswers.filter((a) => a.trim()).length === 0,
226
+ } }), contentClassName: "hidden", children: null }), _jsxs(Form, { ...form, children: [_jsx(CompactFieldCard, { header: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-gray-100", children: _jsx(BookOpen, { className: "h-3.5 w-3.5 text-gray-600" }) }), _jsx("p", { className: "text-sm font-semibold text-gray-800", children: "H\u00ECnh \u1EA3nh c\u00E2u chuy\u1EC7n" })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("span", { className: `inline-flex items-center rounded-full px-3 py-1 text-xs font-bold ${uploadedCount > 0
227
+ ? 'bg-green-100 text-green-700 border border-green-200'
228
+ : 'bg-orange-100 text-orange-600 border border-orange-200'}`, children: [uploadedCount, " \u1EA3nh"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addImage, className: "gap-2 border-purple-200 text-purple-600 hover:bg-purple-50", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm \u1EA3nh"] })] })] }), children: _jsx(FormField, { control: control, name: "images", render: () => (_jsxs(FormItem, { children: [_jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: watchedImages.map((imgUrl, index) => (_jsxs(Card, { className: "border border-gray-200", children: [_jsx(CardHeader, { className: "pb-2 pt-3 px-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("span", { className: "text-sm font-semibold text-gray-600 flex items-center gap-2", children: [_jsx("span", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-purple-100 text-purple-700 text-xs font-bold", children: index + 1 }), "\u1EA2nh ", index + 1] }), _jsxs("div", { className: "flex items-center gap-1", children: [imgUrl && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => clearImage(index), className: "h-7 w-7 rounded-full text-orange-400 hover:text-orange-600 hover:bg-orange-50", title: "X\u00F3a \u1EA3nh", children: _jsx(ImageIcon, { className: "h-3.5 w-3.5" }) })), watchedImages.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeImage(index), className: "h-7 w-7 rounded-full text-red-400 hover:text-red-600 hover:bg-red-50", title: "X\u00F3a slot", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] })] }) }), _jsxs(CardContent, { className: "space-y-2 px-4 pb-4", children: [imgUrl ? (_jsx(ImagePreview, { imageUrl: imgUrl, index: index })) : (_jsx(FileUpload, { id: `story-img-${index}`, label: "", accept: "image/*", value: "", onChange: (url) => handleImageChange(index, url), placeholder: `Upload ảnh ${index + 1}`, maxSize: 5, autoUpload: true, prefix: "questions/story-images" })), form.formState.errors?.images?.[index] && (_jsxs("p", { className: "text-sm font-medium text-destructive", children: ["Vui l\u00F2ng t\u1EA3i l\u00EAn \u1EA3nh ", index + 1] }))] })] }, index))) }), form.formState.errors?.images?.message && (_jsx(FormMessage, {}))] })) }) }), _jsx(CompactFieldCard, { header: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-gray-100", children: _jsx(Mic, { className: "h-3.5 w-3.5 text-gray-600" }) }), _jsx("p", { className: "text-sm font-semibold text-gray-800", children: "C\u00E2u h\u1ECFi: K\u1EC3 c\u00E2u chuy\u1EC7n" })] }), children: _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(Volume2, { className: "h-4 w-4 text-indigo-500" }), "File Audio c\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(FormField, { control: control, name: "audioUrl", render: ({ field }) => (_jsxs(FormItem, { children: [_jsx(FormControl, { children: _jsx(FileUpload, { id: "audio-story-images", label: "", accept: "audio/*", value: field.value, onChange: (url) => { field.onChange(url); clearErrors('audioUrl'); }, maxSize: 10, placeholder: "T\u1EA3i l\u00EAn file \u00E2m thanh (mp3, wav...)", autoUpload: true, prefix: "listen-and-speak" }) }), _jsx(FormMessage, {})] })) })] }) }), _jsxs(CompactFieldCard, { header: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100", children: _jsx(MessageSquareText, { className: "h-3.5 w-3.5 text-gray-600" }) }), _jsxs("p", { className: "text-sm font-semibold text-gray-800", children: ["\u0110\u00E1p \u00E1n mong mu\u1ED1n (", watchedExpectedAnswers.filter((a) => a.trim()).length, ")"] })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addExpectedAnswer, className: "gap-2 border-green-200 text-green-600 hover:bg-green-50", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm \u0111\u00E1p \u00E1n"] })] }), children: [_jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-blue-100 bg-blue-50/60 px-3 py-2.5", children: [_jsx(Info, { className: "h-4 w-4 text-blue-500 mt-0.5 flex-shrink-0" }), _jsxs("div", { className: "text-xs text-blue-700 space-y-1", children: [_jsx("p", { children: "Nh\u1EADp c\u00E1c c\u00E2u tr\u1EA3 l\u1EDDi mong mu\u1ED1n. C\u00F3 th\u1EC3 th\u00EAm nhi\u1EC1u \u0111\u00E1p \u00E1n n\u1EBFu c\u00F3 nhi\u1EC1u c\u00E1ch tr\u1EA3 l\u1EDDi \u0111\u00FAng." }), _jsxs("p", { children: [_jsx("span", { className: "font-semibold", children: "\u2022 Ch\u1EA5m th\u1EE7 c\u00F4ng:" }), " Gi\u00E1o vi\u00EAn d\u00F9ng c\u00E1c \u0111\u00E1p \u00E1n n\u00E0y l\u00E0m tham kh\u1EA3o \u0111\u1EC3 ch\u1EA5m \u0111i\u1EC3m."] }), _jsxs("p", { children: [_jsx("span", { className: "font-semibold", children: "\u2022 Ch\u1EA5m t\u1EF1 \u0111\u1ED9ng (AI):" }), " H\u1EC7 th\u1ED1ng d\u00F9ng AI so s\u00E1nh gi\u1ECDng n\u00F3i c\u1EE7a h\u1ECDc sinh v\u1EDBi c\u00E1c \u0111\u00E1p \u00E1n \u0111\u1EC3 t\u1EF1 \u0111\u1ED9ng ch\u1EA5m \u0111i\u1EC3m."] })] })] }), _jsxs("div", { className: "space-y-3", children: [watchedExpectedAnswers.map((answer, index) => (_jsxs("div", { className: "flex items-start gap-3 group/answer", children: [_jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-green-100 text-green-700 font-semibold shadow-sm text-sm", children: index + 1 }), _jsxs("div", { className: "flex-1", children: [_jsx(Input, { placeholder: `VD: ${index === 0 ? 'First, the boy went to the park.' : 'Then he saw a dog and played with it.'}`, value: answer || '', onChange: (e) => handleExpectedAnswerChange(index, e.target.value), className: "border-gray-200 focus:border-green-300 focus:ring-green-200" }), form.formState.errors?.expectedAnswers?.[index]?.message && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: form.formState.errors.expectedAnswers[index]?.message }))] }), watchedExpectedAnswers.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeExpectedAnswer(index), className: "text-red-400 hover:text-red-600 hover:bg-red-50 opacity-0 group-hover/answer:opacity-100 transition-opacity", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }, index))), form.formState.errors?.expectedAnswers && !Array.isArray(form.formState.errors.expectedAnswers) && (_jsx("p", { className: "text-sm text-red-500", children: form.formState.errors.expectedAnswers.message }))] })] }), _jsx(CompactExplanationToggle, { value: watchedExplanation || '', onValueChange: (value) => form.setValue('explanation', value, { shouldValidate: true }), defaultVisible: Boolean(watchedExplanation) })] })] }));
230
229
  }
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Card, CardContent } from '../../../../components/ui/card';
4
4
  import { Input } from '../../../../components/ui/input';
5
5
  import { Label } from '../../../../components/ui/label';
6
- import { CheckCircle2, XCircle, BookOpen } from 'lucide-react';
6
+ import { CheckCircle2, XCircle, BookOpen, Grid3X3 } from 'lucide-react';
7
7
  import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
8
8
  import { useState } from 'react';
9
9
  function OptionCard({ option }) {
@@ -27,7 +27,7 @@ export function MatchByWritingAnswerClient({ questionData, onSubmit, isReviewMod
27
27
  const isCorrect = isReviewMode && userAnswer !== undefined
28
28
  ? userAnswer.trim().toLowerCase() === questionData.correctAnswer.trim().toLowerCase()
29
29
  : undefined;
30
- return (_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-teal-400 to-cyan-500" }), _jsxs(CardContent, { className: "p-6 space-y-6", children: [questionData.options && questionData.options.length > 0 && (_jsxs("div", { className: "rounded-xl border border-purple-200 bg-gradient-to-r from-purple-50 to-violet-50 p-4", children: [_jsx("p", { className: "mb-3 text-sm font-semibold text-purple-700", children: "C\u00E1c l\u1EF1a ch\u1ECDn:" }), _jsx("div", { className: `grid gap-3 ${questionData.options.some((o) => o.imageUrl) ? 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4' : 'grid-cols-1 sm:grid-cols-2'}`, children: questionData.options.map((option, index) => (_jsx(OptionCard, { option: option }, index))) })] })), _jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-5", children: [isExample && (_jsxs("div", { className: "mb-2 inline-flex items-center gap-1.5 rounded-full bg-amber-100 border border-amber-300 px-3 py-1", children: [_jsx(BookOpen, { className: "h-3.5 w-3.5 text-amber-600" }), _jsx("span", { className: "text-xs font-semibold text-amber-700 uppercase tracking-wide", children: "Example" })] })), _jsxs("div", { className: "flex items-center gap-4", children: [(questionImageUrl || questionData.imageUrl) ? (_jsx("div", { className: "relative h-20 w-20 flex-shrink-0 overflow-hidden rounded-lg border border-gray-200 bg-gray-50 shadow-sm", children: _jsx("img", { src: questionImageUrl || questionData.imageUrl, alt: questionData.question, className: "h-full w-full object-contain" }) })) : null, _jsxs("div", { className: "flex-1 space-y-2", children: [_jsx(Label, { className: "text-base font-semibold text-gray-800", children: questionData.question }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Input, { value: displayedAnswer, onChange: (e) => {
30
+ return (_jsxs(Card, { className: "overflow-hidden border border-gray-200 bg-white shadow-sm", children: [_jsx("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-3 py-2", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gradient-to-br from-teal-500 to-cyan-600 text-white", children: _jsx(Grid3X3, { className: "h-3.5 w-3.5" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Gh\u00E9p \u0111\u00E1p \u00E1n b\u1EB1ng c\u00E1ch vi\u1EBFt" })] }) }), _jsxs(CardContent, { className: "space-y-3 p-3", children: [questionData.options && questionData.options.length > 0 && (_jsxs("div", { className: "rounded-xl border border-purple-200 bg-gradient-to-r from-purple-50 to-violet-50 p-4", children: [_jsx("p", { className: "mb-3 text-sm font-semibold text-purple-700", children: "C\u00E1c l\u1EF1a ch\u1ECDn:" }), _jsx("div", { className: `grid gap-3 ${questionData.options.some((o) => o.imageUrl) ? 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4' : 'grid-cols-1 sm:grid-cols-2'}`, children: questionData.options.map((option, index) => (_jsx(OptionCard, { option: option }, index))) })] })), _jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-5", children: [isExample && (_jsxs("div", { className: "mb-2 inline-flex items-center gap-1.5 rounded-full bg-amber-100 border border-amber-300 px-3 py-1", children: [_jsx(BookOpen, { className: "h-3.5 w-3.5 text-amber-600" }), _jsx("span", { className: "text-xs font-semibold text-amber-700 uppercase tracking-wide", children: "Example" })] })), _jsxs("div", { className: "flex items-center gap-4", children: [(questionImageUrl || questionData.imageUrl) ? (_jsx("div", { className: "relative h-20 w-20 flex-shrink-0 overflow-hidden rounded-lg border border-gray-200 bg-gray-50 shadow-sm", children: _jsx("img", { src: questionImageUrl || questionData.imageUrl, alt: questionData.question, className: "h-full w-full object-contain" }) })) : null, _jsxs("div", { className: "flex-1 space-y-2", children: [_jsx(Label, { className: "text-base font-semibold text-gray-800", children: questionData.question }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Input, { value: displayedAnswer, onChange: (e) => {
31
31
  setAnswer(e.target.value);
32
32
  onSubmit?.(e.target.value);
33
33
  }, placeholder: "Vi\u1EBFt \u0111\u00E1p \u00E1n...", disabled: isReviewMode || isExample, className: `max-w-xs border-2 text-lg font-medium ${isReviewMode
@@ -2,15 +2,11 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { ImagePreview } from '../../../../components/ui/image-preview';
4
4
  import { useState, useRef, useEffect } from 'react';
5
- import { Button } from '../../../../components/ui/button';
6
5
  import { Label } from '../../../../components/ui/label';
7
6
  import { Input } from '../../../../components/ui/input';
8
- import { Textarea } from '../../../../components/ui/textarea';
9
- import { PointsInput } from '../../../../components/ui/points-input';
10
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
11
- import { Pencil, Eye, ImageIcon, MessageSquare, HelpCircle, BookOpen, Grid3X3, Users } from 'lucide-react';
7
+ import { ImageIcon, MessageSquare, HelpCircle, Grid3X3, Users } from 'lucide-react';
8
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
12
9
  import { FileUpload } from '../../../../components/ui/file-upload';
13
- import { Checkbox } from '../../../../components/ui/checkbox';
14
10
  import { MatchByWritingAnswerClient } from './MatchByWritingAnswerClient';
15
11
  import { MatchAnswerGroupCard } from '../../groups/MatchAnswerGroupCard';
16
12
  import { useDebouncedCallback, usePresignedFileUrl } from '../../../../shared/lib/hooks';
@@ -85,6 +81,7 @@ function MatchByWritingAnswerContent({ initialData, onSave, onSaveToQuestionBank
85
81
  const [isClientMode, setIsClientMode] = useState(false);
86
82
  const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
87
83
  const [imagePreviewUrl, setImagePreviewUrl] = useState('');
84
+ const [isImageInputVisible, setIsImageInputVisible] = useState(() => Boolean(initialData?.imageUrl));
88
85
  // Inline validation errors
89
86
  const [validationErrors, setValidationErrors] = useState({});
90
87
  // Refs
@@ -332,27 +329,32 @@ function MatchByWritingAnswerContent({ initialData, onSave, onSaveToQuestionBank
332
329
  isExample,
333
330
  options,
334
331
  };
335
- return (_jsxs("div", { className: "space-y-4", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Match by Writing Answer (Preview)" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Ch\u1EBF \u0111\u1ED9 xem tr\u01B0\u1EDBc - Hi\u1EC3n th\u1ECB \u0111\u00E1p \u00E1n \u0111\u00FAng" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(false), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), "Quay l\u1EA1i Edit Mode"] })] }) }) }), _jsx(MatchByWritingAnswerClient, { questionData: questionData, isReviewMode: true, autoFillCorrectAnswers: true, explanation: explanation })] }));
332
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(MatchByWritingAnswerClient, { questionData: questionData, isReviewMode: true, autoFillCorrectAnswers: true, explanation: explanation })] }));
336
333
  }
337
334
  // Creator UI
338
- return (_jsxs("div", { className: "space-y-4", children: [groups && groups.length > 0 && (_jsx(MatchAnswerGroupCard, { currentGroupNumber: currentGroupNumber, groupTitle: groupTitle, groupImageUrl: groupImageUrl, groupImagePreviewUrl: groupImagePreviewUrl, options: options, groupConfig: groupConfig, showGroupTitle: showGroupTitle, showGroupImageUrl: showGroupImageUrl, onGroupTitleChange: setGroupTitle, onGroupImageChange: setGroupImageUrl, onGroupImagePreviewUrlChange: setGroupImagePreviewUrl, onUpdateOption: updateOption, onRemoveOption: removeOption, onAddOption: addOption })), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50", children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-blue-600 shadow-sm", children: _jsx(Grid3X3, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-base font-bold text-gray-900", children: "Gh\u00E9p \u0111\u00E1p \u00E1n b\u1EB1ng c\u00E1ch vi\u1EBFt" }), _jsx("p", { className: "text-xs text-gray-500", children: "Nh\u00ECn h\u00ECnh v\u00E0 vi\u1EBFt \u0111\u00E1p \u00E1n t\u01B0\u01A1ng \u1EE9ng" })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [headerExtra, _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: !question, className: "gap-2 border-teal-200 text-teal-600 hover:bg-teal-50 hover:text-teal-700", children: [_jsx(Eye, { className: "h-4 w-4" }), "Xem tr\u01B0\u1EDBc"] })] })] }) }), _jsxs(CardContent, { className: "space-y-4 px-4 pb-4", children: [_jsxs("div", { className: "flex items-center gap-6", children: [_jsx(PointsInput, { value: points || 1, onChange: (e) => setPoints(parseFloat(e.target.value) || 1) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Checkbox, { id: "isExample", checked: isExample || false, onCheckedChange: (checked) => setIsExample(!!checked) }), _jsx(Label, { htmlFor: "isExample", className: "flex items-center gap-1.5 cursor-pointer text-sm font-medium text-gray-700", children: "\u0110\u00E1nh d\u1EA5u l\u00E0 c\u00E2u h\u1ECFi Example" })] })] }), viewMode === 'CONVERSATION' && (_jsxs("div", { className: "space-y-3", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(Users, { className: "h-4 w-4 text-indigo-500" }), "H\u1ED9i tho\u1EA1i ", _jsx("span", { className: "text-sm font-normal text-gray-500", children: "(tu\u1EF3 ch\u1ECDn - d\u00F9ng cho c\u00E2u h\u1ECFi d\u1EA1ng h\u1ED9i tho\u1EA1i)" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4 rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "askerName", className: "text-sm font-medium text-gray-600", children: "T\u00EAn ng\u01B0\u1EDDi h\u1ECFi" }), _jsx(Input, { id: "askerName", value: askerName, onChange: (e) => setAskerName(e.target.value), placeholder: "VD: John, Mary, Teacher...", className: "border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "answererName", className: "text-sm font-medium text-gray-600", children: "T\u00EAn ng\u01B0\u1EDDi tr\u1EA3 l\u1EDDi" }), _jsx(Input, { id: "answererName", value: answererName, onChange: (e) => setAnswererName(e.target.value), placeholder: "VD: Tom, Anna, Student...", className: "border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200" })] })] })] })), showQuestionField && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "question", className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-4 w-4 text-teal-500" }), "T\u00EAn / C\u00E2u h\u1ECFi ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(t\u00F9y ch\u1ECDn)" })] }), _jsx(Input, { id: "question", value: question, onChange: (e) => {
339
- setQuestion(e.target.value);
340
- if (validationErrors.question) {
341
- setValidationErrors((prev) => ({ ...prev, question: undefined }));
342
- }
343
- }, placeholder: "Nh\u1EADp t\u00EAn ho\u1EB7c c\u00E2u h\u1ECFi... (vd: Mum, Dad, Julia)", className: `border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200 ${validationErrors.question ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.question && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.question }))] })), showImageUrlField && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-teal-500" }), "H\u00ECnh \u1EA3nh c\u00E2u h\u1ECFi ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(t\u00F9y ch\u1ECDn)" })] }), _jsx(FileUpload, { id: "question-image", label: "", accept: "image/*", value: imageUrl || '', onChange: (url) => {
344
- setImageUrl(url);
345
- if (validationErrors.imageUrl) {
346
- setValidationErrors((prev) => ({ ...prev, imageUrl: undefined }));
347
- }
348
- }, onPresignedUrlChange: setImagePreviewUrl, maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" }), displayPreviewUrl && (_jsx(ImagePreview, { src: displayPreviewUrl, alt: "Preview", className: "mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200" }))] })), showAnswerField && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "correctAnswer", className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(HelpCircle, { className: "h-4 w-4 text-green-500" }), "\u0110\u00E1p \u00E1n \u0111\u00FAng ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsxs("select", { id: "correctAnswer", value: correctAnswer, onChange: (e) => {
349
- setCorrectAnswer(e.target.value);
350
- if (validationErrors.correctAnswer) {
351
- setValidationErrors((prev) => ({ ...prev, correctAnswer: undefined }));
352
- }
353
- }, className: `flex h-10 w-full rounded-md border px-3 py-2 text-sm bg-white transition-all focus:outline-none focus:ring-2 focus:ring-offset-0 ${validationErrors.correctAnswer ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : 'border-gray-200 focus:border-green-300 focus:ring-green-200'}`, children: [_jsx("option", { value: "", children: "-- Ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng --" }), options
354
- .filter((opt) => opt.answer && opt.answer.trim() !== '')
355
- .map((opt, idx) => (_jsx("option", { value: opt.answer, children: opt.answer }, idx)))] }), validationErrors.correctAnswer && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.correctAnswer }))] })), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100", children: _jsx("span", { className: "text-lg", children: "\u274C" }) }), _jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("ul", { className: "mt-1 list-inside list-disc text-red-700", children: externalErrors.map((error, index) => (_jsx("li", { children: error }, index))) })] })] }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50", children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm", children: _jsx(BookOpen, { className: "h-3.5 w-3.5 text-white" }) }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-base font-semibold text-gray-800", children: "Gi\u1EA3i th\u00EDch" }), _jsx("span", { className: "inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-600", children: "Kh\u00F4ng b\u1EAFt bu\u1ED9c" })] }), _jsx("p", { className: "text-xs text-gray-500 mt-0.5", children: "Gi\u1EA3i th\u00EDch cho \u0111\u00E1p \u00E1n \u0111\u00FAng \u2014 hi\u1EC3n th\u1ECB sau khi h\u1ECDc sinh ho\u00E0n th\u00E0nh c\u00E2u h\u1ECFi" })] })] }) }), _jsx(CardContent, { className: "px-4 pb-4", children: _jsx(Textarea, { id: "explanation", value: explanation, onChange: (e) => setExplanation(e.target.value), placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0... v\u00EC...", rows: 3, className: "min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none" }) })] })] }));
335
+ return (_jsxs("div", { className: "space-y-3", children: [groups && groups.length > 0 && (_jsx(MatchAnswerGroupCard, { currentGroupNumber: currentGroupNumber, groupTitle: groupTitle, groupImageUrl: groupImageUrl, groupImagePreviewUrl: groupImagePreviewUrl, options: options, groupConfig: groupConfig, showGroupTitle: showGroupTitle, showGroupImageUrl: showGroupImageUrl, onGroupTitleChange: setGroupTitle, onGroupImageChange: setGroupImageUrl, onGroupImagePreviewUrlChange: setGroupImagePreviewUrl, onUpdateOption: updateOption, onRemoveOption: removeOption, onAddOption: addOption })), _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Grid3X3, { className: "h-3.5 w-3.5" }), title: "Gh\u00E9p \u0111\u00E1p \u00E1n b\u1EB1ng c\u00E1ch vi\u1EBFt", points: { value: points || 1, onValueChange: setPoints }, example: { checked: isExample || false, onChange: setIsExample }, headerExtra: headerExtra, preview: {
336
+ onClick: () => setIsClientMode(true),
337
+ disabled: !question,
338
+ } }), children: [viewMode === 'CONVERSATION' && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-1.5 text-sm font-semibold text-gray-800", children: [_jsx(Users, { className: "h-3.5 w-3.5 text-indigo-500" }), "H\u1ED9i tho\u1EA1i ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(tu\u1EF3 ch\u1ECDn)" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-2.5 rounded-lg border border-gray-200 bg-gray-50 p-2.5", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "askerName", className: "text-xs font-medium text-gray-600", children: "T\u00EAn ng\u01B0\u1EDDi h\u1ECFi" }), _jsx(Input, { id: "askerName", value: askerName, onChange: (e) => setAskerName(e.target.value), placeholder: "VD: John, Mary, Teacher...", className: "h-8 border-gray-200 bg-white" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "answererName", className: "text-xs font-medium text-gray-600", children: "T\u00EAn ng\u01B0\u1EDDi tr\u1EA3 l\u1EDDi" }), _jsx(Input, { id: "answererName", value: answererName, onChange: (e) => setAnswererName(e.target.value), placeholder: "VD: Tom, Anna, Student...", className: "h-8 border-gray-200 bg-white" })] })] })] })), showQuestionField && (_jsxs("div", { className: "space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs(Label, { htmlFor: "question", className: "flex items-center gap-1.5 text-sm font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-3.5 w-3.5 text-teal-500" }), "T\u00EAn / C\u00E2u h\u1ECFi ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(t\u00F9y ch\u1ECDn)" })] }), showImageUrlField && (_jsx("button", { type: "button", title: "Th\u00EAm h\u00ECnh \u1EA3nh", onClick: () => setIsImageInputVisible(true), className: `inline-flex h-8 w-8 items-center justify-center rounded-md transition-colors ${imageUrl || isImageInputVisible
339
+ ? 'bg-indigo-600 text-white shadow-sm'
340
+ : 'bg-indigo-50 text-indigo-600 hover:bg-indigo-100'}`, children: _jsx(ImageIcon, { className: "h-4 w-4" }) }))] }), _jsx(Input, { id: "question", value: question, onChange: (e) => {
341
+ setQuestion(e.target.value);
342
+ if (validationErrors.question) {
343
+ setValidationErrors((prev) => ({ ...prev, question: undefined }));
344
+ }
345
+ }, placeholder: "Nh\u1EADp t\u00EAn ho\u1EB7c c\u00E2u h\u1ECFi... (vd: Mum, Dad, Julia)", className: `h-8 border-gray-200 bg-white ${validationErrors.question ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.question && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.question }))] })), showImageUrlField && !showQuestionField && !isImageInputVisible && (_jsxs("button", { type: "button", title: "Th\u00EAm h\u00ECnh \u1EA3nh", onClick: () => setIsImageInputVisible(true), className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-gray-200 bg-white px-2.5 text-xs font-medium text-gray-500 hover:text-gray-700", children: [_jsx(ImageIcon, { className: "h-3.5 w-3.5" }), "Th\u00EAm h\u00ECnh \u1EA3nh"] })), showImageUrlField && isImageInputVisible && (_jsxs("div", { className: "space-y-2", children: [_jsx(FileUpload, { id: "question-image", label: "", accept: "image/*", value: imageUrl || '', onChange: (url) => {
346
+ setImageUrl(url);
347
+ if (validationErrors.imageUrl) {
348
+ setValidationErrors((prev) => ({ ...prev, imageUrl: undefined }));
349
+ }
350
+ }, onPresignedUrlChange: setImagePreviewUrl, maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" }), displayPreviewUrl && (_jsx(ImagePreview, { src: displayPreviewUrl, alt: "Preview", className: "mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200" }))] })), showAnswerField && (_jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { htmlFor: "correctAnswer", className: "flex items-center gap-1.5 text-sm font-semibold text-gray-800", children: [_jsx(HelpCircle, { className: "h-3.5 w-3.5 text-green-500" }), "\u0110\u00E1p \u00E1n \u0111\u00FAng ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsxs("select", { id: "correctAnswer", value: correctAnswer, onChange: (e) => {
351
+ setCorrectAnswer(e.target.value);
352
+ if (validationErrors.correctAnswer) {
353
+ setValidationErrors((prev) => ({ ...prev, correctAnswer: undefined }));
354
+ }
355
+ }, className: `flex h-8 w-full rounded-md border px-3 py-1 text-sm bg-white focus:outline-none focus:ring-2 focus:ring-offset-0 ${validationErrors.correctAnswer ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : 'border-gray-200 focus:border-green-300 focus:ring-green-200'}`, children: [_jsx("option", { value: "", children: "-- Ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng --" }), options
356
+ .filter((opt) => opt.answer && opt.answer.trim() !== '')
357
+ .map((opt, idx) => (_jsx("option", { value: opt.answer, children: opt.answer }, idx)))] }), validationErrors.correctAnswer && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.correctAnswer }))] })), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-red-200 bg-red-50 p-2 text-xs text-red-700", children: [_jsx("span", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("span", { children: externalErrors.join(' · ') })] })), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] }));
356
358
  }
357
359
  export function MatchByWritingAnswerCreator(props) {
358
360
  return _jsx(MatchByWritingAnswerContent, { ...props });
@@ -58,17 +58,17 @@ export function MatchColumnsToMakeSentencesClient({ questionData, isReviewMode =
58
58
  || '—';
59
59
  const isCorrect = isReviewMode &&
60
60
  current.sentence.trim().toLowerCase() === item.sentence.trim().toLowerCase();
61
- return (_jsxs("div", { className: "rounded-xl border border-gray-200 bg-white p-4 shadow-sm", children: [_jsxs("div", { className: "mb-3 flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", item.questionNumber || itemIndex + 1] }), item.isExample && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-0.5 text-xs font-semibold text-amber-700", children: [_jsx(Star, { className: "h-3 w-3" }), "Example"] }))] }), _jsxs("span", { className: "text-xs text-gray-500", children: [item.points || 0, " \u0111i\u1EC3m"] })] }), _jsxs("div", { className: "grid gap-3 md:grid-cols-[minmax(0,1fr)_repeat(2,minmax(0,1fr))]", children: [_jsx("div", { className: "rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-sm font-semibold text-slate-800", children: stemText }), choiceColumns.map((column, choiceIndex) => {
62
- const selectedId = current.fragmentIds[choiceIndex + 1] || '';
63
- return (_jsxs("select", { className: cn('rounded-lg border-2 px-3 py-2 text-sm', isReviewMode && selectedId === item.fragmentIds[choiceIndex + 1]
64
- ? 'border-green-400 bg-green-50'
65
- : selectedId
66
- ? 'border-blue-400 bg-blue-50'
67
- : 'border-gray-200 bg-white'), disabled: !canSelect, value: selectedId, onChange: (event) => handleChoiceChange(itemIndex, choiceIndex, event.target.value), children: [_jsxs("option", { value: "", children: ["Ch\u1ECDn ", column.label || column.id] }), column.fragments.map((fragment) => (_jsxs("option", { value: fragment.id, children: [fragment.id, ". ", fragment.text] }, fragment.id)))] }, `${itemIndex}-${column.id}`));
68
- })] }), _jsx("p", { className: cn('mt-3 text-sm', isReviewMode
69
- ? isCorrect
70
- ? 'text-green-700'
71
- : 'text-red-700'
72
- : 'text-slate-600'), children: current.sentence || 'Chưa ghép câu' }), isReviewMode && !item.isExample && (_jsxs("p", { className: "mt-1 text-xs text-slate-500", children: ["\u0110\u00E1p \u00E1n: ", item.sentence] }))] }, `${item.questionNumber}-${itemIndex}`));
61
+ return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", item.questionNumber || itemIndex + 1] }), item.isExample && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-0.5 text-xs font-semibold text-amber-700", children: [_jsx(Star, { className: "h-3 w-3" }), "Example"] }))] }), _jsxs("span", { className: "text-xs text-gray-500", children: [item.points || 0, " \u0111i\u1EC3m"] })] }), _jsxs("div", { className: "space-y-3 p-3", children: [_jsxs("div", { className: "grid gap-3 md:grid-cols-[minmax(0,1fr)_repeat(2,minmax(0,1fr))]", children: [_jsx("div", { className: "rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-sm font-semibold text-slate-800", children: stemText }), choiceColumns.map((column, choiceIndex) => {
62
+ const selectedId = current.fragmentIds[choiceIndex + 1] || '';
63
+ return (_jsxs("select", { className: cn('rounded-lg border-2 px-3 py-2 text-sm', isReviewMode && selectedId === item.fragmentIds[choiceIndex + 1]
64
+ ? 'border-green-400 bg-green-50'
65
+ : selectedId
66
+ ? 'border-blue-400 bg-blue-50'
67
+ : 'border-gray-200 bg-white'), disabled: !canSelect, value: selectedId, onChange: (event) => handleChoiceChange(itemIndex, choiceIndex, event.target.value), children: [_jsxs("option", { value: "", children: ["Ch\u1ECDn ", column.label || column.id] }), column.fragments.map((fragment) => (_jsxs("option", { value: fragment.id, children: [fragment.id, ". ", fragment.text] }, fragment.id)))] }, `${itemIndex}-${column.id}`));
68
+ })] }), _jsx("p", { className: cn('text-sm', isReviewMode
69
+ ? isCorrect
70
+ ? 'text-green-700'
71
+ : 'text-red-700'
72
+ : 'text-slate-600'), children: current.sentence || 'Chưa ghép câu' }), isReviewMode && !item.isExample && (_jsxs("p", { className: "text-xs text-slate-500", children: ["\u0110\u00E1p \u00E1n: ", item.sentence] }))] })] }, `${item.questionNumber}-${itemIndex}`));
73
73
  }) }), isReviewMode && questionData.explanation && (_jsxs("div", { className: "flex items-start gap-2 rounded-xl border border-amber-100 bg-amber-50 px-4 py-3 text-sm text-amber-800", children: [_jsx(Lightbulb, { className: "mt-0.5 h-4 w-4 flex-shrink-0" }), questionData.explanation] }))] }));
74
74
  }
@@ -5,11 +5,11 @@ import { Button } from '../../../../components/ui/button';
5
5
  import { Label } from '../../../../components/ui/label';
6
6
  import { Input } from '../../../../components/ui/input';
7
7
  import { PointsInput } from '../../../../components/ui/points-input';
8
- import { Textarea } from '../../../../components/ui/textarea';
9
8
  import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
10
9
  import { Switch } from '../../../../components/ui/switch';
11
10
  import { useDebouncedCallback } from '../../../../shared/lib/hooks';
12
- import { Eye, Pencil, Plus, Star, Trash2 } from 'lucide-react';
11
+ import { Columns3, Plus, Star, Trash2 } from 'lucide-react';
12
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
13
13
  import { MatchColumnsToMakeSentencesClient } from './MatchColumnsToMakeSentencesClient';
14
14
  import { MATCH_COLUMNS_DEFAULT_INSTRUCTION } from '../../_shared/types/match-columns-to-make-sentences.type';
15
15
  function parsePointsValue(raw, allowZero = false) {
@@ -216,7 +216,10 @@ export function MatchColumnsToMakeSentencesCreator({ initialData, onChange, exte
216
216
  const displayErrors = externalErrors && externalErrors.length > 0 ? externalErrors : errors;
217
217
  const stemColumn = getMatchColumnsStemColumn(columns);
218
218
  const choiceColumns = getMatchColumnsChoiceColumns(columns);
219
- return (_jsxs("div", { className: "space-y-6", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("h3", { className: "text-lg font-medium text-gray-900", children: "T\u1EA1o c\u00E2u h\u1ECFi Gh\u00E9p c\u1ED9t th\u00E0nh c\u00E2u" }), _jsx("p", { className: "text-sm text-gray-500", children: "C\u1ED9t stem c\u1ED1 \u0111\u1ECBnh theo d\u00F2ng; h\u1ECDc sinh ch\u1ECDn 1 fragment m\u1ED7i c\u1ED9t choice." })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setIsClientMode((prev) => !prev), className: "gap-2", children: [isClientMode ? _jsx(Pencil, { className: "h-4 w-4" }) : _jsx(Eye, { className: "h-4 w-4" }), isClientMode ? 'Chế độ chỉnh sửa' : 'Xem trước học sinh'] })] }), displayErrors.length > 0 && (_jsx("div", { className: "rounded-lg border border-red-200 bg-red-50 p-4 text-sm text-red-700", children: _jsx("ul", { className: "list-disc space-y-1 pl-5", children: displayErrors.map((err, i) => _jsx("li", { children: err }, i)) }) })), isClientMode ? (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base font-semibold", children: "Xem tr\u01B0\u1EDBc hi\u1EC3n th\u1ECB h\u1ECDc sinh" }) }), _jsx(CardContent, { children: _jsx(MatchColumnsToMakeSentencesClient, { questionData: currentPayload }) })] })) : (_jsxs("div", { className: "space-y-6", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base font-semibold", children: "Th\u00F4ng tin chung" }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: "match-columns-title", children: "Ti\u00EAu \u0111\u1EC1" }), _jsx(Input, { id: "match-columns-title", className: "mt-1", value: title, onChange: (e) => setTitle(e.target.value) })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "match-columns-instruction", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Input, { id: "match-columns-instruction", className: "mt-1", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: MATCH_COLUMNS_DEFAULT_INSTRUCTION })] })] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { className: "text-base font-semibold", children: "C\u1ED9t fragment" }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addChoiceColumn, className: "gap-2", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm c\u1ED9t choice"] })] }), _jsx(CardContent, { className: "grid gap-4 md:grid-cols-3", children: columns.map((column, columnIndex) => (_jsxs("div", { className: "space-y-3 rounded-lg border border-gray-200 p-3", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(Input, { value: column.label || '', onChange: (e) => updateColumn(columnIndex, { label: e.target.value }), placeholder: "Nh\u00E3n c\u1ED9t" }), _jsx("span", { className: "text-xs text-slate-500", children: column.role }), column.role === 'choice' && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeColumn(columnIndex), children: _jsx(Trash2, { className: "h-4 w-4 text-red-500" }) }))] }), column.fragments.map((fragment, fragmentIndex) => (_jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { className: "w-16", value: fragment.id, onChange: (e) => updateFragment(columnIndex, fragmentIndex, { id: e.target.value }) }), _jsx(Input, { value: fragment.text, onChange: (e) => updateFragment(columnIndex, fragmentIndex, { text: e.target.value }), placeholder: "Text" }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeFragment(columnIndex, fragmentIndex), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }, `${column.id}-${fragmentIndex}`))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => addFragment(columnIndex), children: "Th\u00EAm fragment" })] }, column.id))) })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "text-base font-semibold", children: ["C\u00E2u h\u1ECFi (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addItem, className: "gap-2", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm c\u00E2u"] })] }), _jsx(CardContent, { className: "space-y-4", children: items.map((item, index) => (_jsxs("div", { className: "space-y-3 rounded-lg border border-gray-200 bg-gray-50/50 p-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", item.questionNumber || index + 1] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Switch, { id: `match-columns-example-${index}`, checked: item.isExample || false, onCheckedChange: (checked) => updateItem(index, { isExample: checked, points: checked ? 0 : 1 }) }), _jsxs(Label, { htmlFor: `match-columns-example-${index}`, className: "flex items-center gap-1 text-xs", children: [_jsx(Star, { className: "h-3 w-3 text-amber-500" }), "Example"] }), !item.isExample && (_jsx("div", { className: "w-24", children: _jsx(PointsInput, { value: item.points, allowZero: true, onChange: (event) => updateItem(index, {
220
- points: parsePointsValue(event.target.value, true),
221
- }) }) })), items.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeItem(index), children: _jsx(Trash2, { className: "h-4 w-4 text-red-500" }) }))] })] }), _jsxs("div", { className: "grid gap-3 md:grid-cols-3", children: [_jsxs("select", { className: "rounded-md border px-3 py-2 text-sm", value: item.stemFragmentId, onChange: (e) => updateItem(index, { stemFragmentId: e.target.value }), children: [_jsx("option", { value: "", children: "Ch\u1ECDn stem" }), (stemColumn?.fragments || []).map((fragment) => (_jsxs("option", { value: fragment.id, children: [fragment.id, ". ", fragment.text || '(trống)'] }, fragment.id)))] }), choiceColumns.map((column, choiceIndex) => (_jsxs("select", { className: "rounded-md border px-3 py-2 text-sm", value: item.fragmentIds[choiceIndex + 1] || '', onChange: (e) => setItemChoice(index, choiceIndex, e.target.value), children: [_jsxs("option", { value: "", children: ["Ch\u1ECDn ", column.label || column.id] }), column.fragments.map((fragment) => (_jsxs("option", { value: fragment.id, children: [fragment.id, ". ", fragment.text || '(trống)'] }, fragment.id)))] }, `${index}-${column.id}`)))] }), _jsx("p", { className: "text-sm text-slate-600", children: item.sentence || 'Câu sẽ tự ghép từ fragment đã chọn.' })] }, index))) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Gi\u1EA3i th\u00EDch" }) }), _jsx(CardContent, { children: _jsx(Textarea, { id: "match-columns-explanation", value: explanation, onChange: (event) => setExplanation(event.target.value), placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0... v\u00EC...", rows: 3 }) })] })] }))] }));
219
+ if (isClientMode) {
220
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(MatchColumnsToMakeSentencesClient, { questionData: currentPayload })] }));
221
+ }
222
+ return (_jsxs("div", { className: "space-y-3", children: [displayErrors.length > 0 && (_jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-2 text-xs text-red-700", children: _jsx("ul", { className: "list-disc space-y-1 pl-4", children: displayErrors.map((err, i) => _jsx("li", { children: err }, i)) }) })), _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Columns3, { className: "h-3.5 w-3.5" }), title: "Gh\u00E9p c\u1ED9t th\u00E0nh c\u00E2u", preview: { onClick: () => setIsClientMode(true) } }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "match-columns-title", className: "text-sm font-semibold text-gray-800", children: "Ti\u00EAu \u0111\u1EC1" }), _jsx(Input, { id: "match-columns-title", className: "h-10 border-gray-200 bg-white", value: title, onChange: (e) => setTitle(e.target.value) })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "match-columns-instruction", className: "text-sm font-semibold text-gray-800", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Input, { id: "match-columns-instruction", className: "h-10 border-gray-200 bg-white", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: MATCH_COLUMNS_DEFAULT_INSTRUCTION })] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsx(CardTitle, { className: "text-sm font-semibold text-gray-800", children: "C\u1ED9t fragment" }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addChoiceColumn, className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm c\u1ED9t choice"] })] }) }), _jsx(CardContent, { className: "grid gap-2.5 px-3 pb-3 md:grid-cols-3", children: columns.map((column, columnIndex) => (_jsxs("div", { className: "space-y-2.5 rounded-md border border-gray-200 bg-white px-3 py-2.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(Input, { value: column.label || '', onChange: (e) => updateColumn(columnIndex, { label: e.target.value }), placeholder: "Nh\u00E3n c\u1ED9t", className: "h-10 border-gray-200 bg-white" }), _jsx("span", { className: "text-xs text-slate-500", children: column.role }), column.role === 'choice' && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeColumn(columnIndex), className: "h-7 w-7", children: _jsx(Trash2, { className: "h-3.5 w-3.5 text-red-500" }) }))] }), column.fragments.map((fragment, fragmentIndex) => (_jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { className: "h-10 w-16 border-gray-200 bg-white", value: fragment.id, onChange: (e) => updateFragment(columnIndex, fragmentIndex, { id: e.target.value }) }), _jsx(Input, { value: fragment.text, onChange: (e) => updateFragment(columnIndex, fragmentIndex, { text: e.target.value }), placeholder: "Text", className: "h-10 border-gray-200 bg-white" }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeFragment(columnIndex, fragmentIndex), className: "h-7 w-7", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }, `${column.id}-${fragmentIndex}`))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => addFragment(columnIndex), className: "h-8 gap-1.5 px-2.5 text-xs", children: "Th\u00EAm fragment" })] }, column.id))) })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs(CardTitle, { className: "text-sm font-semibold text-gray-800", children: ["C\u00E2u h\u1ECFi (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addItem, className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm c\u00E2u"] })] }) }), _jsxs(CardContent, { className: "space-y-2.5 px-3 pb-3", children: [items.map((item, index) => (_jsxs("div", { className: "space-y-2.5 rounded-md border border-gray-200 bg-white px-3 py-2.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", item.questionNumber || index + 1] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: `match-columns-example-${index}`, checked: item.isExample || false, onCheckedChange: (checked) => updateItem(index, { isExample: checked, points: checked ? 0 : 1 }) }), _jsxs(Label, { htmlFor: `match-columns-example-${index}`, className: "flex items-center gap-1 text-xs", children: [_jsx(Star, { className: "h-3 w-3 text-amber-500" }), "Example"] }), !item.isExample && (_jsx("div", { className: "w-24", children: _jsx(PointsInput, { value: item.points, allowZero: true, onChange: (event) => updateItem(index, {
223
+ points: parsePointsValue(event.target.value, true),
224
+ }) }) })), items.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeItem(index), className: "h-7 w-7 text-red-500 hover:bg-red-50", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] })] }), _jsxs("div", { className: "grid gap-2.5 md:grid-cols-3", children: [_jsxs("select", { className: "rounded-md border px-3 py-2 text-sm", value: item.stemFragmentId, onChange: (e) => updateItem(index, { stemFragmentId: e.target.value }), children: [_jsx("option", { value: "", children: "Ch\u1ECDn stem" }), (stemColumn?.fragments || []).map((fragment) => (_jsxs("option", { value: fragment.id, children: [fragment.id, ". ", fragment.text || '(trống)'] }, fragment.id)))] }), choiceColumns.map((column, choiceIndex) => (_jsxs("select", { className: "rounded-md border px-3 py-2 text-sm", value: item.fragmentIds[choiceIndex + 1] || '', onChange: (e) => setItemChoice(index, choiceIndex, e.target.value), children: [_jsxs("option", { value: "", children: ["Ch\u1ECDn ", column.label || column.id] }), column.fragments.map((fragment) => (_jsxs("option", { value: fragment.id, children: [fragment.id, ". ", fragment.text || '(trống)'] }, fragment.id)))] }, `${index}-${column.id}`)))] }), _jsx("p", { className: "text-sm text-slate-600", children: item.sentence || 'Câu sẽ tự ghép từ fragment đã chọn.' })] }, index))), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] })] }));
222
225
  }
@@ -20,12 +20,12 @@ export function MatchWordToPictureClient({ questionData, onSubmit, isReviewMode
20
20
  const isCorrect = isReviewMode && userAnswer !== undefined
21
21
  ? userAnswer.trim().toLowerCase() === questionData.correctAnswer.trim().toLowerCase()
22
22
  : undefined;
23
- return (_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-violet-400 to-indigo-500" }), _jsxs(CardContent, { className: "space-y-6 p-6", children: [_jsx("p", { className: "text-sm font-semibold uppercase tracking-wide text-violet-700", children: questionData.instruction || MATCH_WORD_TO_PICTURE_DEFAULT_INSTRUCTION }), wordBank.length > 0 && (_jsxs("div", { className: "rounded-xl border border-violet-200 bg-gradient-to-r from-violet-50 to-indigo-50 p-4", children: [_jsx("p", { className: "mb-3 text-sm font-semibold text-violet-700", children: "Word bank:" }), _jsx("div", { className: "flex flex-wrap gap-2", children: wordBank.map((word, index) => (_jsx("span", { className: "rounded-full border border-violet-200 bg-white px-3 py-1 text-sm font-semibold text-slate-700", children: word }, `${word}-${index}`))) })] })), _jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-5", children: [isExample && (_jsxs("div", { className: "mb-2 inline-flex items-center gap-1.5 rounded-full border border-amber-300 bg-amber-100 px-3 py-1", children: [_jsx(BookOpen, { className: "h-3.5 w-3.5 text-amber-600" }), _jsx("span", { className: "text-xs font-semibold uppercase tracking-wide text-amber-700", children: "Example" })] })), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx("div", { className: "relative h-24 w-24 flex-shrink-0 overflow-hidden rounded-lg border border-gray-200 bg-gray-50 shadow-sm", children: _jsx("img", { src: questionImageUrl || questionData.imageUrl, alt: questionData.correctAnswer || 'Picture', className: "h-full w-full object-contain" }) }), _jsxs("div", { className: "flex-1 space-y-2", children: [_jsx(Label, { className: "text-base font-semibold text-gray-800", children: "Ch\u1ECDn t\u1EEB ph\u00F9 h\u1EE3p" }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("select", { value: displayedAnswer, onChange: (event) => {
24
- setAnswer(event.target.value);
25
- onSubmit?.(event.target.value);
26
- }, disabled: isReviewMode || isExample, className: `h-10 max-w-xs rounded-md border-2 bg-white px-3 text-sm font-medium ${isReviewMode
27
- ? isCorrect
28
- ? 'border-green-400 bg-green-50 text-green-700'
29
- : 'border-red-400 bg-red-50 text-red-700'
30
- : 'border-violet-300 focus:border-violet-500 focus:ring-violet-200'}`, children: [_jsx("option", { value: "", children: "-- Ch\u1ECDn t\u1EEB --" }), wordBank.map((word, index) => (_jsx("option", { value: word, children: word }, `${word}-${index}`)))] }), isReviewMode && isCorrect !== undefined && (_jsx("span", { className: "flex items-center gap-1", children: isCorrect ? (_jsx(CheckCircle2, { className: "h-5 w-5 text-green-500" })) : (_jsx(XCircle, { className: "h-5 w-5 text-red-500" })) }))] })] })] }), isReviewMode && isCorrect === false && (_jsxs("div", { className: "mt-3 flex items-center gap-2 rounded-lg border border-green-200 bg-green-50 px-4 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), _jsxs("span", { className: "text-sm text-green-700", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng: ", _jsx("strong", { children: questionData.correctAnswer })] })] }))] }), explanation && isReviewMode && (_jsxs("div", { className: "rounded-xl border border-blue-200 bg-blue-50 p-4", children: [_jsx("p", { className: "mb-1 text-sm font-semibold text-blue-800", children: "Gi\u1EA3i th\u00EDch:" }), _jsx("p", { className: "text-sm text-blue-700", children: explanation })] }))] })] }));
23
+ return (_jsx(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: _jsxs(CardContent, { className: "space-y-3 p-3", children: [_jsx("p", { className: "text-sm font-semibold uppercase tracking-wide text-violet-700", children: questionData.instruction || MATCH_WORD_TO_PICTURE_DEFAULT_INSTRUCTION }), wordBank.length > 0 && (_jsxs("div", { className: "rounded-xl border border-violet-200 bg-gradient-to-r from-violet-50 to-indigo-50 p-4", children: [_jsx("p", { className: "mb-3 text-sm font-semibold text-violet-700", children: "Word bank:" }), _jsx("div", { className: "flex flex-wrap gap-2", children: wordBank.map((word, index) => (_jsx("span", { className: "rounded-full border border-violet-200 bg-white px-3 py-1 text-sm font-semibold text-slate-700", children: word }, `${word}-${index}`))) })] })), _jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-5", children: [isExample && (_jsxs("div", { className: "mb-2 inline-flex items-center gap-1.5 rounded-full border border-amber-300 bg-amber-100 px-3 py-1", children: [_jsx(BookOpen, { className: "h-3.5 w-3.5 text-amber-600" }), _jsx("span", { className: "text-xs font-semibold uppercase tracking-wide text-amber-700", children: "Example" })] })), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx("div", { className: "relative h-24 w-24 flex-shrink-0 overflow-hidden rounded-lg border border-gray-200 bg-gray-50 shadow-sm", children: _jsx("img", { src: questionImageUrl || questionData.imageUrl, alt: questionData.correctAnswer || 'Picture', className: "h-full w-full object-contain" }) }), _jsxs("div", { className: "flex-1 space-y-2", children: [_jsx(Label, { className: "text-base font-semibold text-gray-800", children: "Ch\u1ECDn t\u1EEB ph\u00F9 h\u1EE3p" }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("select", { value: displayedAnswer, onChange: (event) => {
24
+ setAnswer(event.target.value);
25
+ onSubmit?.(event.target.value);
26
+ }, disabled: isReviewMode || isExample, className: `h-10 max-w-xs rounded-md border-2 bg-white px-3 text-sm font-medium ${isReviewMode
27
+ ? isCorrect
28
+ ? 'border-green-400 bg-green-50 text-green-700'
29
+ : 'border-red-400 bg-red-50 text-red-700'
30
+ : 'border-violet-300 focus:border-violet-500 focus:ring-violet-200'}`, children: [_jsx("option", { value: "", children: "-- Ch\u1ECDn t\u1EEB --" }), wordBank.map((word, index) => (_jsx("option", { value: word, children: word }, `${word}-${index}`)))] }), isReviewMode && isCorrect !== undefined && (_jsx("span", { className: "flex items-center gap-1", children: isCorrect ? (_jsx(CheckCircle2, { className: "h-5 w-5 text-green-500" })) : (_jsx(XCircle, { className: "h-5 w-5 text-red-500" })) }))] })] })] }), isReviewMode && isCorrect === false && (_jsxs("div", { className: "mt-3 flex items-center gap-2 rounded-lg border border-green-200 bg-green-50 px-4 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), _jsxs("span", { className: "text-sm text-green-700", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng: ", _jsx("strong", { children: questionData.correctAnswer })] })] }))] }), explanation && isReviewMode && (_jsxs("div", { className: "rounded-xl border border-blue-200 bg-blue-50 p-4", children: [_jsx("p", { className: "mb-1 text-sm font-semibold text-blue-800", children: "Gi\u1EA3i th\u00EDch:" }), _jsx("p", { className: "text-sm text-blue-700", children: explanation })] }))] }) }));
31
31
  }