@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
@@ -11,6 +11,7 @@ import { Textarea } from '../../../../components/ui/textarea';
11
11
  import { Switch } from '../../../../components/ui/switch';
12
12
  import { Label } from '../../../../components/ui/label';
13
13
  import { cn } from '../../../../shared/lib/utils';
14
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
14
15
  import { WordOrderAndMatchGroupClient } from './WordOrderAndMatchGroupClient';
15
16
  const itemSchema = z.object({
16
17
  scrambledWords: z.array(z.string().min(1, 'Từ không được để trống')).min(2, 'Cần ít nhất 2 từ xáo trộn'),
@@ -149,34 +150,31 @@ export function WordOrderAndMatchGroupCreator({ initialData, onSave, onChange, e
149
150
  const onSubmit = (data) => {
150
151
  onSave?.(data);
151
152
  };
152
- return (_jsxs("div", { className: "w-full space-y-6", children: [_jsxs("div", { className: "flex flex-col sm:flex-row sm:items-center justify-between gap-3 border-b pb-4", children: [_jsxs("div", { children: [_jsxs("h3", { className: "text-lg font-bold text-gray-900 flex items-center gap-2", children: [_jsx(Layers, { className: "h-5 w-5 text-indigo-600" }), "S\u1EAFp x\u1EBFp t\u1EEB & N\u1ED1i nh\u00E3n (Nh\u00F3m c\u00E2u h\u1ECFi)"] }), _jsx("p", { className: "text-xs text-gray-500 mt-0.5", children: "D\u1EA1ng b\u00E0i h\u1ECDc sinh s\u1EAFp x\u1EBFp c\u00E1c t\u1EEB x\u00E1o tr\u1ED9n th\u00E0nh c\u00E2u ho\u00E0n ch\u1EC9nh v\u00E0 n\u1ED1i nh\u00E3n ch\u1EEF c\u00E1i t\u01B0\u01A1ng \u1EE9ng." })] }), _jsxs("div", { className: "flex items-center rounded-xl bg-gray-100 p-1", children: [_jsxs("button", { type: "button", onClick: () => setActiveTab('editor'), className: cn('flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-semibold transition-all', activeTab === 'editor'
153
- ? 'bg-white text-indigo-700 shadow-xs'
154
- : 'text-gray-600 hover:text-gray-900'), children: [_jsx(Edit3, { className: "h-3.5 w-3.5" }), "So\u1EA1n th\u1EA3o"] }), _jsxs("button", { type: "button", onClick: () => setActiveTab('preview'), className: cn('flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-semibold transition-all', activeTab === 'preview'
155
- ? 'bg-white text-indigo-700 shadow-xs'
156
- : 'text-gray-600 hover:text-gray-900'), children: [_jsx(Eye, { className: "h-3.5 w-3.5" }), "Xem tr\u01B0\u1EDBc"] })] })] }), externalErrors && externalErrors.length > 0 && (_jsx("div", { className: "rounded-xl border border-rose-200 bg-rose-50 p-4 text-xs text-rose-800", children: _jsx("ul", { className: "list-disc pl-4 space-y-1", children: externalErrors.map((err, i) => (_jsx("li", { children: err }, i))) }) })), activeTab === 'preview' ? (
157
- /* Preview Mode */
158
- _jsxs("div", { className: "rounded-2xl border border-gray-200 bg-gray-50/50 p-6", children: [_jsx("div", { className: "mb-4 text-xs font-bold text-gray-500 uppercase tracking-wider", children: "Giao di\u1EC7n l\u00E0m b\u00E0i th\u1EF1c t\u1EBF c\u1EE7a h\u1ECDc sinh:" }), _jsx(WordOrderAndMatchGroupClient, { questionData: watchedValues })] })) : (
159
- /* Editor Mode */
160
- _jsxs("form", { onSubmit: handleSubmit(onSubmit), className: "space-y-6", children: [_jsxs("div", { className: "rounded-2xl border border-gray-200 bg-white p-5 shadow-xs space-y-4", children: [_jsxs("div", { children: [_jsxs(Label, { className: "text-xs font-bold text-gray-700 uppercase tracking-wider", children: ["1. H\u01B0\u1EDBng d\u1EABn l\u00E0m b\u00E0i chung (Instruction) ", _jsx("span", { className: "text-rose-500", children: "*" })] }), _jsx(Textarea, { ...register('instruction'), rows: 2, placeholder: "Nh\u1EADp h\u01B0\u1EDBng d\u1EABn l\u00E0m b\u00E0i...", className: "mt-1.5 text-sm" }), errors.instruction && (_jsx("p", { className: "mt-1 text-xs text-rose-500", children: errors.instruction.message }))] }), _jsxs("div", { children: [_jsxs(Label, { className: "text-xs font-bold text-gray-700 uppercase tracking-wider flex items-center justify-between", children: [_jsx("span", { children: "2. Danh s\u00E1ch nh\u00E3n l\u1EF1a ch\u1ECDn (Match Options)" }), _jsx("span", { className: "text-[11px] font-normal text-gray-400", children: "V\u00ED d\u1EE5: A, B, C, D, E, F" })] }), _jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-2", children: [watchedValues.matchOptions?.map((opt) => (_jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-xl border border-indigo-200 bg-indigo-50/70 px-3 py-1 text-xs font-bold text-indigo-700", children: [_jsx("span", { children: opt }), watchedValues.matchOptions && watchedValues.matchOptions.length > 1 && (_jsx("button", { type: "button", onClick: () => handleRemoveMatchOption(opt), className: "text-indigo-400 hover:text-rose-600 transition-colors", title: "Xo\u00E1 nh\u00E3n", children: _jsx(Trash2, { className: "h-3 w-3" }) }))] }, opt))), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Input, { value: newOptionInput, onChange: (e) => setNewOptionInput(e.target.value), placeholder: "Nh\u00E3n m\u1EDBi (VD: G)", className: "h-8 w-28 text-xs uppercase", onKeyDown: (e) => {
153
+ const tabSwitcher = (_jsxs("div", { className: "flex items-center rounded-md bg-gray-100 p-0.5", children: [_jsxs("button", { type: "button", onClick: () => setActiveTab('editor'), className: cn('flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs font-semibold transition-all', activeTab === 'editor'
154
+ ? 'bg-white text-indigo-700 shadow-xs'
155
+ : 'text-gray-600 hover:text-gray-900'), children: [_jsx(Edit3, { className: "h-3.5 w-3.5" }), "So\u1EA1n th\u1EA3o"] }), _jsxs("button", { type: "button", onClick: () => setActiveTab('preview'), className: cn('flex h-8 items-center gap-1.5 rounded-md px-2.5 text-xs font-semibold transition-all', activeTab === 'preview'
156
+ ? 'bg-white text-indigo-700 shadow-xs'
157
+ : 'text-gray-600 hover:text-gray-900'), children: [_jsx(Eye, { className: "h-3.5 w-3.5" }), "Xem tr\u01B0\u1EDBc"] })] }));
158
+ return (_jsxs("div", { className: "w-full space-y-3", children: [externalErrors && externalErrors.length > 0 && (_jsx("div", { className: "rounded-md border border-rose-200 bg-rose-50 p-2 text-xs text-rose-800", children: _jsx("ul", { className: "list-disc space-y-1 pl-4", children: externalErrors.map((err, i) => (_jsx("li", { children: err }, i))) }) })), activeTab === 'preview' ? (_jsxs("div", { className: "space-y-3", children: [_jsx(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Layers, { className: "h-3.5 w-3.5" }), title: "S\u1EAFp x\u1EBFp t\u1EEB & N\u1ED1i nh\u00E3n", extraActions: tabSwitcher }), contentClassName: "hidden", children: null }), _jsx(CompactPreviewBanner, { onBack: () => setActiveTab('editor') }), _jsx(WordOrderAndMatchGroupClient, { questionData: watchedValues })] })) : (_jsxs("form", { onSubmit: handleSubmit(onSubmit), className: "space-y-3", children: [_jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Layers, { className: "h-3.5 w-3.5" }), title: "S\u1EAFp x\u1EBFp t\u1EEB & N\u1ED1i nh\u00E3n", extraActions: tabSwitcher }), children: [_jsxs("div", { children: [_jsxs(Label, { className: "text-xs font-bold text-gray-700 uppercase tracking-wider", children: ["1. H\u01B0\u1EDBng d\u1EABn l\u00E0m b\u00E0i chung (Instruction) ", _jsx("span", { className: "text-rose-500", children: "*" })] }), _jsx(Textarea, { ...register('instruction'), rows: 2, placeholder: "Nh\u1EADp h\u01B0\u1EDBng d\u1EABn l\u00E0m b\u00E0i...", className: "mt-1.5 text-sm" }), errors.instruction && (_jsx("p", { className: "mt-1 text-xs text-rose-500", children: errors.instruction.message }))] }), _jsxs("div", { children: [_jsxs(Label, { className: "text-xs font-bold text-gray-700 uppercase tracking-wider flex items-center justify-between", children: [_jsx("span", { children: "2. Danh s\u00E1ch nh\u00E3n l\u1EF1a ch\u1ECDn (Match Options)" }), _jsx("span", { className: "text-[11px] font-normal text-gray-400", children: "V\u00ED d\u1EE5: A, B, C, D, E, F" })] }), _jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-2", children: [watchedValues.matchOptions?.map((opt) => (_jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-xl border border-indigo-200 bg-indigo-50/70 px-3 py-1 text-xs font-bold text-indigo-700", children: [_jsx("span", { children: opt }), watchedValues.matchOptions && watchedValues.matchOptions.length > 1 && (_jsx("button", { type: "button", onClick: () => handleRemoveMatchOption(opt), className: "text-indigo-400 hover:text-rose-600 transition-colors", title: "Xo\u00E1 nh\u00E3n", children: _jsx(Trash2, { className: "h-3 w-3" }) }))] }, opt))), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Input, { value: newOptionInput, onChange: (e) => setNewOptionInput(e.target.value), placeholder: "Nh\u00E3n m\u1EDBi (VD: G)", className: "h-8 w-28 text-xs uppercase", onKeyDown: (e) => {
161
159
  if (e.key === 'Enter') {
162
160
  e.preventDefault();
163
161
  handleAddMatchOption();
164
162
  }
165
- } }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: handleAddMatchOption, className: "h-8 text-xs font-medium", children: [_jsx(Plus, { className: "h-3.5 w-3.5 mr-1" }), " Th\u00EAm"] })] })] }), errors.matchOptions && (_jsx("p", { className: "mt-1 text-xs text-rose-500", children: errors.matchOptions.message }))] })] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("h4", { className: "text-sm font-bold text-gray-800 uppercase tracking-wider flex items-center gap-2", children: [_jsx(Tag, { className: "h-4 w-4 text-indigo-600" }), "Danh s\u00E1ch c\u00E2u h\u1ECFi con (", fields.length, ")"] }), _jsxs(Button, { type: "button", onClick: handleAddItem, size: "sm", className: "bg-indigo-600 text-white hover:bg-indigo-700 text-xs font-medium", children: [_jsx(Plus, { className: "h-3.5 w-3.5 mr-1" }), " Th\u00EAm c\u00E2u h\u1ECFi"] })] }), fields.map((field, index) => {
163
+ } }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: handleAddMatchOption, className: "h-8 text-xs font-medium", children: [_jsx(Plus, { className: "h-3.5 w-3.5 mr-1" }), " Th\u00EAm"] })] })] }), errors.matchOptions && (_jsx("p", { className: "mt-1 text-xs text-rose-500", children: errors.matchOptions.message }))] })] }), _jsxs(CompactFieldCard, { header: _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("p", { className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(Tag, { className: "h-3.5 w-3.5 text-indigo-600" }), "Danh s\u00E1ch c\u00E2u h\u1ECFi con (", fields.length, ")"] }), _jsxs(Button, { type: "button", onClick: handleAddItem, size: "sm", className: "h-8 gap-1.5 bg-indigo-600 px-2.5 text-xs font-medium text-white hover:bg-indigo-700", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), " Th\u00EAm c\u00E2u h\u1ECFi"] })] }), children: [fields.map((field, index) => {
166
164
  const itemWords = watchedValues.items?.[index]?.scrambledWords || [];
167
165
  const isExample = watchedValues.items?.[index]?.isExample;
168
- return (_jsxs("div", { className: cn('rounded-2xl border p-5 bg-white shadow-xs transition-all space-y-4', isExample ? 'border-amber-200 bg-amber-50/20' : 'border-gray-200'), children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 border-b border-gray-100 pb-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("span", { className: "inline-flex h-7 items-center justify-center rounded-lg bg-indigo-100 px-2.5 text-xs font-bold text-indigo-800", children: ["#", index + 1] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: `example-${index}`, checked: isExample || false, onCheckedChange: (checked) => {
166
+ return (_jsxs("div", { className: cn('space-y-2.5 rounded-md border bg-white px-3 py-2.5 transition-all', isExample ? 'border-amber-200 bg-amber-50/20' : 'border-gray-200'), children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("span", { className: "inline-flex h-7 items-center justify-center rounded-lg bg-indigo-100 px-2.5 text-xs font-bold text-indigo-800", children: ["#", index + 1] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: `example-${index}`, checked: isExample || false, onCheckedChange: (checked) => {
169
167
  setValue(`items.${index}.isExample`, checked, { shouldDirty: true });
170
168
  if (checked) {
171
169
  setValue(`items.${index}.points`, 0, { shouldDirty: true });
172
170
  }
173
- } }), _jsx(Label, { htmlFor: `example-${index}`, className: "text-xs font-medium text-gray-700 cursor-pointer", children: "C\u00E2u v\u00ED d\u1EE5 m\u1EABu (0 \u0111i\u1EC3m)" })] })] }), _jsxs("div", { className: "flex items-center gap-3", children: [!isExample && (_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Label, { className: "text-xs font-medium text-gray-600", children: "\u0110i\u1EC3m:" }), _jsx(Input, { type: "number", ...register(`items.${index}.points`, { valueAsNumber: true }), className: "h-8 w-16 text-center text-xs", min: 0, step: 0.5 })] })), fields.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => remove(index), className: "h-8 w-8 p-0 text-gray-400 hover:text-rose-600 hover:bg-rose-50", title: "Xo\u00E1 c\u00E2u n\u00E0y", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] })] }), _jsxs("div", { className: "grid grid-cols-1 gap-4 lg:grid-cols-12", children: [_jsxs("div", { className: "space-y-1.5 lg:col-span-8", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs(Label, { className: "text-xs font-bold text-gray-700", children: ["C\u00E2u \u0111\u00FAng ho\u00E0n ch\u1EC9nh (Ordered Phrase) ", _jsx("span", { className: "text-rose-500", children: "*" })] }), _jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => handleAutoScramble(index), className: "h-6 text-[11px] text-indigo-600 hover:bg-indigo-50 font-medium px-2", children: [_jsx(Shuffle, { className: "h-3 w-3 mr-1" }), " T\u1EF1 x\u00E1o tr\u1ED9n t\u1EEB"] })] }), _jsx(Input, { ...register(`items.${index}.orderedPhrase`), placeholder: "V\u00ED d\u1EE5: I'm sure everything will be OK", className: "text-sm font-medium" }), errors.items?.[index]?.orderedPhrase && (_jsx("p", { className: "text-xs text-rose-500", children: errors.items[index]?.orderedPhrase?.message }))] }), _jsxs("div", { className: "space-y-1.5 lg:col-span-4", children: [_jsxs(Label, { className: "text-xs font-bold text-gray-700", children: ["Nh\u00E3n \u0111\u00FAng (Match Label) ", _jsx("span", { className: "text-rose-500", children: "*" })] }), _jsx(Controller, { control: control, name: `items.${index}.matchLabel`, render: ({ field }) => (_jsx("div", { className: "flex flex-wrap gap-1", children: watchedValues.matchOptions?.map((opt) => (_jsx("button", { type: "button", onClick: () => field.onChange(opt), className: cn('h-9 w-9 rounded-lg font-bold text-xs transition-all flex items-center justify-center', field.value === opt
171
+ } }), _jsx(Label, { htmlFor: `example-${index}`, className: "text-xs font-medium text-gray-700 cursor-pointer", children: "C\u00E2u v\u00ED d\u1EE5 m\u1EABu (0 \u0111i\u1EC3m)" })] })] }), _jsxs("div", { className: "flex items-center gap-3", children: [!isExample && (_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Label, { className: "text-xs font-medium text-gray-600", children: "\u0110i\u1EC3m:" }), _jsx(Input, { type: "number", ...register(`items.${index}.points`, { valueAsNumber: true }), className: "h-8 w-16 text-center text-xs", min: 0, step: 0.5 })] })), fields.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => remove(index), className: "h-8 w-8 p-0 text-gray-400 hover:text-rose-600 hover:bg-rose-50", title: "Xo\u00E1 c\u00E2u n\u00E0y", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] })] }), _jsxs("div", { className: "grid grid-cols-1 gap-2.5 lg:grid-cols-12", children: [_jsxs("div", { className: "space-y-1.5 lg:col-span-8", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs(Label, { className: "text-xs font-bold text-gray-700", children: ["C\u00E2u \u0111\u00FAng ho\u00E0n ch\u1EC9nh (Ordered Phrase) ", _jsx("span", { className: "text-rose-500", children: "*" })] }), _jsxs(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => handleAutoScramble(index), className: "h-6 text-[11px] text-indigo-600 hover:bg-indigo-50 font-medium px-2", children: [_jsx(Shuffle, { className: "h-3 w-3 mr-1" }), " T\u1EF1 x\u00E1o tr\u1ED9n t\u1EEB"] })] }), _jsx(Input, { ...register(`items.${index}.orderedPhrase`), placeholder: "V\u00ED d\u1EE5: I'm sure everything will be OK", className: "text-sm font-medium" }), errors.items?.[index]?.orderedPhrase && (_jsx("p", { className: "text-xs text-rose-500", children: errors.items[index]?.orderedPhrase?.message }))] }), _jsxs("div", { className: "space-y-1.5 lg:col-span-4", children: [_jsxs(Label, { className: "text-xs font-bold text-gray-700", children: ["Nh\u00E3n \u0111\u00FAng (Match Label) ", _jsx("span", { className: "text-rose-500", children: "*" })] }), _jsx(Controller, { control: control, name: `items.${index}.matchLabel`, render: ({ field }) => (_jsx("div", { className: "flex flex-wrap gap-1", children: watchedValues.matchOptions?.map((opt) => (_jsx("button", { type: "button", onClick: () => field.onChange(opt), className: cn('h-9 w-9 rounded-lg font-bold text-xs transition-all flex items-center justify-center', field.value === opt
174
172
  ? 'bg-indigo-600 text-white shadow-xs'
175
- : 'border border-gray-200 bg-white text-gray-700 hover:border-indigo-300'), children: opt }, opt))) })) }), errors.items?.[index]?.matchLabel && (_jsx("p", { className: "text-xs text-rose-500", children: errors.items[index]?.matchLabel?.message }))] })] }), _jsxs("div", { className: "rounded-xl border border-dashed border-gray-200 bg-gray-50/70 p-3 space-y-2", children: [_jsxs(Label, { className: "text-xs font-bold text-gray-600 flex items-center justify-between", children: [_jsxs("span", { children: ["C\u00E1c t\u1EEB b\u1ECB x\u00E1o tr\u1ED9n (Scrambled Words) (", itemWords.length, ")"] }), _jsx("span", { className: "text-[11px] font-normal text-gray-400", children: "Th\u1EE9 t\u1EF1 t\u1EEB \u1EDF \u0111\u00E2y s\u1EBD hi\u1EC3n th\u1ECB cho h\u1ECDc sinh" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [itemWords.map((word, wordIdx) => (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-lg border border-gray-300 bg-white px-2.5 py-1 text-xs font-semibold text-gray-800 shadow-xs", children: [_jsx("span", { children: word }), _jsx("button", { type: "button", onClick: () => handleRemoveWord(index, wordIdx), className: "text-gray-400 hover:text-rose-600 transition-colors", children: _jsx(Trash2, { className: "h-3 w-3" }) })] }, wordIdx))), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Input, { value: newWordInputs[index] || '', onChange: (e) => setNewWordInputs((prev) => ({ ...prev, [index]: e.target.value })), placeholder: "+ Th\u00EAm t\u1EEB", className: "h-7 w-28 text-xs", onKeyDown: (e) => {
173
+ : 'border border-gray-200 bg-white text-gray-700 hover:border-indigo-300'), children: opt }, opt))) })) }), errors.items?.[index]?.matchLabel && (_jsx("p", { className: "text-xs text-rose-500", children: errors.items[index]?.matchLabel?.message }))] })] }), _jsxs("div", { className: "space-y-2 rounded-md border border-dashed border-gray-200 bg-gray-50/70 px-3 py-2.5", children: [_jsxs(Label, { className: "text-xs font-bold text-gray-600 flex items-center justify-between", children: [_jsxs("span", { children: ["C\u00E1c t\u1EEB b\u1ECB x\u00E1o tr\u1ED9n (Scrambled Words) (", itemWords.length, ")"] }), _jsx("span", { className: "text-[11px] font-normal text-gray-400", children: "Th\u1EE9 t\u1EF1 t\u1EEB \u1EDF \u0111\u00E2y s\u1EBD hi\u1EC3n th\u1ECB cho h\u1ECDc sinh" })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [itemWords.map((word, wordIdx) => (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-lg border border-gray-300 bg-white px-2.5 py-1 text-xs font-semibold text-gray-800 shadow-xs", children: [_jsx("span", { children: word }), _jsx("button", { type: "button", onClick: () => handleRemoveWord(index, wordIdx), className: "text-gray-400 hover:text-rose-600 transition-colors", children: _jsx(Trash2, { className: "h-3 w-3" }) })] }, wordIdx))), _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx(Input, { value: newWordInputs[index] || '', onChange: (e) => setNewWordInputs((prev) => ({ ...prev, [index]: e.target.value })), placeholder: "+ Th\u00EAm t\u1EEB", className: "h-7 w-28 text-xs", onKeyDown: (e) => {
176
174
  if (e.key === 'Enter') {
177
175
  e.preventDefault();
178
176
  handleAddWord(index);
179
177
  }
180
178
  } }), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => handleAddWord(index), className: "h-7 text-xs font-medium px-2", children: "Th\u00EAm" })] })] }), errors.items?.[index]?.scrambledWords && (_jsx("p", { className: "text-xs text-rose-500", children: errors.items[index]?.scrambledWords?.message }))] })] }, field.id));
181
- })] }), _jsxs("div", { className: "rounded-2xl border border-gray-200 bg-white p-5 shadow-xs space-y-1.5", children: [_jsx(Label, { className: "text-xs font-bold text-gray-700 uppercase tracking-wider", children: "3. Gi\u1EA3i th\u00EDch \u0111\u00E1p \u00E1n (Explanation - Tu\u1EF3 ch\u1ECDn)" }), _jsx(Textarea, { ...register('explanation'), rows: 2, placeholder: "Nh\u1EADp gi\u1EA3i th\u00EDch cho \u0111\u1EC1 b\u00E0i n\u00E0y n\u1EBFu c\u00F3...", className: "text-sm" })] })] }))] }));
179
+ }), _jsx(CompactExplanationToggle, { value: watchedValues.explanation || '', onValueChange: (value) => setValue('explanation', value, { shouldDirty: true }), defaultVisible: Boolean(initialData?.explanation) })] })] }))] }));
182
180
  }
@@ -86,7 +86,7 @@ export function WordOrderingClient({ questionData, onSubmit, isReviewMode = fals
86
86
  const isWrong = isReviewMode && selectedWords.length > 0 && !isCorrect;
87
87
  const isComplete = selectedWords.length === words.length;
88
88
  const displayExplanation = explanation || questionData.explanation;
89
- return (_jsx("div", { className: "space-y-4", children: _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-5 py-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-600 text-white shadow-sm", children: _jsx(ArrowUpDown, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "S\u1EAFp x\u1EBFp t\u1EEB" }), isExample && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-100 px-2.5 py-0.5 text-xs font-semibold text-amber-700", children: [_jsx(Lightbulb, { className: "h-3 w-3" }), "V\u00ED d\u1EE5"] }))] }), isReviewMode && isComplete && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold', isCorrect ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'), children: [isCorrect ? _jsx(Check, { className: "h-3.5 w-3.5" }) : _jsx(X, { className: "h-3.5 w-3.5" }), isCorrect ? 'Đúng' : 'Sai'] }))] }), _jsxs("div", { className: "p-5 space-y-5", children: [(imagePreviewUrl || imageUrl) && (_jsx("div", { className: "flex justify-center", children: _jsx("div", { className: "overflow-hidden rounded-lg border border-gray-200 bg-gray-50", children: isLoading ? (_jsx("div", { className: "flex h-48 w-48 items-center justify-center", children: _jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-amber-500" }) })) : (_jsx("img", { src: imagePreviewUrl || imageUrl, alt: "H\u00ECnh \u1EA3nh c\u00E2u h\u1ECFi", className: "max-h-56 w-auto object-contain p-2" })) }) })), _jsxs("div", { className: "space-y-2", children: [_jsx("span", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider", children: "\u0110\u00E1p \u00E1n c\u1EE7a b\u1EA1n" }), _jsx("div", { className: "flex flex-wrap gap-2 min-h-[48px] rounded-lg border-2 border-dashed border-gray-300 bg-gray-50 p-3", children: selectedWords.length === 0 ? (_jsx("span", { className: "text-sm text-gray-400 italic", children: "Nh\u1EA5n v\u00E0o c\u00E1c t\u1EEB b\u00EAn d\u01B0\u1EDBi \u0111\u1EC3 s\u1EAFp x\u1EBFp..." })) : (selectedWords.map((word, index) => {
89
+ return (_jsx("div", { className: "space-y-4", children: _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-md bg-gradient-to-br from-amber-500 to-orange-600 text-white", children: _jsx(ArrowUpDown, { className: "h-3.5 w-3.5" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "S\u1EAFp x\u1EBFp t\u1EEB" }), isExample && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-100 px-2.5 py-0.5 text-xs font-semibold text-amber-700", children: [_jsx(Lightbulb, { className: "h-3 w-3" }), "V\u00ED d\u1EE5"] }))] }), isReviewMode && isComplete && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold', isCorrect ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'), children: [isCorrect ? _jsx(Check, { className: "h-3.5 w-3.5" }) : _jsx(X, { className: "h-3.5 w-3.5" }), isCorrect ? 'Đúng' : 'Sai'] }))] }), _jsxs("div", { className: "space-y-3 p-3", children: [(imagePreviewUrl || imageUrl) && (_jsx("div", { className: "flex justify-center", children: _jsx("div", { className: "overflow-hidden rounded-lg border border-gray-200 bg-gray-50", children: isLoading ? (_jsx("div", { className: "flex h-48 w-48 items-center justify-center", children: _jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-amber-500" }) })) : (_jsx("img", { src: imagePreviewUrl || imageUrl, alt: "H\u00ECnh \u1EA3nh c\u00E2u h\u1ECFi", className: "max-h-56 w-auto object-contain p-2" })) }) })), _jsxs("div", { className: "space-y-2", children: [_jsx("span", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider", children: "\u0110\u00E1p \u00E1n c\u1EE7a b\u1EA1n" }), _jsx("div", { className: "flex flex-wrap gap-2 min-h-[48px] rounded-lg border-2 border-dashed border-gray-300 bg-gray-50 p-3", children: selectedWords.length === 0 ? (_jsx("span", { className: "text-sm text-gray-400 italic", children: "Nh\u1EA5n v\u00E0o c\u00E1c t\u1EEB b\u00EAn d\u01B0\u1EDBi \u0111\u1EC3 s\u1EAFp x\u1EBFp..." })) : (selectedWords.map((word, index) => {
90
90
  const isWordCorrect = isReviewMode && answer[index] === word;
91
91
  const isWordWrong = isReviewMode && answer[index] !== word;
92
92
  return (_jsxs("button", { type: "button", onClick: () => handleRemoveWord(index), disabled: isReviewMode, className: cn('inline-flex items-center gap-1 rounded-lg px-3 py-2 text-sm font-semibold transition-all shadow-sm', isReviewMode
@@ -3,15 +3,11 @@ 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, useCallback } from 'react';
5
5
  import { Button } from '../../../../components/ui/button';
6
- 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 { Checkbox } from '../../../../components/ui/checkbox';
12
7
  import { usePresignedFileUrl, useDebouncedCallback } from '../../../../shared/lib/hooks';
13
8
  import { useReactHookForm } from '../../../../shared/lib/hooks/useReactHookForm';
14
- import { Pencil, Eye, Plus, Trash2, ArrowUpDown, GripVertical, Shuffle, ImageIcon, BookOpen, Lightbulb } from 'lucide-react';
9
+ import { Plus, Trash2, ArrowUpDown, GripVertical, Shuffle, ImageIcon } from 'lucide-react';
10
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
15
11
  import { FileUpload } from '../../../../components/ui/file-upload';
16
12
  import { WordOrderingClient } from './WordOrderingClient';
17
13
  import { wordOrderingFormSchema, } from '../../../../shared/lib/validation/word-ordering.validation';
@@ -32,6 +28,7 @@ function WordOrderingCreatorContent({ initialData, onSave, onCancel, onChange, e
32
28
  // UI State
33
29
  const [isClientMode, setIsClientMode] = useState(false);
34
30
  const [imagePreviewUrl, setImagePreviewUrl] = useState('');
31
+ const [isImageInputVisible, setIsImageInputVisible] = useState(() => Boolean(apiData?.imageUrl));
35
32
  // Refs
36
33
  const previousInitialDataRef = useRef(initialData);
37
34
  const isInitializingRef = useRef(false);
@@ -172,20 +169,32 @@ function WordOrderingCreatorContent({ initialData, onSave, onCancel, onChange, e
172
169
  isExample: watchedIsExample,
173
170
  explanation: watchedExplanation,
174
171
  };
175
- 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: "S\u1EAFp x\u1EBFp t\u1EEB (Xem tr\u01B0\u1EDBc)" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Ch\u1EBF \u0111\u1ED9 xem tr\u01B0\u1EDBc - Hi\u1EC7n \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 ch\u1EC9nh s\u1EEDa"] })] }) }) }), _jsx(WordOrderingClient, { questionData: questionData, isReviewMode: true, userAnswer: watchedAnswer })] }));
172
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(WordOrderingClient, { questionData: questionData, isReviewMode: true, userAnswer: watchedAnswer })] }));
176
173
  }
177
174
  // Creator UI
178
- return (_jsxs("div", { className: "space-y-4", children: [_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 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(ArrowUpDown, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-base font-bold text-gray-900", children: "S\u1EAFp x\u1EBFp t\u1EEB" }), _jsx("p", { className: "text-xs text-gray-500", children: "S\u1EAFp x\u1EBFp c\u00E1c ch\u1EEF c\u00E1i / t\u1EEB th\u00E0nh t\u1EEB / c\u00E2u \u0111\u00FAng" })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [headerExtra, _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: watchedWords.filter((w) => w.trim()).length < 2, className: "gap-2 border-amber-200 text-amber-600 hover:bg-amber-50 hover:text-amber-700", children: [_jsx(Eye, { className: "h-4 w-4" }), "Xem tr\u01B0\u1EDBc"] })] })] }) }), _jsxs(CardContent, { className: "space-y-4 px-4 pb-4", children: [_jsx("div", { className: "rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-5 shadow-sm", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-blue-100", children: _jsx(Lightbulb, { className: "h-4 w-4 text-blue-600" }) }), _jsxs("div", { className: "text-sm text-blue-900", children: [_jsx("p", { className: "font-semibold text-blue-800", children: "H\u01B0\u1EDBng d\u1EABn t\u1EA1o c\u00E2u h\u1ECFi" }), _jsxs("ul", { className: "mt-2 space-y-1.5 text-blue-700", children: [_jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-blue-400" }), "Nh\u1EADp ", _jsx("strong", { children: "\u0111\u00E1p \u00E1n \u0111\u00FAng" }), " theo th\u1EE9 t\u1EF1 ch\u00EDnh x\u00E1c (ph\u00EDa d\u01B0\u1EDBi)"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-blue-400" }), "Nh\u1EA5n ", _jsx("strong", { children: "\"X\u00E1o tr\u1ED9n \u2192 T\u1EEB\"" }), " \u0111\u1EC3 t\u1EF1 \u0111\u1ED9ng t\u1EA1o danh s\u00E1ch t\u1EEB hi\u1EC3n th\u1ECB cho h\u1ECDc sinh"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-blue-400" }), "Upload ", _jsx("strong", { children: "h\u00ECnh \u1EA3nh" }), " minh ho\u1EA1 (n\u1EBFu c\u00F3)"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-blue-400" }), "Nh\u1EADp ", _jsx("strong", { children: "gi\u1EA3i th\u00EDch" }), " cho \u0111\u00E1p \u00E1n \u0111\u00FAng (tu\u1EF3 ch\u1ECDn)"] })] })] })] }) }), _jsxs("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: "flex items-center gap-2", children: [_jsx(Checkbox, { id: "isExample", checked: watchedIsExample || false, onCheckedChange: (checked) => form.setValue('isExample', !!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" })] })] }), _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-amber-500" }), "H\u00ECnh \u1EA3nh"] }), _jsx(FileUpload, { id: "word-ordering-image", label: "", accept: "image/*", value: watchedImageUrl || '', onChange: (url) => {
179
- form.setValue('imageUrl', url, { shouldValidate: true });
180
- }, onPresignedUrlChange: setImagePreviewUrl, maxSize: 5, placeholder: "Upload h\u00ECnh \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" }))] })] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-green-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-6 w-6 items-center justify-center rounded-md bg-green-100", children: _jsx(ArrowUpDown, { className: "h-3.5 w-3.5 text-green-600" }) }), _jsxs(CardTitle, { className: "text-base font-semibold text-gray-800", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng (", watchedAnswer.length, " t\u1EEB/ch\u1EEF)"] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: shuffleWords, disabled: watchedAnswer.filter((a) => a.trim()).length < 2, className: "gap-2 border-amber-200 text-amber-600 hover:bg-amber-50", title: "X\u00E1o tr\u1ED9n th\u1EE9 t\u1EF1 \u0111\u00E1p \u00E1n \u0111\u1EC3 t\u1EA1o danh s\u00E1ch t\u1EEB", children: [_jsx(Shuffle, { className: "h-4 w-4" }), "X\u00E1o tr\u1ED9n \u2192 T\u1EEB"] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: addWord, className: "gap-2 border-green-200 text-green-600 hover:bg-green-50 hover:text-green-700", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm"] })] })] }) }), _jsxs(CardContent, { className: "space-y-3 px-4 pb-4", children: [_jsxs("p", { className: "text-xs text-gray-500", children: ["Nh\u1EADp c\u00E1c t\u1EEB/ch\u1EEF c\u00E1i theo ", _jsx("strong", { children: "th\u1EE9 t\u1EF1 \u0111\u00FAng" }), ". Nh\u1EA5n \"X\u00E1o tr\u1ED9n \u2192 T\u1EEB\" \u0111\u1EC3 t\u1EA1o danh s\u00E1ch t\u1EEB x\u00E1o tr\u1ED9n t\u1EF1 \u0111\u1ED9ng."] }), watchedAnswer.map((word, index) => (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-green-500 to-emerald-600 text-white text-sm font-bold shadow-sm", children: index + 1 }), _jsx("div", { className: "flex-1", children: _jsx(Input, { value: word, onChange: (e) => {
181
- const newAnswer = [...watchedAnswer];
182
- newAnswer[index] = e.target.value;
183
- form.setValue('answer', newAnswer, { shouldValidate: true });
184
- }, placeholder: `Từ/chữ cái thứ ${index + 1}`, className: "border-gray-200" }) }), watchedAnswer.length > 2 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => removeWord(index), className: "text-red-500 hover:text-red-700 hover:bg-red-50", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }, index))), form.formState.errors.answer?.message && (_jsx("p", { className: "text-sm text-red-600", children: form.formState.errors.answer.message }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-orange-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-6 w-6 items-center justify-center rounded-md bg-orange-100", children: _jsx(GripVertical, { className: "h-3.5 w-3.5 text-orange-600" }) }), _jsxs(CardTitle, { className: "text-base font-semibold text-gray-800", children: ["T\u1EEB hi\u1EC3n th\u1ECB (x\u00E1o tr\u1ED9n) (", watchedWords.length, " t\u1EEB/ch\u1EEF)"] })] }) }), _jsxs(CardContent, { className: "space-y-3 px-4 pb-4", children: [_jsx("p", { className: "text-xs text-gray-500", children: "Danh s\u00E1ch t\u1EEB/ch\u1EEF c\u00E1i hi\u1EC3n th\u1ECB cho h\u1ECDc sinh (\u0111\u00E3 x\u00E1o tr\u1ED9n). C\u00F3 th\u1EC3 ch\u1EC9nh s\u1EEDa tr\u1EF1c ti\u1EBFp." }), watchedWords.map((word, index) => (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-orange-400 to-amber-500 text-white text-sm font-bold shadow-sm", children: index + 1 }), _jsx("div", { className: "flex-1", children: _jsx(Input, { value: word, onChange: (e) => {
185
- const newWords = [...watchedWords];
186
- newWords[index] = e.target.value;
187
- form.setValue('words', newWords, { shouldValidate: true });
188
- }, placeholder: `Từ/chữ cái thứ ${index + 1}`, className: "border-gray-200" }) })] }, index))), form.formState.errors.words?.message && (_jsx("p", { className: "text-sm text-red-600", children: form.formState.errors.words.message }))] })] }), _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, { ...form.register('explanation'), 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" }) })] })] }));
175
+ return (_jsxs("div", { className: "space-y-3", children: [_jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(ArrowUpDown, { className: "h-3.5 w-3.5" }), title: "S\u1EAFp x\u1EBFp t\u1EEB", points: {
176
+ value: watchedPoints,
177
+ onValueChange: (value) => form.setValue('points', value, { shouldValidate: true }),
178
+ error: form.hasError('points') ? form.getFieldError('points') : undefined,
179
+ }, example: {
180
+ checked: watchedIsExample || false,
181
+ onChange: (checked) => form.setValue('isExample', checked),
182
+ }, extraActions: _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 ${watchedImageUrl || isImageInputVisible
183
+ ? 'bg-indigo-600 text-white shadow-sm'
184
+ : 'bg-indigo-50 text-indigo-600 hover:bg-indigo-100'}`, children: _jsx(ImageIcon, { className: "h-4 w-4" }) }), headerExtra: headerExtra, preview: {
185
+ onClick: () => setIsClientMode(true),
186
+ disabled: watchedWords.filter((w) => w.trim()).length < 2,
187
+ } }), children: [isImageInputVisible && (_jsxs("div", { className: "space-y-2", children: [_jsx(FileUpload, { id: "word-ordering-image", label: "", accept: "image/*", value: watchedImageUrl || '', onChange: (url) => {
188
+ form.setValue('imageUrl', url, { shouldValidate: true });
189
+ }, onPresignedUrlChange: setImagePreviewUrl, maxSize: 5, placeholder: "Upload h\u00ECnh \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" }))] })), _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("p", { className: "text-xs font-medium text-gray-600", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng ", _jsxs("span", { className: "font-normal text-gray-500", children: ["(", watchedAnswer.length, " t\u1EEB/ch\u1EEF)"] })] }), _jsxs("div", { className: "flex gap-1.5", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: shuffleWords, disabled: watchedAnswer.filter((a) => a.trim()).length < 2, className: "h-8 gap-1.5 border-amber-200 px-2.5 text-xs text-amber-600 hover:bg-amber-50", title: "X\u00E1o tr\u1ED9n th\u1EE9 t\u1EF1 \u0111\u00E1p \u00E1n \u0111\u1EC3 t\u1EA1o danh s\u00E1ch t\u1EEB", children: [_jsx(Shuffle, { className: "h-3.5 w-3.5" }), "X\u00E1o tr\u1ED9n \u2192 T\u1EEB"] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: addWord, className: "h-8 gap-1.5 border-green-200 px-2.5 text-xs text-green-600 hover:bg-green-50 hover:text-green-700", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm"] })] })] }), watchedAnswer.map((word, index) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-md bg-gradient-to-br from-green-500 to-emerald-600 text-xs font-bold text-white", children: index + 1 }), _jsx(Input, { value: word, onChange: (e) => {
190
+ const newAnswer = [...watchedAnswer];
191
+ newAnswer[index] = e.target.value;
192
+ form.setValue('answer', newAnswer, { shouldValidate: true });
193
+ }, placeholder: `Từ/chữ cái thứ ${index + 1}`, className: "h-8 border-gray-200" }), watchedAnswer.length > 2 && (_jsx(Button, { variant: "ghost", size: "icon", onClick: () => removeWord(index), className: "h-7 w-7 text-red-500 hover:bg-red-50 hover:text-red-700", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }, index))), form.formState.errors.answer?.message && (_jsx("p", { className: "text-sm text-red-600", children: form.formState.errors.answer.message }))] }), _jsxs(CompactFieldCard, { header: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-orange-100", children: _jsx(GripVertical, { className: "h-3.5 w-3.5 text-orange-600" }) }), _jsxs("span", { className: "text-sm font-semibold text-gray-800", children: ["T\u1EEB hi\u1EC3n th\u1ECB (x\u00E1o tr\u1ED9n) ", _jsxs("span", { className: "text-xs font-normal text-gray-500", children: ["(", watchedWords.length, " t\u1EEB/ch\u1EEF)"] })] })] }), children: [watchedWords.map((word, index) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-md bg-gradient-to-br from-orange-400 to-amber-500 text-xs font-bold text-white", children: index + 1 }), _jsx(Input, { value: word, onChange: (e) => {
194
+ const newWords = [...watchedWords];
195
+ newWords[index] = e.target.value;
196
+ form.setValue('words', newWords, { shouldValidate: true });
197
+ }, placeholder: `Từ/chữ cái thứ ${index + 1}`, className: "h-8 border-gray-200" })] }, index))), form.formState.errors.words?.message && (_jsx("p", { className: "text-sm text-red-600", children: form.formState.errors.words.message })), _jsx(CompactExplanationToggle, { value: watchedExplanation ?? '', onValueChange: (value) => form.setValue('explanation', value, { shouldValidate: true }), defaultVisible: Boolean(apiData?.explanation) })] })] }));
189
198
  }
190
199
  export function WordOrderingCreator(props) {
191
200
  return _jsx(WordOrderingCreatorContent, { ...props });
@@ -16,11 +16,11 @@ export function WordOrderingGroupClient({ questionData, isReviewMode = false, us
16
16
  });
17
17
  onAnswerChange(next);
18
18
  };
19
- return (_jsxs("div", { className: "space-y-4", children: [questionData.instruction && (_jsx("div", { className: "rounded-xl border border-indigo-100 bg-indigo-50 px-4 py-3 text-sm font-medium text-indigo-800", children: questionData.instruction })), items.map((item, itemIndex) => (_jsxs("div", { className: "rounded-xl border border-gray-200 bg-white p-4", 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"] })] }), _jsx(WordOrderingClient, { questionData: {
20
- words: item.words,
21
- answer: item.answer,
22
- isExample: item.isExample,
23
- points: item.points,
24
- imageUrl: item.imageUrl,
25
- }, isReviewMode: isReviewMode || item.isExample, userAnswer: item.isExample ? item.answer : userAnswers[itemIndex], onSubmit: (ordered) => handleItemSubmit(itemIndex, ordered) })] }, `${item.questionNumber}-${itemIndex}`))), 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] }))] }));
19
+ return (_jsxs("div", { className: "space-y-4", children: [questionData.instruction && (_jsx("div", { className: "rounded-xl border border-indigo-100 bg-indigo-50 px-4 py-3 text-sm font-medium text-indigo-800", children: questionData.instruction })), items.map((item, itemIndex) => (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white", 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"] })] }), _jsx("div", { className: "space-y-3 p-3", children: _jsx(WordOrderingClient, { questionData: {
20
+ words: item.words,
21
+ answer: item.answer,
22
+ isExample: item.isExample,
23
+ points: item.points,
24
+ imageUrl: item.imageUrl,
25
+ }, isReviewMode: isReviewMode || item.isExample, userAnswer: item.isExample ? item.answer : userAnswers[itemIndex], onSubmit: (ordered) => handleItemSubmit(itemIndex, ordered) }) })] }, `${item.questionNumber}-${itemIndex}`))), 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] }))] }));
26
26
  }
@@ -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, ImageIcon, Pencil, Plus, Star, Trash2 } from 'lucide-react';
20
+ import { ImageIcon, ListOrdered, Plus, Star, Trash2 } from 'lucide-react';
21
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
22
22
  import { FileUpload } from '../../../../components/ui/file-upload';
23
23
  import { WordOrderingGroupClient } from './WordOrderingGroupClient';
24
24
  import { WORD_ORDERING_GROUP_DEFAULT_INSTRUCTION } from '../../_shared/types/word-ordering-group.type';
@@ -108,15 +108,18 @@ export function WordOrderingGroupCreator({ initialData, onChange, externalErrors
108
108
  };
109
109
  const currentPayload = buildPayload();
110
110
  const displayErrors = externalErrors && externalErrors.length > 0 ? externalErrors : errors;
111
- 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 S\u1EAFp x\u1EBFp t\u1EEB (nh\u00F3m)" }), _jsx("p", { className: "text-sm text-gray-500", children: "M\u1ED7i d\u00F2ng m\u1ED9t c\u00E2u x\u00E1o tr\u1ED9n. M\u1ED7i token \u0111i\u1EC1n m\u1ED9t \u00F4." })] }), _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(WordOrderingGroupClient, { questionData: currentPayload }) })] })) : (_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" }) }), _jsx(CardContent, { children: _jsx(Input, { value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: WORD_ORDERING_GROUP_DEFAULT_INSTRUCTION }) })] }), _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: () => setItems((prev) => [...prev, createEmptyWordOrderingGroupItem(prev.length + 1)]), children: [_jsx(Plus, { className: "mr-1 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 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, {
112
- points: parsePointsValue(event.target.value, true),
113
- }) }) })), 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" }) }))] })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-amber-500" }), "H\u00ECnh \u1EA3nh (tu\u1EF3 ch\u1ECDn)"] }), _jsx(FileUpload, { id: `word-ordering-group-image-${index}`, label: "", accept: "image/*", value: item.imageUrl || '', onChange: (url) => updateItem(index, { imageUrl: url || undefined }), maxSize: 5, placeholder: "Upload h\u00ECnh \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" })] }), _jsxs("div", { children: [_jsx(Label, { children: "Token x\u00E1o tr\u1ED9n" }), _jsx(TokenBoxRow, { tokens: withMinTokens(item.words), onChange: (tokenIndex, value) => updateItem(index, {
114
- words: setTokenAt(item.words, tokenIndex, value),
115
- }), onAdd: () => updateItem(index, { words: [...item.words, ''] }), onRemove: (tokenIndex) => updateItem(index, {
116
- words: item.words.filter((_, current) => current !== tokenIndex),
117
- }) })] }), _jsxs("div", { children: [_jsx(Label, { children: "Th\u1EE9 t\u1EF1 \u0111\u00FAng" }), _jsx(TokenBoxRow, { tokens: withMinTokens(item.answer), onChange: (tokenIndex, value) => updateItem(index, {
118
- answer: setTokenAt(item.answer, tokenIndex, value),
119
- }), onAdd: () => updateItem(index, { answer: [...item.answer, ''] }), onRemove: (tokenIndex) => updateItem(index, {
120
- answer: item.answer.filter((_, current) => current !== tokenIndex),
121
- }) })] })] }, 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 }) })] })] }))] }));
111
+ if (isClientMode) {
112
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(WordOrderingGroupClient, { questionData: currentPayload })] }));
113
+ }
114
+ 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)) }) })), _jsx(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(ListOrdered, { className: "h-3.5 w-3.5" }), title: "S\u1EAFp x\u1EBFp t\u1EEB (Nh\u00F3m)", preview: { onClick: () => setIsClientMode(true) } }), children: _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "word-ordering-instruction", className: "text-sm font-semibold text-gray-800", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Input, { id: "word-ordering-instruction", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: WORD_ORDERING_GROUP_DEFAULT_INSTRUCTION, className: "h-10 border-gray-200 bg-white" })] }) }), _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: () => setItems((prev) => [...prev, createEmptyWordOrderingGroupItem(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 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", 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, {
115
+ points: parsePointsValue(event.target.value, true),
116
+ }) }) })), 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" }) }))] })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { className: "flex items-center gap-2", children: [_jsx(ImageIcon, { className: "h-3.5 w-3.5 text-amber-500" }), "H\u00ECnh \u1EA3nh (tu\u1EF3 ch\u1ECDn)"] }), _jsx(FileUpload, { id: `word-ordering-group-image-${index}`, label: "", accept: "image/*", value: item.imageUrl || '', onChange: (url) => updateItem(index, { imageUrl: url || undefined }), maxSize: 5, placeholder: "Upload h\u00ECnh \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" })] }), _jsxs("div", { children: [_jsx(Label, { children: "Token x\u00E1o tr\u1ED9n" }), _jsx(TokenBoxRow, { tokens: withMinTokens(item.words), onChange: (tokenIndex, value) => updateItem(index, {
117
+ words: setTokenAt(item.words, tokenIndex, value),
118
+ }), onAdd: () => updateItem(index, { words: [...item.words, ''] }), onRemove: (tokenIndex) => updateItem(index, {
119
+ words: item.words.filter((_, current) => current !== tokenIndex),
120
+ }) })] }), _jsxs("div", { children: [_jsx(Label, { children: "Th\u1EE9 t\u1EF1 \u0111\u00FAng" }), _jsx(TokenBoxRow, { tokens: withMinTokens(item.answer), onChange: (tokenIndex, value) => updateItem(index, {
121
+ answer: setTokenAt(item.answer, tokenIndex, value),
122
+ }), onAdd: () => updateItem(index, { answer: [...item.answer, ''] }), onRemove: (tokenIndex) => updateItem(index, {
123
+ answer: item.answer.filter((_, current) => current !== tokenIndex),
124
+ }) })] })] }, index))), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] })] }));
122
125
  }
@@ -85,11 +85,11 @@ export function WriteAShortLetterClient({ questionData, onSubmit, isReviewMode =
85
85
  const target = minWords;
86
86
  return Math.min((wordCount / target) * 100, 100);
87
87
  }, [wordCount, minWords]);
88
- return (_jsxs("div", { className: "space-y-5", children: [instruction && (_jsx("div", { className: "overflow-hidden rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 shadow-sm", children: _jsxs("div", { className: "flex items-center gap-3 px-5 py-4", children: [_jsx("div", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-blue-500 to-indigo-600 text-white shadow-sm", children: _jsx(BookOpen, { className: "h-5 w-5" }) }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-blue-900", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(HtmlOrText, { value: instruction, className: "text-sm text-blue-700" })] })] }) })), passageList.length > 0 && (_jsxs("div", { className: "overflow-hidden rounded-xl border border-indigo-100 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-3 border-b border-indigo-50 bg-gradient-to-r from-indigo-50 to-white px-5 py-3", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-indigo-500 to-blue-600 text-white shadow-sm", children: _jsx(BookOpen, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-indigo-800", children: "B\u00E0i \u0111\u1ECDc" })] }), _jsx("div", { className: "space-y-3 p-5", children: passageList.map((item, index) => (_jsx(HtmlOrText, { value: item, className: "text-sm leading-relaxed text-gray-800" }, index))) })] })), guideImages && guideImages.length > 0 && (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-3 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-5 py-3", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-violet-500 to-purple-600 text-white shadow-sm", children: _jsx(ImageIcon, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "\u1EA2nh h\u01B0\u1EDBng d\u1EABn" })] }), _jsx("div", { className: "p-5", children: _jsx("div", { className: "flex flex-col gap-4", children: guideImages.map((imageKey, index) => (_jsx(GuideImageDisplay, { imageKey: imageKey }, `${imageKey}-${index}`))) }) })] })), informations && informations.filter((i) => i?.trim()).length > 0 && (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-3 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-5 py-3", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-emerald-500 to-teal-600 text-white shadow-sm", children: _jsx(FileText, { className: "h-4 w-4" }) }), _jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["Th\u00F4ng tin c\u1EA7n bao g\u1ED3m (", informations.length, ")"] })] }), _jsx("div", { className: "p-5", children: _jsx("div", { className: "space-y-3", children: informations.filter((i) => i?.trim()).map((info, index) => (_jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-gray-100 bg-gradient-to-r from-white to-gray-50 p-3 transition-all hover:border-gray-200 hover:shadow-sm", children: [_jsx("div", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-emerald-400 to-teal-500 text-xs font-bold text-white shadow-sm", children: index + 1 }), _jsx(HtmlOrText, { value: info, className: "text-sm text-gray-700 leading-relaxed pt-0.5" })] }, index))) }) })] })), _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-5 py-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-purple-500 to-violet-600 text-white shadow-sm", children: _jsx(PenLine, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Vi\u1EBFt th\u01B0 c\u1EE7a b\u1EA1n" })] }), _jsxs("div", { className: cn('flex items-center gap-2 rounded-full px-3 py-1.5 text-xs font-semibold transition-all', isExceeded
88
+ return (_jsxs("div", { className: "space-y-3", children: [instruction && (_jsx("div", { className: "overflow-hidden rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 shadow-sm", children: _jsxs("div", { className: "flex items-center gap-3 px-3 py-2", children: [_jsx("div", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-blue-500 to-indigo-600 text-white shadow-sm", children: _jsx(BookOpen, { className: "h-5 w-5" }) }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-blue-900", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(HtmlOrText, { value: instruction, className: "text-sm text-blue-700" })] })] }) })), passageList.length > 0 && (_jsxs("div", { className: "overflow-hidden rounded-xl border border-indigo-100 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-3 border-b border-indigo-50 bg-gradient-to-r from-indigo-50 to-white px-3 py-2", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-indigo-500 to-blue-600 text-white shadow-sm", children: _jsx(BookOpen, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-indigo-800", children: "B\u00E0i \u0111\u1ECDc" })] }), _jsx("div", { className: "space-y-3 p-3", children: passageList.map((item, index) => (_jsx(HtmlOrText, { value: item, className: "text-sm leading-relaxed text-gray-800" }, index))) })] })), guideImages && guideImages.length > 0 && (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-3 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-3 py-2", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-violet-500 to-purple-600 text-white shadow-sm", children: _jsx(ImageIcon, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "\u1EA2nh h\u01B0\u1EDBng d\u1EABn" })] }), _jsx("div", { className: "p-3", children: _jsx("div", { className: "flex flex-col gap-4", children: guideImages.map((imageKey, index) => (_jsx(GuideImageDisplay, { imageKey: imageKey }, `${imageKey}-${index}`))) }) })] })), informations && informations.filter((i) => i?.trim()).length > 0 && (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-3 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-3 py-2", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-emerald-500 to-teal-600 text-white shadow-sm", children: _jsx(FileText, { className: "h-4 w-4" }) }), _jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["Th\u00F4ng tin c\u1EA7n bao g\u1ED3m (", informations.length, ")"] })] }), _jsx("div", { className: "p-3", children: _jsx("div", { className: "space-y-3", children: informations.filter((i) => i?.trim()).map((info, index) => (_jsxs("div", { className: "flex items-start gap-3 rounded-lg border border-gray-100 bg-gradient-to-r from-white to-gray-50 p-3 transition-all hover:border-gray-200 hover:shadow-sm", children: [_jsx("div", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-gradient-to-br from-emerald-400 to-teal-500 text-xs font-bold text-white shadow-sm", children: index + 1 }), _jsx(HtmlOrText, { value: info, className: "text-sm text-gray-700 leading-relaxed pt-0.5" })] }, index))) }) })] })), _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-3", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-purple-500 to-violet-600 text-white shadow-sm", children: _jsx(PenLine, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Vi\u1EBFt th\u01B0 c\u1EE7a b\u1EA1n" })] }), _jsxs("div", { className: cn('flex items-center gap-2 rounded-full px-3 py-1.5 text-xs font-semibold transition-all', isExceeded
89
89
  ? 'bg-red-100 text-red-700'
90
90
  : isValidWordCount
91
91
  ? 'bg-green-100 text-green-700'
92
- : 'bg-amber-100 text-amber-700'), children: [isValidWordCount && !isExceeded && _jsx(Check, { className: "h-3.5 w-3.5" }), isExceeded && _jsx(AlertCircle, { className: "h-3.5 w-3.5" }), _jsxs("span", { children: [wordCount, " / ", minWords, maxWords ? ` - ${maxWords}` : '+', " t\u1EEB"] })] })] }), _jsxs("div", { className: "p-5 space-y-4", children: [!isReviewMode && (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between text-xs text-gray-500", children: [_jsx("span", { children: "Ti\u1EBFn \u0111\u1ED9 vi\u1EBFt" }), _jsxs("span", { children: [Math.round(progressPercentage), "%"] })] }), _jsx("div", { className: "h-2 w-full overflow-hidden rounded-full bg-gray-100", children: _jsx("div", { className: cn('h-full transition-all duration-300 ease-out rounded-full', isExceeded
92
+ : 'bg-amber-100 text-amber-700'), children: [isValidWordCount && !isExceeded && _jsx(Check, { className: "h-3.5 w-3.5" }), isExceeded && _jsx(AlertCircle, { className: "h-3.5 w-3.5" }), _jsxs("span", { children: [wordCount, " / ", minWords, maxWords ? ` - ${maxWords}` : '+', " t\u1EEB"] })] })] }), _jsxs("div", { className: "p-3 space-y-3", children: [!isReviewMode && (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between text-xs text-gray-500", children: [_jsx("span", { children: "Ti\u1EBFn \u0111\u1ED9 vi\u1EBFt" }), _jsxs("span", { children: [Math.round(progressPercentage), "%"] })] }), _jsx("div", { className: "h-2 w-full overflow-hidden rounded-full bg-gray-100", children: _jsx("div", { className: cn('h-full transition-all duration-300 ease-out rounded-full', isExceeded
93
93
  ? 'bg-gradient-to-r from-red-400 to-rose-500'
94
94
  : isValidWordCount
95
95
  ? 'bg-gradient-to-r from-green-400 to-emerald-500'
@@ -4,11 +4,10 @@ import { useState, useRef, useEffect } 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 { PointsInput } from '../../../../components/ui/points-input';
8
7
  import { FileUpload } from '../../../../components/ui/file-upload';
9
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
10
8
  import { RichTextEditor } from '../../../../components/shared/RichTextEditor';
11
- import { Pencil, Eye, Plus, Trash2, Lightbulb, Mail, ImageIcon, X } from 'lucide-react';
9
+ import { Plus, Trash2, Mail, ImageIcon, X } from 'lucide-react';
10
+ import { CompactCreatorHeader, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
12
11
  import { WriteAShortLetterClient } from './WriteAShortLetterClient';
13
12
  import { useReactHookForm } from '../../../../shared/lib/hooks/useReactHookForm';
14
13
  import { usePresignedFileUrl } from '../../../../shared/lib/hooks/usePresignedFileUrl';
@@ -294,32 +293,39 @@ function WriteAShortLetterCreatorContent({ initialData, onChange, externalErrors
294
293
  guideImages: guideImages.length > 0 ? guideImages : undefined,
295
294
  ...(passage ? { passage } : {}),
296
295
  };
297
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Write a Short Letter (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(WriteAShortLetterClient, { questionData: questionData, onSubmit: () => { }, isReviewMode: true })] }));
296
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(WriteAShortLetterClient, { questionData: questionData, onSubmit: () => { }, isReviewMode: true })] }));
298
297
  }
299
- return (_jsxs("div", { className: "space-y-6", children: [externalErrors && externalErrors.length > 0 && (_jsx("div", { className: "rounded-lg border border-red-200 bg-red-50 p-4", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex-shrink-0", children: _jsx("svg", { className: "h-5 w-5 text-red-600", fill: "currentColor", viewBox: "0 0 20 20", children: _jsx("path", { fillRule: "evenodd", d: "M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z", clipRule: "evenodd" }) }) }), _jsxs("div", { className: "flex-1", children: [_jsx("h3", { className: "text-sm font-medium text-red-800", children: "Vui l\u00F2ng s\u1EEDa c\u00E1c l\u1ED7i sau:" }), _jsx("ul", { className: "mt-2 list-disc list-inside space-y-1", children: externalErrors.map((error, index) => (_jsx("li", { className: "text-sm text-red-700", children: error }, index))) })] })] }) })), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50", children: [_jsx("div", { className: "h-1.5 bg-gradient-to-r from-pink-500 via-rose-500 to-orange-500" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-pink-500 to-rose-600 shadow-md", children: _jsx(Mail, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold text-gray-900", children: "Write a Short Letter" }), _jsx("p", { className: "text-sm text-gray-500", children: "T\u1EA1o b\u00E0i t\u1EADp vi\u1EBFt th\u01B0 v\u1EDBi c\u00E1c th\u00F4ng tin c\u1EA7n bao g\u1ED3m" })] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: !minWords || minWords < 1, className: "gap-2 border-rose-200 text-rose-600 hover:bg-rose-50 hover:text-rose-700", children: [_jsx(Eye, { className: "h-4 w-4" }), "Xem tr\u01B0\u1EDBc"] })] }) }), _jsxs(CardContent, { className: "space-y-6", children: [_jsx("div", { className: "rounded-xl border border-pink-200 bg-gradient-to-r from-pink-50 to-rose-50 p-5 shadow-sm", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-pink-100", children: _jsx(Lightbulb, { className: "h-4 w-4 text-pink-600" }) }), _jsxs("div", { className: "text-sm text-pink-900", children: [_jsx("p", { className: "font-semibold text-pink-800", children: "H\u01B0\u1EDBng d\u1EABn t\u1EA1o c\u00E2u h\u1ECFi" }), _jsxs("ul", { className: "mt-2 space-y-1.5 text-pink-700", children: [_jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-pink-400" }), "Nh\u1EADp h\u01B0\u1EDBng d\u1EABn vi\u1EBFt th\u01B0 cho h\u1ECDc sinh"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-pink-400" }), "Li\u1EC7t k\u00EA c\u00E1c th\u00F4ng tin c\u1EA7n bao g\u1ED3m trong b\u00E0i vi\u1EBFt"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-pink-400" }), "Thi\u1EBFt l\u1EADp gi\u1EDBi h\u1EA1n s\u1ED1 t\u1EEB t\u1ED1i thi\u1EC3u/t\u1ED1i \u0111a"] })] })] })] }) }), _jsxs("div", { className: "grid grid-cols-1 gap-6 md:grid-cols-2", children: [_jsx("div", { children: _jsx(PointsInput, { defaultValue: points, onChange: (e) => setPoints(Number(e.target.value) || 1), error: errors.points }) }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-gray-50/50 p-4", children: [_jsx(Label, { className: "mb-3 block text-sm font-medium text-gray-700", children: "Gi\u1EDBi h\u1EA1n s\u1ED1 t\u1EEB" }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "flex-1", children: [_jsxs(Label, { htmlFor: "minWords", className: "text-xs text-gray-500", children: ["T\u1ED1i thi\u1EC3u ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "minWords", type: "number", min: "1", step: "1", value: minWords ?? '', onChange: (e) => {
300
- const value = e.target.value;
301
- if (value === '') {
302
- setMinWords(1);
303
- }
304
- else {
305
- const numValue = parseInt(value, 10);
306
- setMinWords(numValue < 1 ? 1 : numValue);
307
- }
308
- form.clearErrors('minWords');
309
- }, placeholder: "20", className: `mt-1 ${errors.minWords ? 'border-red-500 focus:ring-red-500' : ''}` }), errors.minWords && (_jsx("p", { className: "mt-1 text-xs text-red-600", children: errors.minWords }))] }), _jsx("span", { className: "mt-6 text-gray-400", children: "\u2014" }), _jsxs("div", { className: "flex-1", children: [_jsx(Label, { htmlFor: "maxWords", className: "text-xs text-gray-500", children: "T\u1ED1i \u0111a" }), _jsx(Input, { id: "maxWords", type: "number", min: "1", step: "1", value: maxWords ?? '', onChange: (e) => {
310
- const value = e.target.value;
311
- if (value === '') {
312
- setMaxWords(undefined);
313
- }
314
- else {
315
- const numValue = parseInt(value, 10);
316
- setMaxWords(numValue < 1 ? 1 : numValue);
317
- }
318
- form.clearErrors('maxWords');
319
- }, placeholder: "\u221E", className: `mt-1 ${errors.maxWords ? 'border-red-500 focus:ring-red-500' : ''}` }), errors.maxWords && (_jsx("p", { className: "mt-1 text-xs text-red-600", children: errors.maxWords }))] })] })] })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "instruction", className: "text-sm font-medium", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx("div", { className: `mt-1.5 ${errors.instruction ? 'rounded-md ring-1 ring-red-500' : ''}`, children: _jsx(RichTextEditor, { value: instruction, onChange: (html) => {
320
- setInstruction(html);
321
- form.clearErrors('instruction');
322
- }, minHeightClassName: "min-h-[120px]" }) }), errors.instruction && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: errors.instruction }))] }), _jsxs("div", { className: "space-y-3 rounded-lg border border-blue-100 bg-blue-50/30 p-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-blue-100", children: _jsx("span", { className: "text-xs font-semibold text-blue-600", children: "i" }) }), _jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "Th\u00F4ng tin c\u1EA7n bao g\u1ED3m" })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addInformation, className: "h-8 gap-1.5 border-blue-200 bg-white text-blue-600 hover:bg-blue-50 hover:text-blue-700", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm"] })] }), errors.informations && (_jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-3", children: _jsx("p", { className: "text-sm text-red-600", children: errors.informations }) })), _jsx("div", { className: "space-y-2", children: informations.map((info, index) => (_jsx("div", { className: "space-y-1", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 text-xs font-medium text-blue-600", children: index + 1 }), _jsx(Input, { value: info, onChange: (e) => updateInformation(index, e.target.value), placeholder: `Thông tin ${index + 1}...`, className: "flex-1 border-gray-200" }), informations.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeInformation(index), className: "h-8 w-8 flex-shrink-0 p-0 text-gray-400 hover:bg-red-50 hover:text-red-500", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }) }, index))) }), _jsx("p", { className: "text-xs text-gray-500 italic", children: "\uD83D\uDCA1 Li\u1EC7t k\u00EA c\u00E1c th\u00F4ng tin h\u1ECDc sinh c\u1EA7n \u0111\u1EC1 c\u1EADp trong b\u00E0i vi\u1EBFt" })] }), _jsxs("div", { className: "space-y-3 rounded-lg border border-violet-100 bg-violet-50/30 p-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-violet-100", children: _jsx(ImageIcon, { className: "h-3.5 w-3.5 text-violet-600" }) }), _jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "\u1EA2nh h\u01B0\u1EDBng d\u1EABn" }), _jsx("span", { className: "text-xs text-gray-400" })] }), guideImages.length > 0 && (_jsx("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4", children: guideImages.map((imageKey, index) => (_jsx(GuideImageItem, { imageKey: imageKey, index: index, localPreviewUrl: guideImagePreviewUrls[imageKey], onRemove: removeGuideImage }, `${imageKey}-${index}`))) })), _jsx(FileUpload, { label: "Th\u00EAm \u1EA3nh h\u01B0\u1EDBng d\u1EABn", accept: "image/*", value: "", onChange: handleGuideImageUpload, onPresignedUrlChange: handleGuideImagePresignedUrl, autoUpload: true, prefix: "write-short-letter/guide", placeholder: "Ch\u1ECDn ho\u1EB7c k\u00E9o th\u1EA3 \u1EA3nh v\u00E0o \u0111\u00E2y", icon: _jsx(ImageIcon, { className: "h-4 w-4" }) }), _jsx("p", { className: "text-xs text-gray-500 italic", children: "\uD83D\uDCF7 Upload nhi\u1EC1u \u1EA3nh \u0111\u1EC3 h\u01B0\u1EDBng d\u1EABn h\u1ECDc sinh vi\u1EBFt b\u00E0i (v\u00ED d\u1EE5: m\u1EABu th\u01B0, h\u00ECnh \u1EA3nh minh h\u1ECDa)" })] })] })] })] }));
298
+ return (_jsxs("div", { className: "space-y-3", children: [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(' · ') })] })), _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Mail, { className: "h-3.5 w-3.5" }), title: "Write a Short Letter", points: {
299
+ value: points,
300
+ onValueChange: setPoints,
301
+ error: errors.points,
302
+ }, preview: {
303
+ onClick: () => setIsClientMode(true),
304
+ disabled: !minWords || minWords < 1,
305
+ } }), children: [_jsxs("div", { className: "rounded-md border border-gray-200 bg-gray-50/50 p-3", children: [_jsx(Label, { className: "mb-3 block text-sm font-medium text-gray-700", children: "Gi\u1EDBi h\u1EA1n s\u1ED1 t\u1EEB" }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "flex-1", children: [_jsxs(Label, { htmlFor: "minWords", className: "text-xs text-gray-500", children: ["T\u1ED1i thi\u1EC3u ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "minWords", type: "number", min: "1", step: "1", value: minWords ?? '', onChange: (e) => {
306
+ const value = e.target.value;
307
+ if (value === '') {
308
+ setMinWords(1);
309
+ }
310
+ else {
311
+ const numValue = parseInt(value, 10);
312
+ setMinWords(numValue < 1 ? 1 : numValue);
313
+ }
314
+ form.clearErrors('minWords');
315
+ }, placeholder: "20", className: `mt-1 ${errors.minWords ? 'border-red-500 focus:ring-red-500' : ''}` }), errors.minWords && (_jsx("p", { className: "mt-1 text-xs text-red-600", children: errors.minWords }))] }), _jsx("span", { className: "mt-6 text-gray-400", children: "\u2014" }), _jsxs("div", { className: "flex-1", children: [_jsx(Label, { htmlFor: "maxWords", className: "text-xs text-gray-500", children: "T\u1ED1i \u0111a" }), _jsx(Input, { id: "maxWords", type: "number", min: "1", step: "1", value: maxWords ?? '', onChange: (e) => {
316
+ const value = e.target.value;
317
+ if (value === '') {
318
+ setMaxWords(undefined);
319
+ }
320
+ else {
321
+ const numValue = parseInt(value, 10);
322
+ setMaxWords(numValue < 1 ? 1 : numValue);
323
+ }
324
+ form.clearErrors('maxWords');
325
+ }, placeholder: "\u221E", className: `mt-1 ${errors.maxWords ? 'border-red-500 focus:ring-red-500' : ''}` }), errors.maxWords && (_jsx("p", { className: "mt-1 text-xs text-red-600", children: errors.maxWords }))] })] })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "instruction", className: "text-sm font-medium", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx("div", { className: `mt-1.5 ${errors.instruction ? 'rounded-md ring-1 ring-red-500' : ''}`, children: _jsx(RichTextEditor, { value: instruction, onChange: (html) => {
326
+ setInstruction(html);
327
+ form.clearErrors('instruction');
328
+ }, minHeightClassName: "min-h-[120px]" }) }), errors.instruction && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: errors.instruction }))] }), _jsxs("div", { className: "space-y-3 rounded-lg border border-blue-100 bg-blue-50/30 p-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-blue-100", children: _jsx("span", { className: "text-xs font-semibold text-blue-600", children: "i" }) }), _jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "Th\u00F4ng tin c\u1EA7n bao g\u1ED3m" })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addInformation, className: "h-8 gap-1.5 border-blue-200 bg-white text-blue-600 hover:bg-blue-50 hover:text-blue-700", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm"] })] }), errors.informations && (_jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-3", children: _jsx("p", { className: "text-sm text-red-600", children: errors.informations }) })), _jsx("div", { className: "space-y-2", children: informations.map((info, index) => (_jsx("div", { className: "space-y-1", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 text-xs font-medium text-blue-600", children: index + 1 }), _jsx(Input, { value: info, onChange: (e) => updateInformation(index, e.target.value), placeholder: `Thông tin ${index + 1}...`, className: "flex-1 border-gray-200" }), informations.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeInformation(index), className: "h-8 w-8 flex-shrink-0 p-0 text-gray-400 hover:bg-red-50 hover:text-red-500", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }) }, index))) }), _jsx("p", { className: "text-xs text-gray-500 italic", children: "\uD83D\uDCA1 Li\u1EC7t k\u00EA c\u00E1c th\u00F4ng tin h\u1ECDc sinh c\u1EA7n \u0111\u1EC1 c\u1EADp trong b\u00E0i vi\u1EBFt" })] }), _jsxs("div", { className: "space-y-3 rounded-lg border border-violet-100 bg-violet-50/30 p-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-violet-100", children: _jsx(ImageIcon, { className: "h-3.5 w-3.5 text-violet-600" }) }), _jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "\u1EA2nh h\u01B0\u1EDBng d\u1EABn" }), _jsx("span", { className: "text-xs text-gray-400" })] }), guideImages.length > 0 && (_jsx("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4", children: guideImages.map((imageKey, index) => (_jsx(GuideImageItem, { imageKey: imageKey, index: index, localPreviewUrl: guideImagePreviewUrls[imageKey], onRemove: removeGuideImage }, `${imageKey}-${index}`))) })), _jsx(FileUpload, { label: "Th\u00EAm \u1EA3nh h\u01B0\u1EDBng d\u1EABn", accept: "image/*", value: "", onChange: handleGuideImageUpload, onPresignedUrlChange: handleGuideImagePresignedUrl, autoUpload: true, prefix: "write-short-letter/guide", placeholder: "Ch\u1ECDn ho\u1EB7c k\u00E9o th\u1EA3 \u1EA3nh v\u00E0o \u0111\u00E2y", icon: _jsx(ImageIcon, { className: "h-4 w-4" }) }), _jsx("p", { className: "text-xs text-gray-500 italic", children: "\uD83D\uDCF7 Upload nhi\u1EC1u \u1EA3nh \u0111\u1EC3 h\u01B0\u1EDBng d\u1EABn h\u1ECDc sinh vi\u1EBFt b\u00E0i (v\u00ED d\u1EE5: m\u1EABu th\u01B0, h\u00ECnh \u1EA3nh minh h\u1ECDa)" })] })] })] }));
323
329
  }
324
330
  export function WriteAShortLetterCreator(props) {
325
331
  return _jsx(WriteAShortLetterCreatorContent, { ...props });
@@ -47,7 +47,7 @@ export function WriteCorrectVerbFormClient({ questionData, onSubmit, isReviewMod
47
47
  }) }));
48
48
  };
49
49
  // ============ RENDER ============
50
- return (_jsxs("div", { className: "space-y-5", children: [questionData.instruction && (_jsx("div", { className: "overflow-hidden rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 shadow-sm", children: _jsxs("div", { className: "flex items-center gap-3 px-5 py-4", children: [_jsx("div", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-blue-500 to-indigo-600 text-white shadow-sm", children: _jsx(FileText, { className: "h-5 w-5" }) }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-blue-900", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx("p", { className: "text-sm text-blue-700", children: questionData.instruction })] })] }) })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: cn('flex items-center justify-between border-b px-5 py-3', isReviewMode && isCorrect && 'border-green-200 bg-gradient-to-r from-green-50 to-emerald-50', isReviewMode && isWrong && 'border-red-200 bg-gradient-to-r from-red-50 to-rose-50', (!isReviewMode || (!isCorrect && !isWrong)) && 'border-gray-100 bg-gradient-to-r from-gray-50 to-white'), children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: cn('flex h-8 w-8 items-center justify-center rounded-lg text-white shadow-sm', isReviewMode && isCorrect && 'bg-gradient-to-br from-green-500 to-emerald-600', isReviewMode && isWrong && 'bg-gradient-to-br from-red-500 to-rose-600', (!isReviewMode || (!isCorrect && !isWrong)) && 'bg-gradient-to-br from-violet-500 to-purple-600'), children: _jsx(PenLine, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Chia \u0111\u1ED9ng t\u1EEB \u0111\u00FAng" })] }), isReviewMode && hasAnswered && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold', isCorrect ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'), children: [isCorrect ? _jsx(Check, { className: "h-3.5 w-3.5" }) : _jsx(X, { className: "h-3.5 w-3.5" }), isCorrect ? 'Đúng' : 'Sai'] }))] }), _jsxs("div", { className: "p-6 space-y-5", children: [_jsx("div", { className: "rounded-xl bg-gradient-to-br from-slate-50 to-gray-50 border border-gray-100 p-5", children: renderSentenceWithInput() }), isReviewMode && (_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: cn('rounded-xl border-2 p-4', isCorrect
50
+ return (_jsxs("div", { className: "space-y-3", children: [questionData.instruction && (_jsx("div", { className: "overflow-hidden rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 shadow-sm", children: _jsxs("div", { className: "flex items-center gap-2 px-3 py-2", children: [_jsx("div", { className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-md bg-gradient-to-br from-blue-500 to-indigo-600 text-white", children: _jsx(FileText, { className: "h-3.5 w-3.5" }) }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-blue-900", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx("p", { className: "text-sm text-blue-700", children: questionData.instruction })] })] }) })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: cn('flex items-center justify-between border-b px-3 py-2', isReviewMode && isCorrect && 'border-green-200 bg-gradient-to-r from-green-50 to-emerald-50', isReviewMode && isWrong && 'border-red-200 bg-gradient-to-r from-red-50 to-rose-50', (!isReviewMode || (!isCorrect && !isWrong)) && 'border-gray-100 bg-gradient-to-r from-gray-50 to-white'), children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: cn('flex h-7 w-7 items-center justify-center rounded-md text-white', isReviewMode && isCorrect && 'bg-gradient-to-br from-green-500 to-emerald-600', isReviewMode && isWrong && 'bg-gradient-to-br from-red-500 to-rose-600', (!isReviewMode || (!isCorrect && !isWrong)) && 'bg-gradient-to-br from-violet-500 to-purple-600'), children: _jsx(PenLine, { className: "h-3.5 w-3.5" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Chia \u0111\u1ED9ng t\u1EEB \u0111\u00FAng" })] }), isReviewMode && hasAnswered && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold', isCorrect ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'), children: [isCorrect ? _jsx(Check, { className: "h-3.5 w-3.5" }) : _jsx(X, { className: "h-3.5 w-3.5" }), isCorrect ? 'Đúng' : 'Sai'] }))] }), _jsxs("div", { className: "space-y-3 p-3", children: [_jsx("div", { className: "rounded-xl bg-gradient-to-br from-slate-50 to-gray-50 border border-gray-100 p-5", children: renderSentenceWithInput() }), isReviewMode && (_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: cn('rounded-xl border-2 p-4', isCorrect
51
51
  ? 'border-green-200 bg-green-50'
52
52
  : isWrong
53
53
  ? 'border-red-200 bg-red-50'
@@ -57,5 +57,5 @@ export function WriteCorrectVerbFormClient({ questionData, onSubmit, isReviewMod
57
57
  ? 'bg-gradient-to-br from-green-500 to-emerald-600'
58
58
  : 'bg-gradient-to-br from-red-500 to-rose-600'), children: isCorrect ? _jsx(Check, { className: "h-5 w-5" }) : _jsx(X, { className: "h-5 w-5" }) }), _jsxs("div", { children: [_jsx("p", { className: cn('text-sm font-semibold', isCorrect ? 'text-green-800' : 'text-red-800'), children: isCorrect ? 'Chính xác!' : 'Chưa đúng' }), _jsx("p", { className: cn('text-xs', isCorrect ? 'text-green-600' : 'text-red-600'), children: isCorrect
59
59
  ? 'Bạn đã chia động từ đúng.'
60
- : `Đáp án đúng là "${questionData.correctAnswer}".` })] })] }) }), questionData.explanation && (_jsx("div", { className: "rounded-xl bg-gradient-to-r from-blue-50 to-indigo-50 p-4 border border-blue-200", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-lg bg-blue-500 text-white", children: _jsx(Lightbulb, { className: "h-3.5 w-3.5" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("span", { className: "text-xs font-semibold text-blue-700 uppercase tracking-wider", children: "Gi\u1EA3i th\u00EDch" }), _jsx("p", { className: "mt-1 text-sm text-gray-700 leading-relaxed", children: questionData.explanation })] })] }) }))] })), !isReviewMode && !hasAnswered && (_jsx("div", { className: "rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 text-white shadow-sm", children: _jsx("span", { className: "text-sm font-bold", children: "!" }) }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-amber-800", children: "Nh\u1EADp d\u1EA1ng \u0111\u00FAng c\u1EE7a \u0111\u1ED9ng t\u1EEB" }), _jsx("p", { className: "text-xs text-amber-600", children: "S\u1EED d\u1EE5ng g\u1EE3i \u00FD trong ngo\u1EB7c \u0111\u1EC3 chia \u0111\u1ED9ng t\u1EEB ph\u00F9 h\u1EE3p." })] })] }) }))] })] }), !isReviewMode && questionData.caseSensitive && (_jsx("div", { className: "overflow-hidden rounded-xl border border-yellow-200 bg-gradient-to-r from-yellow-50 to-amber-50 shadow-sm", children: _jsxs("div", { className: "flex items-center gap-3 px-5 py-4", children: [_jsx("div", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-yellow-400 to-amber-500 text-white shadow-sm", children: _jsx(AlertTriangle, { className: "h-5 w-5" }) }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-yellow-900", children: "L\u01B0u \u00FD quan tr\u1ECDng" }), _jsx("p", { className: "text-sm text-yellow-700", children: "\u0110\u00E1p \u00E1n c\u00F3 ph\u00E2n bi\u1EC7t ch\u1EEF hoa/th\u01B0\u1EDDng" })] })] }) }))] }));
60
+ : `Đáp án đúng là "${questionData.correctAnswer}".` })] })] }) }), questionData.explanation && (_jsx("div", { className: "rounded-xl bg-gradient-to-r from-blue-50 to-indigo-50 p-4 border border-blue-200", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-lg bg-blue-500 text-white", children: _jsx(Lightbulb, { className: "h-3.5 w-3.5" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("span", { className: "text-xs font-semibold text-blue-700 uppercase tracking-wider", children: "Gi\u1EA3i th\u00EDch" }), _jsx("p", { className: "mt-1 text-sm text-gray-700 leading-relaxed", children: questionData.explanation })] })] }) }))] })), !isReviewMode && !hasAnswered && (_jsx("div", { className: "rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 text-white shadow-sm", children: _jsx("span", { className: "text-sm font-bold", children: "!" }) }), _jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium text-amber-800", children: "Nh\u1EADp d\u1EA1ng \u0111\u00FAng c\u1EE7a \u0111\u1ED9ng t\u1EEB" }), _jsx("p", { className: "text-xs text-amber-600", children: "S\u1EED d\u1EE5ng g\u1EE3i \u00FD trong ngo\u1EB7c \u0111\u1EC3 chia \u0111\u1ED9ng t\u1EEB ph\u00F9 h\u1EE3p." })] })] }) }))] })] }), !isReviewMode && questionData.caseSensitive && (_jsx("div", { className: "overflow-hidden rounded-xl border border-yellow-200 bg-gradient-to-r from-yellow-50 to-amber-50 shadow-sm", children: _jsxs("div", { className: "flex items-center gap-2 px-3 py-2", children: [_jsx("div", { className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-md bg-gradient-to-br from-yellow-400 to-amber-500 text-white", children: _jsx(AlertTriangle, { className: "h-3.5 w-3.5" }) }), _jsxs("div", { children: [_jsx("h3", { className: "text-sm font-semibold text-yellow-900", children: "L\u01B0u \u00FD quan tr\u1ECDng" }), _jsx("p", { className: "text-sm text-yellow-700", children: "\u0110\u00E1p \u00E1n c\u00F3 ph\u00E2n bi\u1EC7t ch\u1EEF hoa/th\u01B0\u1EDDng" })] })] }) }))] }));
61
61
  }