@tinyweb_dev/oe-exam-sdk 1.0.6 → 1.0.7

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 (225) 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/index.d.ts +0 -1
  29. package/dist/components/questions/_shared/types/index.js +0 -1
  30. package/dist/components/questions/_shared/types/look-and-choose-answer.type.d.ts +0 -13
  31. package/dist/components/questions/_shared/utils/answer-builder.js +0 -11
  32. package/dist/components/questions/components/ColorRegionCreator.js +66 -63
  33. package/dist/components/questions/components/QuestionSearchDropdown.js +0 -8
  34. package/dist/components/questions/components/QuestionTypeSelector.js +0 -2
  35. package/dist/components/questions/components/index.d.ts +1 -0
  36. package/dist/components/questions/components/index.js +1 -0
  37. package/dist/components/questions/creator/question-type-registry.js +0 -8
  38. package/dist/components/questions/groups/ArticlesGroupCard.js +2 -2
  39. package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -1
  40. package/dist/components/questions/groups/DocumentGroupCard.js +1 -1
  41. package/dist/components/questions/groups/ImageLabelGroupCard.js +1 -1
  42. package/dist/components/questions/groups/MatchAnswerGroupCard.js +2 -2
  43. package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +0 -3
  44. package/dist/components/questions/question-bank/QuestionEditorRenderer.js +1 -10
  45. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.d.ts +0 -2
  46. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.js +0 -41
  47. package/dist/components/questions/question-bank/editor-adapters/index.d.ts +2 -2
  48. package/dist/components/questions/question-bank/editor-adapters/index.js +2 -2
  49. package/dist/components/questions/question-bank/editor-adapters/media-adapters.d.ts +0 -1
  50. package/dist/components/questions/question-bank/editor-adapters/media-adapters.js +0 -28
  51. package/dist/components/questions/question-bank/question-bank-picker.utils.js +0 -7
  52. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +16 -16
  53. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +36 -30
  54. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupClient.js +1 -1
  55. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupCreator.js +7 -6
  56. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +2 -2
  57. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +11 -7
  58. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -1
  59. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +11 -5
  60. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +5 -5
  61. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +34 -28
  62. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseAnswerGroupImageUpload.js +2 -2
  63. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +1 -1
  64. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupCreator.js +44 -28
  65. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupClient.js +7 -7
  66. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupCreator.js +8 -5
  67. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +2 -2
  68. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +20 -16
  69. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.js +1 -1
  70. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleCreator.js +66 -29
  71. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +5 -5
  72. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +45 -37
  73. package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupCreator.js +24 -20
  74. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +2 -2
  75. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +68 -65
  76. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixClient.js +1 -1
  77. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixCreator.js +45 -5
  78. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +2 -2
  79. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +18 -14
  80. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +2 -2
  81. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +30 -30
  82. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +2 -2
  83. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +24 -31
  84. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +2 -2
  85. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +29 -32
  86. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -1
  87. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +27 -29
  88. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -1
  89. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +11 -15
  90. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -1
  91. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +22 -24
  92. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -1
  93. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +17 -24
  94. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -1
  95. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +31 -31
  96. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -1
  97. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +11 -15
  98. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -1
  99. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +14 -15
  100. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +2 -2
  101. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +27 -25
  102. package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesClient.js +12 -12
  103. package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesCreator.js +8 -5
  104. package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureClient.js +8 -8
  105. package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureCreator.js +25 -20
  106. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupClient.js +1 -1
  107. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupCreator.js +13 -10
  108. package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupCreator.js +8 -5
  109. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +7 -11
  110. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -1
  111. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +15 -12
  112. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesClient.js +1 -1
  113. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesCreator.js +5 -5
  114. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +35 -39
  115. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +2 -2
  116. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardClient.js +1 -1
  117. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +7 -8
  118. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -1
  119. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +30 -23
  120. package/dist/components/questions/types/spontaneous-qa/SpontaneousQAClient.js +1 -1
  121. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +26 -19
  122. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.js +1 -1
  123. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.js +13 -11
  124. package/dist/components/questions/types/true-false/TrueFalseClient.js +2 -2
  125. package/dist/components/questions/types/true-false/TrueFalseCreator.js +35 -29
  126. package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupClient.js +1 -1
  127. package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupCreator.js +11 -7
  128. package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +1 -1
  129. package/dist/components/questions/types/true-false-group/TrueFalseGroupCreator.js +11 -7
  130. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +2 -2
  131. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +21 -56
  132. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -1
  133. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +30 -24
  134. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.js +3 -3
  135. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupCreator.js +12 -14
  136. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -1
  137. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +27 -18
  138. package/dist/components/questions/types/word-ordering-group/WordOrderingGroupClient.js +7 -7
  139. package/dist/components/questions/types/word-ordering-group/WordOrderingGroupCreator.js +16 -13
  140. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +2 -2
  141. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +34 -28
  142. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +2 -2
  143. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +22 -22
  144. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +21 -18
  145. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -1
  146. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +7 -4
  147. package/dist/components/questions/viewer/QuestionViewer.js +0 -98
  148. package/dist/components/results/renderers/review-question-body-movers.js +2 -26
  149. package/dist/components/results/review-data.js +0 -4
  150. package/dist/components/ui/points-input.d.ts +1 -0
  151. package/dist/components/ui/points-input.js +12 -2
  152. package/dist/shared/constants/question-skills.js +0 -11
  153. package/dist/shared/lib/i18n/messages/en/admin.d.ts +0 -3
  154. package/dist/shared/lib/i18n/messages/en/admin.js +0 -3
  155. package/dist/shared/lib/i18n/messages/en.d.ts +0 -3
  156. package/dist/shared/lib/i18n/messages/vi/admin.d.ts +0 -3
  157. package/dist/shared/lib/i18n/messages/vi/admin.js +0 -3
  158. package/dist/shared/lib/i18n/messages/vi.d.ts +0 -3
  159. package/dist/shared/lib/utils/question-reverse-transform.js +0 -19
  160. package/dist/shared/lib/utils/question-transform-types.d.ts +1 -23
  161. package/dist/shared/lib/utils/question-transform-validation.js +0 -40
  162. package/dist/shared/lib/utils/question-transform.js +0 -9
  163. package/dist/shared/types/common.types.d.ts +1 -1
  164. package/dist/shared/types/entities/question.types.d.ts +1 -11
  165. package/dist/shared/types/questions/index.d.ts +0 -3
  166. package/dist/shared/types/questions/index.js +0 -6
  167. package/package.json +1 -1
  168. package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.d.ts +0 -2
  169. package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.js +0 -131
  170. package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.d.ts +0 -2
  171. package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.js +0 -76
  172. package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.d.ts +0 -2
  173. package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.js +0 -68
  174. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.d.ts +0 -16
  175. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.js +0 -212
  176. package/dist/components/questions/_shared/types/gn-speaking-interview.type.d.ts +0 -32
  177. package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +0 -1
  178. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.d.ts +0 -47
  179. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +0 -1
  180. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.d.ts +0 -2
  181. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +0 -262
  182. package/dist/components/questions/types/gn-speaking-interview/register.d.ts +0 -2
  183. package/dist/components/questions/types/gn-speaking-interview/register.js +0 -121
  184. package/dist/components/questions/types/gn-speaking-interview/transform.d.ts +0 -2
  185. package/dist/components/questions/types/gn-speaking-interview/transform.js +0 -48
  186. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.d.ts +0 -20
  187. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +0 -72
  188. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.d.ts +0 -15
  189. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +0 -230
  190. package/dist/components/questions/types/look-picture-choose-correct-answer/index.d.ts +0 -1
  191. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +0 -1
  192. package/dist/components/questions/types/look-picture-choose-correct-answer/register.d.ts +0 -2
  193. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +0 -66
  194. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.d.ts +0 -2
  195. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +0 -42
  196. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.d.ts +0 -6
  197. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +0 -96
  198. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.d.ts +0 -15
  199. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +0 -238
  200. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.d.ts +0 -2
  201. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +0 -2
  202. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.d.ts +0 -2
  203. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +0 -76
  204. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.d.ts +0 -2
  205. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +0 -19
  206. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.d.ts +0 -7
  207. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +0 -134
  208. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.d.ts +0 -3
  209. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +0 -617
  210. package/dist/components/questions/types/look-picture-fill-word-hint/index.d.ts +0 -2
  211. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +0 -2
  212. package/dist/components/questions/types/look-picture-fill-word-hint/register.d.ts +0 -2
  213. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +0 -82
  214. package/dist/components/questions/types/look-picture-fill-word-hint/transform.d.ts +0 -2
  215. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +0 -29
  216. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.d.ts +0 -9
  217. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +0 -12
  218. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.d.ts +0 -17
  219. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +0 -47
  220. package/dist/shared/types/questions/look-picture-choose-correct-answer.d.ts +0 -38
  221. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +0 -5
  222. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.d.ts +0 -63
  223. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +0 -9
  224. package/dist/shared/types/questions/look-picture-fill-word-hint.d.ts +0 -57
  225. package/dist/shared/types/questions/look-picture-fill-word-hint.js +0 -7
@@ -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
  }
@@ -2,14 +2,10 @@
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 { useEffect, useRef, useState } from 'react';
5
- import { Button } from '../../../../components/ui/button';
6
5
  import { Label } from '../../../../components/ui/label';
7
- import { Textarea } from '../../../../components/ui/textarea';
8
- import { PointsInput } from '../../../../components/ui/points-input';
9
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
10
- import { BookOpen, Eye, HelpCircle, ImageIcon, Pencil } from 'lucide-react';
6
+ import { HelpCircle, ImageIcon } from 'lucide-react';
11
7
  import { FileUpload } from '../../../../components/ui/file-upload';
12
- import { Checkbox } from '../../../../components/ui/checkbox';
8
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
13
9
  import { MATCH_WORD_TO_PICTURE_DEFAULT_INSTRUCTION, } from '../../_shared/types/match-word-to-picture.type';
14
10
  import { MatchWordToPictureClient } from './MatchWordToPictureClient';
15
11
  import { WordBankEditor } from './WordBankEditor';
@@ -238,7 +234,7 @@ function MatchWordToPictureContent({ initialData, onChange, onUnsavedChangesChan
238
234
  debouncedOnChange,
239
235
  ]);
240
236
  if (isClientMode) {
241
- 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 word to picture (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(MatchWordToPictureClient, { questionData: {
237
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(MatchWordToPictureClient, { questionData: {
242
238
  imageUrl: displayPreviewUrl || imageUrl,
243
239
  correctAnswer,
244
240
  points,
@@ -248,19 +244,28 @@ function MatchWordToPictureContent({ initialData, onChange, onUnsavedChangesChan
248
244
  instruction,
249
245
  }, isReviewMode: true, autoFillCorrectAnswers: true, explanation: explanation })] }));
250
246
  }
251
- return (_jsxs("div", { className: "space-y-4", children: [groups && groups.length > 0 && (_jsx(WordBankEditor, { wordBank: wordBank, instruction: instruction, onWordBankChange: setWordBank, onInstructionChange: setInstruction })), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-violet-100/50", children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { className: "text-base font-bold text-gray-900", children: "N\u1ED1i t\u1EEB v\u1EDBi tranh" }), _jsx("p", { className: "text-xs text-gray-500", children: "Upload tranh v\u00E0 ch\u1ECDn t\u1EEB trong word bank" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: !imageUrl, className: "gap-2 border-violet-200 text-violet-600 hover:bg-violet-50", 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: "mwtp-isExample", checked: isExample || false, onCheckedChange: (checked) => setIsExample(!!checked) }), _jsx(Label, { htmlFor: "mwtp-isExample", className: "cursor-pointer text-sm font-medium text-gray-700", children: "\u0110\u00E1nh d\u1EA5u l\u00E0 c\u00E2u h\u1ECFi Example" })] })] }), (!groups || groups.length === 0) && (_jsx(WordBankEditor, { wordBank: wordBank, instruction: instruction, onWordBankChange: setWordBank, onInstructionChange: setInstruction })), _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-violet-500" }), "H\u00ECnh \u1EA3nh ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(FileUpload, { id: "mwtp-question-image", label: "", accept: "image/*", value: imageUrl || '', onChange: (url) => {
252
- setImageUrl(url);
253
- if (validationErrors.imageUrl) {
254
- setValidationErrors((prev) => ({ ...prev, imageUrl: undefined }));
255
- }
256
- }, 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" })), validationErrors.imageUrl && _jsx("p", { className: "text-sm text-red-600", children: validationErrors.imageUrl })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "mwtp-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: "mwtp-correctAnswer", value: correctAnswer, onChange: (event) => {
257
- setCorrectAnswer(event.target.value);
258
- if (validationErrors.correctAnswer) {
259
- setValidationErrors((prev) => ({ ...prev, correctAnswer: undefined }));
260
- }
261
- }, className: `flex h-10 w-full rounded-md border bg-white px-3 py-2 text-sm ${validationErrors.correctAnswer
262
- ? 'border-red-400 focus:border-red-500'
263
- : 'border-gray-200 focus:border-green-300'}`, children: [_jsx("option", { value: "", children: "-- Ch\u1ECDn t\u1EEB trong word bank --" }), usableWords.map((word, index) => (_jsx("option", { value: word, children: word }, `${word}-${index}`)))] }), validationErrors.correctAnswer && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.correctAnswer })), validationErrors.wordBank && _jsx("p", { className: "text-sm text-red-600", children: validationErrors.wordBank })] }), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-700", children: [_jsx("p", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("ul", { className: "mt-1 list-inside list-disc", 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: "mt-0.5 text-xs text-gray-500", 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: "mwtp-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, className: "min-h-[80px] resize-none border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200" }) })] })] }));
247
+ return (_jsxs("div", { className: "space-y-3", children: [groups && groups.length > 0 && (_jsx(WordBankEditor, { wordBank: wordBank, instruction: instruction, onWordBankChange: setWordBank, onInstructionChange: setInstruction })), _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(ImageIcon, { className: "h-3.5 w-3.5" }), title: "N\u1ED1i t\u1EEB v\u1EDBi tranh", points: {
248
+ value: points || 1,
249
+ onValueChange: setPoints,
250
+ }, example: {
251
+ checked: isExample || false,
252
+ onChange: setIsExample,
253
+ }, preview: {
254
+ onClick: () => setIsClientMode(true),
255
+ disabled: !imageUrl,
256
+ } }), children: [(!groups || groups.length === 0) && (_jsx(WordBankEditor, { wordBank: wordBank, instruction: instruction, onWordBankChange: setWordBank, onInstructionChange: setInstruction })), _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-violet-500" }), "H\u00ECnh \u1EA3nh ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(FileUpload, { id: "mwtp-question-image", label: "", accept: "image/*", value: imageUrl || '', onChange: (url) => {
257
+ setImageUrl(url);
258
+ if (validationErrors.imageUrl) {
259
+ setValidationErrors((prev) => ({ ...prev, imageUrl: undefined }));
260
+ }
261
+ }, 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" })), validationErrors.imageUrl && _jsx("p", { className: "text-sm text-red-600", children: validationErrors.imageUrl })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "mwtp-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: "mwtp-correctAnswer", value: correctAnswer, onChange: (event) => {
262
+ setCorrectAnswer(event.target.value);
263
+ if (validationErrors.correctAnswer) {
264
+ setValidationErrors((prev) => ({ ...prev, correctAnswer: undefined }));
265
+ }
266
+ }, className: `flex h-10 w-full rounded-md border bg-white px-3 py-2 text-sm ${validationErrors.correctAnswer
267
+ ? 'border-red-400 focus:border-red-500'
268
+ : 'border-gray-200 focus:border-green-300'}`, children: [_jsx("option", { value: "", children: "-- Ch\u1ECDn t\u1EEB trong word bank --" }), usableWords.map((word, index) => (_jsx("option", { value: word, children: word }, `${word}-${index}`)))] }), validationErrors.correctAnswer && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.correctAnswer })), validationErrors.wordBank && _jsx("p", { className: "text-sm text-red-600", children: validationErrors.wordBank })] }), 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 })] }));
264
269
  }
265
270
  export function MatchWordToPictureCreator(props) {
266
271
  return _jsx(MatchWordToPictureContent, { ...props });
@@ -81,7 +81,7 @@ export function MatchWordToPictureGroupClient({ questionData, isReviewMode = fal
81
81
  const canSelect = !isReviewMode && !item.isExample && Boolean(onAnswerChange);
82
82
  const isCorrect = normalizeWord(selectedWord) === normalizeWord(item.correctAnswer) &&
83
83
  Boolean(item.correctAnswer);
84
- 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 bg-gradient-to-r from-gray-50 to-white px-4 py-2.5", 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-violet-500 to-indigo-600 text-white", children: _jsx(CircleHelp, { className: "h-3.5 w-3.5" }) }), _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: "flex flex-col gap-4 p-4 sm:flex-row sm:items-center", children: [toMatchWordToPictureImageUrls(item.imageUrl).length > 0 ? (_jsx("div", { className: "flex flex-wrap gap-2", children: toMatchWordToPictureImageUrls(item.imageUrl).map((url, imageIndex) => (_jsx(ClientImageItem, { url: url, alt: `Hình ảnh câu ${item.questionNumber || itemIndex + 1}${imageIndex > 0 ? ` (${imageIndex + 1})` : ''}` }, `${item.questionNumber}-${imageIndex}`))) })) : null, _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("p", { className: "text-sm font-semibold text-gray-800", children: [item.questionNumber || itemIndex + 1, ". Ch\u1ECDn t\u1EEB ph\u00F9 h\u1EE3p"] }), _jsxs("div", { className: "flex items-center gap-3", children: [item.isExample ? (_jsx("span", { className: "text-sm italic text-gray-800 underline decoration-gray-400", children: item.correctAnswer })) : (_jsxs("select", { value: selectedWord, disabled: !canSelect, onChange: (event) => handleSelect(itemIndex, event.target.value), className: cn('h-10 max-w-xs rounded-md border-2 bg-white px-3 text-sm font-medium', isReviewMode && isCorrect
84
+ 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 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-lg bg-gradient-to-br from-violet-500 to-indigo-600 text-white", children: _jsx(CircleHelp, { className: "h-3.5 w-3.5" }) }), _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: "flex flex-col space-y-3 p-3 sm:flex-row sm:items-center sm:space-y-0 sm:gap-3", children: [toMatchWordToPictureImageUrls(item.imageUrl).length > 0 ? (_jsx("div", { className: "flex flex-wrap gap-2", children: toMatchWordToPictureImageUrls(item.imageUrl).map((url, imageIndex) => (_jsx(ClientImageItem, { url: url, alt: `Hình ảnh câu ${item.questionNumber || itemIndex + 1}${imageIndex > 0 ? ` (${imageIndex + 1})` : ''}` }, `${item.questionNumber}-${imageIndex}`))) })) : null, _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("p", { className: "text-sm font-semibold text-gray-800", children: [item.questionNumber || itemIndex + 1, ". Ch\u1ECDn t\u1EEB ph\u00F9 h\u1EE3p"] }), _jsxs("div", { className: "flex items-center gap-3", children: [item.isExample ? (_jsx("span", { className: "text-sm italic text-gray-800 underline decoration-gray-400", children: item.correctAnswer })) : (_jsxs("select", { value: selectedWord, disabled: !canSelect, onChange: (event) => handleSelect(itemIndex, event.target.value), className: cn('h-10 max-w-xs rounded-md border-2 bg-white px-3 text-sm font-medium', isReviewMode && isCorrect
85
85
  ? 'border-green-400 bg-green-50 text-green-700'
86
86
  : isReviewMode && selectedWord && !isCorrect
87
87
  ? 'border-red-400 bg-red-50 text-red-700'
@@ -5,7 +5,6 @@ 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';
@@ -18,7 +17,8 @@ function parsePointsValue(raw, allowZero = false) {
18
17
  const min = allowZero ? 0 : 1;
19
18
  return Math.max(min, parsed);
20
19
  }
21
- import { Eye, Pencil, Plus, Star, Trash2 } from 'lucide-react';
20
+ import { ImageIcon, Plus, Star, Trash2 } from 'lucide-react';
21
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
22
22
  import { RichTextEditor } from '../../../../components/shared/RichTextEditor';
23
23
  import { MatchWordToPictureGroupClient } from './MatchWordToPictureGroupClient';
24
24
  import { MatchWordToPictureGroupImageField } from './MatchWordToPictureGroupImageField';
@@ -99,12 +99,15 @@ export function MatchWordToPictureGroupCreator({ initialData, onChange, external
99
99
  };
100
100
  const currentPayload = buildPayload();
101
101
  const displayErrors = externalErrors && externalErrors.length > 0 ? externalErrors : errors;
102
- 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 N\u1ED1i t\u1EEB v\u1EDBi tranh (nh\u00F3m)" }), _jsx("p", { className: "text-sm text-gray-500", children: "Word bank d\u00F9ng chung. C\u00F3 th\u1EC3 1 \u1EA3nh chung + \u00F4 s\u1ED1, ho\u1EB7c m\u1ED7i d\u00F2ng m\u1ED9t tranh." })] }), _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(MatchWordToPictureGroupClient, { questionData: currentPayload }) })] })) : (_jsxs("div", { className: "space-y-6", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base font-semibold", children: "Word bank" }) }), _jsxs(CardContent, { className: "space-y-3", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: "mwtp-group-title", children: "Ti\u00EAu \u0111\u1EC1 (optional)" }), _jsx(Input, { id: "mwtp-group-title", className: "mt-1", value: title, onChange: (e) => setTitle(e.target.value), placeholder: "VOCABULARY" })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "mwtp-group-instruction", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Input, { id: "mwtp-group-instruction", className: "mt-1", value: instruction, onChange: (e) => setInstruction(e.target.value) })] }), _jsxs("div", { children: [_jsx(Label, { children: "N\u1ED9i dung b\u00E0i \u0111\u1ECDc (Passage)" }), _jsx("div", { className: "mt-1", children: _jsx(RichTextEditor, { value: passage, onChange: setPassage, minHeightClassName: "min-h-[160px]" }) })] }), _jsx(MatchWordToPictureGroupImageField, { id: "mwtp-group-shared-image", label: "\u1EA2nh chung (optional \u2014 1 b\u1EE9c tranh nhi\u1EC1u \u00F4 s\u1ED1)", value: imageUrl, onChange: setImageUrl }), wordBank.map((word, index) => (_jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { value: word, onChange: (e) => setWordBank((prev) => prev.map((item, i) => (i === index ? e.target.value : item))), placeholder: `Từ ${index + 1}` }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setWordBank((prev) => prev.filter((_, i) => i !== index)), children: _jsx(Trash2, { className: "h-4 w-4" }) })] }, index))), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setWordBank((prev) => [...prev, '']), children: [_jsx(Plus, { className: "mr-1 h-4 w-4" }), "Th\u00EAm t\u1EEB"] })] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "text-base font-semibold", children: ["Tranh (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setItems((prev) => [...prev, createEmptyMatchWordToPictureGroupItem(prev.length + 1)]), children: [_jsx(Plus, { className: "mr-1 h-4 w-4" }), "Th\u00EAm tranh"] })] }), _jsx(CardContent, { className: "space-y-4", children: items.map((item, index) => (_jsxs("div", { className: "space-y-3 rounded-lg border border-gray-200 p-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("span", { className: "text-sm font-medium", children: ["C\u00E2u ", item.questionNumber || index + 1] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Switch, { checked: item.isExample || false, onCheckedChange: (checked) => updateItem(index, { isExample: checked, points: checked ? 0 : 1 }) }), _jsxs(Label, { 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, {
103
- points: parsePointsValue(event.target.value, true),
104
- }) }) })), items.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setItems((prev) => prev.filter((_, i) => i !== index).map((entry, i) => ({ ...entry, questionNumber: i + 1 }))), children: _jsx(Trash2, { className: "h-4 w-4 text-red-500" }) }))] })] }), _jsx(MatchWordToPictureGroupImageField, { id: `mwtp-group-image-${index}`, label: "\u1EA2nh", value: toMatchWordToPictureImageUrls(item.imageUrl)[0] || '', onChange: (url) => updateItem(index, {
105
- imageUrl: fromMatchWordToPictureImageUrls(url),
106
- }) }), _jsxs("select", { className: "rounded-md border px-3 py-2 text-sm", value: item.correctAnswer, onChange: (e) => updateItem(index, { correctAnswer: e.target.value }), children: [_jsx("option", { value: "", children: "Ch\u1ECDn t\u1EEB \u0111\u00FAng" }), Array.from(new Set([
107
- ...wordBank.map((word) => word.trim()).filter(Boolean),
108
- item.correctAnswer.trim(),
109
- ].filter(Boolean))).map((word) => (_jsx("option", { value: word, children: word }, word)))] })] }, index))) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Gi\u1EA3i th\u00EDch" }) }), _jsx(CardContent, { children: _jsx(Textarea, { value: explanation, onChange: (event) => setExplanation(event.target.value), rows: 3 }) })] })] }))] }));
102
+ if (isClientMode) {
103
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(MatchWordToPictureGroupClient, { questionData: currentPayload })] }));
104
+ }
105
+ 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(ImageIcon, { className: "h-3.5 w-3.5" }), title: "N\u1ED1i t\u1EEB v\u1EDBi tranh (Nh\u00F3m)", preview: { onClick: () => setIsClientMode(true) } }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "mwtp-group-title", className: "text-sm font-semibold text-gray-800", children: "Ti\u00EAu \u0111\u1EC1 (optional)" }), _jsx(Input, { id: "mwtp-group-title", className: "h-10 border-gray-200 bg-white", value: title, onChange: (e) => setTitle(e.target.value), placeholder: "VOCABULARY" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "mwtp-group-instruction", className: "text-sm font-semibold text-gray-800", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Input, { id: "mwtp-group-instruction", className: "h-10 border-gray-200 bg-white", value: instruction, onChange: (e) => setInstruction(e.target.value) })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "N\u1ED9i dung b\u00E0i \u0111\u1ECDc (Passage)" }), _jsx(RichTextEditor, { value: passage, onChange: setPassage, minHeightClassName: "min-h-[160px]" })] }), _jsx(MatchWordToPictureGroupImageField, { id: "mwtp-group-shared-image", label: "\u1EA2nh chung (optional \u2014 1 b\u1EE9c tranh nhi\u1EC1u \u00F4 s\u1ED1)", value: imageUrl, onChange: setImageUrl }), wordBank.map((word, index) => (_jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { value: word, onChange: (e) => setWordBank((prev) => prev.map((item, i) => (i === index ? e.target.value : item))), placeholder: `Từ ${index + 1}`, className: "h-10 border-gray-200 bg-white" }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => setWordBank((prev) => prev.filter((_, i) => i !== index)), className: "h-7 w-7", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }, index))), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setWordBank((prev) => [...prev, '']), className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm t\u1EEB"] })] }), _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: ["Tranh (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setItems((prev) => [...prev, createEmptyMatchWordToPictureGroupItem(prev.length + 1)]), className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm tranh"] })] }) }), _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", children: ["C\u00E2u ", item.questionNumber || index + 1] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: item.isExample || false, onCheckedChange: (checked) => updateItem(index, { isExample: checked, points: checked ? 0 : 1 }) }), _jsxs(Label, { 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, {
106
+ points: parsePointsValue(event.target.value, true),
107
+ }) }) })), items.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => setItems((prev) => prev.filter((_, i) => i !== index).map((entry, i) => ({ ...entry, questionNumber: i + 1 }))), className: "h-7 w-7 text-red-500 hover:bg-red-50", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] })] }), _jsx(MatchWordToPictureGroupImageField, { id: `mwtp-group-image-${index}`, label: "\u1EA2nh", value: toMatchWordToPictureImageUrls(item.imageUrl)[0] || '', onChange: (url) => updateItem(index, {
108
+ imageUrl: fromMatchWordToPictureImageUrls(url),
109
+ }) }), _jsxs("select", { className: "rounded-md border px-3 py-2 text-sm", value: item.correctAnswer, onChange: (e) => updateItem(index, { correctAnswer: e.target.value }), children: [_jsx("option", { value: "", children: "Ch\u1ECDn t\u1EEB \u0111\u00FAng" }), Array.from(new Set([
110
+ ...wordBank.map((word) => word.trim()).filter(Boolean),
111
+ item.correctAnswer.trim(),
112
+ ].filter(Boolean))).map((word) => (_jsx("option", { value: word, children: word }, word)))] })] }, index))), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] })] }));
110
113
  }
@@ -4,12 +4,12 @@ import { useEffect, useRef, useState } from 'react';
4
4
  import { Button } from '../../../../components/ui/button';
5
5
  import { Label } from '../../../../components/ui/label';
6
6
  import { Input } from '../../../../components/ui/input';
7
- import { Textarea } from '../../../../components/ui/textarea';
8
7
  import { PointsInput } from '../../../../components/ui/points-input';
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 { Link2, Plus, Star, Trash2 } from 'lucide-react';
12
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
13
13
  import { RichTextEditor } from '../../../../components/shared/RichTextEditor';
14
14
  import { MatchingWithLinesGroupClient } from './MatchingWithLinesGroupClient';
15
15
  import { MatchingWithLinesImageField } from './MatchingWithLinesImageField';
@@ -186,7 +186,10 @@ export function MatchingWithLinesGroupCreator({ initialData, onChange, externalE
186
186
  };
187
187
  const currentPayload = buildPayload();
188
188
  const displayErrors = externalErrors && externalErrors.length > 0 ? externalErrors : errors;
189
- 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 N\u1ED1i b\u1EB1ng \u0111\u01B0\u1EDDng k\u1EBB (Nh\u00F3m)" }), _jsx("p", { className: "text-sm text-gray-500", children: "N\u1ED1i \u1EA3nh c\u1ED9t tr\u00E1i v\u1EDBi t\u1EEB c\u1ED9t ph\u1EA3i. M\u1ED7i t\u1EEB d\u00F9ng \u0111\u00FAng 1 l\u1EA7n." })] }), _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 && (_jsxs("div", { className: "rounded-lg border border-red-200 bg-red-50 p-4", children: [_jsx("h3", { className: "text-sm font-medium text-red-800", children: "C\u1EA7n s\u1EEDa c\u00E1c l\u1ED7i sau:" }), _jsx("ul", { className: "mt-2 list-disc space-y-1 pl-5 text-sm text-red-700", 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(MatchingWithLinesGroupClient, { questionData: currentPayload, userAnswers: currentPayload.items.map((item) => item.correctAnswer) }) })] })) : (_jsxs("div", { className: "space-y-6", children: [_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base font-semibold", children: "H\u01B0\u1EDBng d\u1EABn chung" }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: "mwl-title", children: "Ti\u00EAu \u0111\u1EC1 (optional)" }), _jsx(Input, { id: "mwl-title", value: title, onChange: (e) => setTitle(e.target.value), placeholder: "VOCABULARY", className: "mt-1" })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "mwl-instruction", children: "H\u01B0\u1EDBng d\u1EABn b\u00E0i l\u00E0m" }), _jsx(Input, { id: "mwl-instruction", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: MATCHING_WITH_LINES_GROUP_DEFAULT_INSTRUCTION, className: "mt-1" })] }), _jsxs("div", { children: [_jsx(Label, { children: "N\u1ED9i dung b\u00E0i \u0111\u1ECDc (Passage)" }), _jsx("div", { className: "mt-1", children: _jsx(RichTextEditor, { value: passage, onChange: setPassage, minHeightClassName: "min-h-[160px]" }) })] }), _jsx(MatchingWithLinesImageField, { id: "mwl-group-image", label: "\u1EA2nh chung (optional)", value: imageUrl, onChange: setImageUrl })] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "text-base font-semibold", children: ["C\u1EB7p n\u1ED1i (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addPair, className: "gap-2", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm c\u1EB7p"] })] }), _jsx(CardContent, { className: "space-y-4", children: items.map((item, index) => (_jsxs("div", { className: "space-y-4 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\u1EB7p ", index + 1] }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: `mwl-example-${index}`, checked: item.isExample || false, onCheckedChange: (checked) => updateItem(index, { isExample: checked, points: checked ? 0 : 1 }) }), _jsxs(Label, { htmlFor: `mwl-example-${index}`, className: "flex cursor-pointer 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, {
190
- points: parsePointsValue(event.target.value, true),
191
- }) }) })), items.length > 2 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removePair(index), className: "text-red-500 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] })] }), _jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-3", children: [_jsx(MatchingWithLinesImageField, { id: `mwl-image-${index}`, label: "\u1EA2nh c\u1ED9t tr\u00E1i", value: item.imageUrl || '', onChange: (url) => updateItem(index, { imageUrl: url }) }), _jsxs("div", { children: [_jsx(Label, { htmlFor: `mwl-left-text-${index}`, children: "Ch\u1EEF c\u1ED9t tr\u00E1i (text-to-text)" }), _jsx(Input, { id: `mwl-left-text-${index}`, value: item.text || '', onChange: (e) => updateItem(index, { text: e.target.value }), placeholder: "ball", className: "mt-1 bg-white" })] })] }), _jsxs("div", { className: "space-y-3", children: [_jsx(MatchingWithLinesImageField, { id: `mwl-right-image-${index}`, label: "\u1EA2nh c\u1ED9t ph\u1EA3i (image-to-image)", value: rightItems[index]?.imageUrl || '', onChange: (url) => updateRightItem(index, { imageUrl: url || undefined }) }), _jsxs("div", { children: [_jsx(Label, { htmlFor: `mwl-right-text-${index}`, children: "T\u1EEB c\u1ED9t ph\u1EA3i" }), _jsx(Input, { id: `mwl-right-text-${index}`, value: rightItems[index]?.text || '', onChange: (e) => updateRightItem(index, { text: e.target.value }), placeholder: "paintbrush", className: "mt-1 bg-white" })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: `mwl-answer-${index}`, children: "\u0110\u00E1p \u00E1n \u0111\u00FAng (id t\u1EEB)" }), _jsxs("select", { id: `mwl-answer-${index}`, value: item.correctAnswer, onChange: (e) => updateItem(index, { correctAnswer: e.target.value }), className: "mt-1 w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm", children: [_jsx("option", { value: "", children: "Ch\u1ECDn t\u1EEB \u0111\u00FAng" }), rightItems.map((right) => (_jsxs("option", { value: right.id, children: [right.text || right.id, " (", right.id, ")"] }, right.id)))] })] })] })] })] }, `${rightItems[index]?.id || index}`))) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Gi\u1EA3i th\u00EDch" }) }), _jsx(CardContent, { children: _jsx(Textarea, { id: "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 }) })] })] }))] }));
189
+ if (isClientMode) {
190
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(MatchingWithLinesGroupClient, { questionData: currentPayload, userAnswers: currentPayload.items.map((item) => item.correctAnswer) })] }));
191
+ }
192
+ 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(Link2, { className: "h-3.5 w-3.5" }), title: "N\u1ED1i b\u1EB1ng \u0111\u01B0\u1EDDng k\u1EBB (Nh\u00F3m)", preview: { onClick: () => setIsClientMode(true) } }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "mwl-title", className: "text-sm font-semibold text-gray-800", children: "Ti\u00EAu \u0111\u1EC1 (optional)" }), _jsx(Input, { id: "mwl-title", value: title, onChange: (e) => setTitle(e.target.value), placeholder: "VOCABULARY", className: "h-10 border-gray-200 bg-white" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "mwl-instruction", className: "text-sm font-semibold text-gray-800", children: "H\u01B0\u1EDBng d\u1EABn b\u00E0i l\u00E0m" }), _jsx(Input, { id: "mwl-instruction", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: MATCHING_WITH_LINES_GROUP_DEFAULT_INSTRUCTION, className: "h-10 border-gray-200 bg-white" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "N\u1ED9i dung b\u00E0i \u0111\u1ECDc (Passage)" }), _jsx(RichTextEditor, { value: passage, onChange: setPassage, minHeightClassName: "min-h-[160px]" })] }), _jsx(MatchingWithLinesImageField, { id: "mwl-group-image", label: "\u1EA2nh chung (optional)", value: imageUrl, onChange: setImageUrl })] }), _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\u1EB7p n\u1ED1i (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addPair, className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm c\u1EB7p"] })] }) }), _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\u1EB7p ", index + 1] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: `mwl-example-${index}`, checked: item.isExample || false, onCheckedChange: (checked) => updateItem(index, { isExample: checked, points: checked ? 0 : 1 }) }), _jsxs(Label, { htmlFor: `mwl-example-${index}`, className: "flex cursor-pointer 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, {
193
+ points: parsePointsValue(event.target.value, true),
194
+ }) }) })), items.length > 2 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removePair(index), className: "h-7 w-7 text-red-500 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] })] }), _jsxs("div", { className: "grid gap-2.5 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-2.5", children: [_jsx(MatchingWithLinesImageField, { id: `mwl-image-${index}`, label: "\u1EA2nh c\u1ED9t tr\u00E1i", value: item.imageUrl || '', onChange: (url) => updateItem(index, { imageUrl: url }) }), _jsxs("div", { children: [_jsx(Label, { htmlFor: `mwl-left-text-${index}`, children: "Ch\u1EEF c\u1ED9t tr\u00E1i (text-to-text)" }), _jsx(Input, { id: `mwl-left-text-${index}`, value: item.text || '', onChange: (e) => updateItem(index, { text: e.target.value }), placeholder: "ball", className: "mt-1 bg-white" })] })] }), _jsxs("div", { className: "space-y-2.5", children: [_jsx(MatchingWithLinesImageField, { id: `mwl-right-image-${index}`, label: "\u1EA2nh c\u1ED9t ph\u1EA3i (image-to-image)", value: rightItems[index]?.imageUrl || '', onChange: (url) => updateRightItem(index, { imageUrl: url || undefined }) }), _jsxs("div", { children: [_jsx(Label, { htmlFor: `mwl-right-text-${index}`, children: "T\u1EEB c\u1ED9t ph\u1EA3i" }), _jsx(Input, { id: `mwl-right-text-${index}`, value: rightItems[index]?.text || '', onChange: (e) => updateRightItem(index, { text: e.target.value }), placeholder: "paintbrush", className: "mt-1 bg-white" })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: `mwl-answer-${index}`, children: "\u0110\u00E1p \u00E1n \u0111\u00FAng (id t\u1EEB)" }), _jsxs("select", { id: `mwl-answer-${index}`, value: item.correctAnswer, onChange: (e) => updateItem(index, { correctAnswer: e.target.value }), className: "mt-1 w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm", children: [_jsx("option", { value: "", children: "Ch\u1ECDn t\u1EEB \u0111\u00FAng" }), rightItems.map((right) => (_jsxs("option", { value: right.id, children: [right.text || right.id, " (", right.id, ")"] }, right.id)))] })] })] })] })] }, `${rightItems[index]?.id || index}`))), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] })] }));
192
195
  }
@@ -3,12 +3,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useRef, useEffect } from 'react';
4
4
  import { Label } from '../../../../components/ui/label';
5
5
  import { Textarea } from '../../../../components/ui/textarea';
6
- import { PointsInput } from '../../../../components/ui/points-input';
7
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
8
6
  import { useDebouncedCallback } from '../../../../shared/lib/hooks';
9
7
  import { useReactHookForm } from '../../../../shared/lib/hooks/useReactHookForm';
10
- import { BookOpen, FileText, Info, MessageSquare } from 'lucide-react';
11
- import { CreatorGuide } from '../../components/CreatorGuide';
8
+ import { FileText, Info, MessageSquare } from 'lucide-react';
9
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, } from '../../_shared/components/compact';
12
10
  import { readDisplayedContentFormSchema, } from '../../../../shared/lib/validation/read-displayed-content.validation';
13
11
  export function ReadDisplayedContentCreator({ initialData, onSave, onChange, externalErrors, validationRef, }) {
14
12
  const apiData = initialData;
@@ -115,11 +113,9 @@ export function ReadDisplayedContentCreator({ initialData, onSave, onChange, ext
115
113
  ...data,
116
114
  });
117
115
  });
118
- return (_jsxs("div", { className: "space-y-4", children: [_jsx(CreatorGuide, { colorScheme: "blue", steps: [
119
- { text: 'Nhập đoạn văn bản mà học sinh sẽ đọc to (reference text)', highlight: true },
120
- { text: 'Thêm giải thích (tùy chọn)' },
121
- ], tips: [
122
- 'Học sinh sẽ nhìn thấy đoạn văn trên màn hình đọc to.',
123
- 'AI sẽ so sánh giọng đọc của học sinh với văn bản gốc (Phonetic Distance).',
124
- ] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white", children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsx("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-gray-100", children: _jsx(FileText, { className: "h-4 w-4 text-gray-600" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-base font-bold text-gray-900", children: "\u0110o\u1EA1n v\u0103n b\u1EA3n c\u1EA7n \u0111\u1ECDc" }), _jsx("p", { className: "text-xs text-gray-500", children: "T\u1EA1o c\u00E2u h\u1ECFi \u0111\u1ECDc \u0111o\u1EA1n v\u0103n" })] })] }) }) }), _jsxs(CardContent, { className: "space-y-3 px-4 pb-4", children: [_jsx("div", { className: "flex items-center gap-6", children: _jsx(PointsInput, { ...form.register('points', { valueAsNumber: true }), error: form.hasError('points') ? form.getFieldError('points') : undefined }) }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "referenceText", className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-4 w-4 text-indigo-500" }), _jsx("span", { className: "flex items-center gap-1", children: "\u0110o\u1EA1n v\u0103n b\u1EA3n" }), _jsx("span", { className: "text-sm font-normal text-red-500", children: "*" })] }), _jsx(Textarea, { id: "referenceText", ...form.register('referenceText'), placeholder: "VD: The fat cat sat on a red mat. It has a small bell on its neck. The bell rings when the cat jumps.", rows: 4, className: `min-h-[120px] border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 resize-none ${form.hasError('referenceText') ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), form.hasError('referenceText') && (_jsx("p", { className: "text-sm text-red-600", children: form.getFieldError('referenceText') })), _jsxs("div", { className: "mt-2 flex items-start gap-3 rounded-xl border border-blue-100 bg-gradient-to-r from-blue-50 to-indigo-50 p-4 text-sm text-blue-800", children: [_jsx(Info, { className: "mt-0.5 h-4 w-4 flex-shrink-0 text-indigo-500" }), _jsx("p", { children: "\u0110\u00E2y l\u00E0 \u0111o\u1EA1n v\u0103n s\u1EBD hi\u1EC3n th\u1ECB tr\u00EAn m\u00E0n h\u00ECnh cho h\u1ECDc sinh \u0111\u1ECDc to. AI s\u1EBD so s\u00E1nh ph\u00E1t \u00E2m c\u1EE7a h\u1ECDc sinh v\u1EDBi \u0111o\u1EA1n v\u0103n n\u00E0y." })] })] }), 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", 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 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, { value: watchedExplanation || '', onChange: (e) => form.setValue('explanation', e.target.value, { shouldValidate: true }), placeholder: "VD: \u0110o\u1EA1n v\u0103n t\u1EADp trung v\u00E0o c\u00E1c \u00E2m ng\u1EAFn /\u00E6/, /e/, /\u026A/...", rows: 3, className: "min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none" }) })] })] }));
116
+ return (_jsxs("div", { className: "space-y-3", children: [_jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(FileText, { className: "h-3.5 w-3.5" }), title: "\u0110o\u1EA1n v\u0103n b\u1EA3n c\u1EA7n \u0111\u1ECDc", points: {
117
+ value: watchedPoints,
118
+ onValueChange: (value) => form.setValue('points', value, { shouldValidate: true }),
119
+ error: form.hasError('points') ? form.getFieldError('points') : undefined,
120
+ } }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { htmlFor: "referenceText", 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-indigo-500" }), _jsx("span", { children: "\u0110o\u1EA1n v\u0103n b\u1EA3n" }), _jsx("span", { className: "text-xs font-normal text-red-500", children: "*" })] }), _jsx(Textarea, { id: "referenceText", ...form.register('referenceText'), placeholder: "VD: The fat cat sat on a red mat. It has a small bell on its neck. The bell rings when the cat jumps.", rows: 4, className: `min-h-[120px] border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 resize-none ${form.hasError('referenceText') ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), form.hasError('referenceText') && (_jsx("p", { className: "text-sm text-red-600", children: form.getFieldError('referenceText') })), _jsxs("div", { className: "flex items-start gap-2 rounded-md border border-blue-100 bg-blue-50 p-2 text-xs text-blue-800", children: [_jsx(Info, { className: "mt-0.5 h-3.5 w-3.5 flex-shrink-0 text-indigo-500" }), _jsx("p", { children: "\u0110\u00E2y l\u00E0 \u0111o\u1EA1n v\u0103n s\u1EBD hi\u1EC3n th\u1ECB tr\u00EAn m\u00E0n h\u00ECnh cho h\u1ECDc sinh \u0111\u1ECDc to. AI s\u1EBD so s\u00E1nh ph\u00E1t \u00E2m c\u1EE7a h\u1ECDc sinh v\u1EDBi \u0111o\u1EA1n v\u0103n n\u00E0y." })] })] }), 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: watchedExplanation || '', onValueChange: (value) => form.setValue('explanation', value, { shouldValidate: true }) })] }));
125
121
  }