@tinyweb_dev/oe-exam-sdk 1.0.5 → 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
@@ -5,13 +5,12 @@ 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 { Textarea } from '../../../../components/ui/textarea';
8
- import { PointsInput } from '../../../../components/ui/points-input';
9
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
10
8
  import { Switch } from '../../../../components/ui/switch';
11
9
  import { FileUpload } from '../../../../components/ui/file-upload';
12
10
  import { RichTextEditor } from '../../../../components/shared/RichTextEditor';
13
11
  import { useDebouncedCallback } from '../../../../shared/lib/hooks';
14
- import { Eye, ImageIcon, Pencil, Plus, Star, Trash2, Type } from 'lucide-react';
12
+ import { BookOpen, HelpCircle, ImageIcon, Plus, Trash2, Type, Volume2 } from 'lucide-react';
13
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
15
14
  import { ChooseTheCorrectAnswerGroupClient } from './ChooseTheCorrectAnswerGroupClient';
16
15
  import { ChooseAnswerGroupImageUploadItem, ChooseAnswerGroupOptionImageField, } from './ChooseAnswerGroupImageUpload';
17
16
  import { fromChooseAnswerGroupImageUrls, getChooseAnswerGroupOptionImageSrc, isChooseAnswerGroupOptionFilled, toChooseAnswerGroupImageUrls, toEditableChooseAnswerGroupImageUrls, } from '../../_shared/types/choose-the-correct-answer-group.type';
@@ -59,13 +58,6 @@ function normalizeItems(rawItems) {
59
58
  function sumPoints(items) {
60
59
  return items.reduce((total, item) => (item.isExample ? total : total + (typeof item.points === 'number' ? item.points : 1)), 0);
61
60
  }
62
- function parsePointsValue(raw, allowZero) {
63
- const parsed = Number.parseInt(raw.replace(/[^0-9]/g, ''), 10);
64
- if (Number.isNaN(parsed)) {
65
- return allowZero ? 0 : 1;
66
- }
67
- return allowZero ? Math.max(0, parsed) : Math.max(1, parsed);
68
- }
69
61
  export function ChooseTheCorrectAnswerGroupCreator({ initialData, onChange, externalErrors, onUnsavedChangesChange, validationRef, }) {
70
62
  const [instruction, setInstruction] = useState(initialData?.instruction || 'Choose the correct answer.');
71
63
  const [passage, setPassage] = useState(initialData?.passage || '');
@@ -75,6 +67,13 @@ export function ChooseTheCorrectAnswerGroupCreator({ initialData, onChange, exte
75
67
  const [explanation, setExplanation] = useState(initialData?.explanation || '');
76
68
  const [isClientMode, setIsClientMode] = useState(false);
77
69
  const [errors, setErrors] = useState([]);
70
+ const [isGroupAudioVisible, setIsGroupAudioVisible] = useState(() => Boolean(initialData?.audioUrl));
71
+ const [isGroupImageVisible, setIsGroupImageVisible] = useState(() => toChooseAnswerGroupImageUrls(initialData?.imageUrl).length > 0);
72
+ const [isPassageVisible, setIsPassageVisible] = useState(() => {
73
+ const html = initialData?.passage;
74
+ return Boolean(html && html !== '<p></p>' && html !== '<p><br></p>' && html.trim());
75
+ });
76
+ const [visibleItemAudio, setVisibleItemAudio] = useState({});
78
77
  const onChangeRef = useRef(onChange);
79
78
  const previousPayloadRef = useRef('');
80
79
  useEffect(() => {
@@ -164,26 +163,43 @@ export function ChooseTheCorrectAnswerGroupCreator({ initialData, onChange, exte
164
163
  });
165
164
  };
166
165
  if (isClientMode) {
167
- return (_jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "flex justify-end", children: _jsxs(Button, { type: "button", variant: "outline", onClick: () => setIsClientMode(false), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), "Ch\u1EC9nh s\u1EEDa"] }) }), _jsx(ChooseTheCorrectAnswerGroupClient, { questionData: buildPayload(), isReviewMode: true })] }));
166
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(ChooseTheCorrectAnswerGroupClient, { questionData: buildPayload(), isReviewMode: true })] }));
168
167
  }
169
168
  const allErrors = [...errors, ...(externalErrors || [])];
170
- return (_jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "flex justify-end", children: _jsxs(Button, { type: "button", variant: "outline", onClick: () => setIsClientMode(true), children: [_jsx(Eye, { className: "mr-2 h-4 w-4" }), "Xem tr\u01B0\u1EDBc"] }) }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: "Nh\u00F3m ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng" }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { htmlFor: "group-instruction", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Input, { id: "group-instruction", value: instruction, onChange: (event) => setInstruction(event.target.value), placeholder: "Choose the correct answer." })] }), _jsx("div", { className: "space-y-2", children: _jsx(FileUpload, { id: "choose-answer-group-audio", label: "Audio chung (tu\u1EF3 ch\u1ECDn)", accept: "audio/*", value: audioUrl, onChange: setAudioUrl, maxSize: 20, placeholder: "Upload file audio ho\u1EB7c paste URL", autoUpload: true, prefix: "questions", showPlayButton: true }) }), _jsx("div", { className: "space-y-2", children: _jsx(FileUpload, { id: "choose-answer-group-image", label: "H\u00ECnh \u1EA3nh chung (tu\u1EF3 ch\u1ECDn)", accept: "image/*", value: imageUrl, onChange: setImageUrl, maxSize: 5, placeholder: "Upload \u1EA3nh nh\u00F3m ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" }) }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "N\u1ED9i dung b\u00E0i \u0111\u1ECDc (Passage - Tu\u1EF3 ch\u1ECDn)" }), _jsx(RichTextEditor, { value: passage, onChange: setPassage, minHeightClassName: "min-h-[160px]" })] }), _jsxs("div", { className: "rounded-xl border border-gray-200 bg-gray-50 p-4", children: [_jsx(PointsInput, { value: sumPoints(items), allowZero: true, readOnly: true }), _jsx("p", { className: "mt-2 text-xs text-gray-500", children: "T\u1ED5ng \u0111i\u1EC3m \u0111\u01B0\u1EE3c c\u1ED9ng t\u1EEB c\u00E1c c\u00E2u kh\u00F4ng ph\u1EA3i example." })] })] })] }), items.map((item, itemIndex) => (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between space-y-0", children: [_jsxs(CardTitle, { className: "text-base", children: ["C\u00E2u ", itemIndex + 1] }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeItem(itemIndex), disabled: items.length <= 1, children: _jsx(Trash2, { className: "h-4 w-4" }) })] }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between rounded-lg border border-amber-100 bg-amber-50 px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2 text-sm font-medium text-amber-800", children: [_jsx(Star, { className: "h-4 w-4" }), "C\u00E2u v\u00ED d\u1EE5"] }), _jsx(Switch, { checked: item.isExample === true, onCheckedChange: (checked) => updateItem(itemIndex, {
171
- isExample: checked,
172
- points: checked ? 0 : (item.points || 1),
173
- }) })] }), _jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: "C\u00E2u h\u1ECFi" }), _jsx(Textarea, { value: item.question, onChange: (event) => updateItem(itemIndex, { question: event.target.value }), rows: 2, placeholder: "A place for someone to stay or live is called" })] }), _jsx("div", { className: "space-y-2", children: _jsx(FileUpload, { id: `choose-answer-item-audio-${itemIndex}`, label: "Audio c\u00E2u h\u1ECFi (tu\u1EF3 ch\u1ECDn)", accept: "audio/*", value: item.audioUrl || '', onChange: (url) => updateItem(itemIndex, { audioUrl: url }), maxSize: 20, placeholder: "Upload file audio ho\u1EB7c paste URL", autoUpload: true, prefix: "questions", showPlayButton: true }) }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs(Label, { className: "flex items-center gap-2", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-teal-500" }), "H\u00ECnh \u1EA3nh (tu\u1EF3 ch\u1ECDn)"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => {
169
+ return (_jsxs("div", { className: "space-y-3", children: [_jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(HelpCircle, { className: "h-3.5 w-3.5" }), title: "Nh\u00F3m ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng", points: {
170
+ value: sumPoints(items),
171
+ onValueChange: () => undefined,
172
+ allowZero: true,
173
+ }, preview: { onClick: () => setIsClientMode(true) } }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(Label, { htmlFor: "group-instruction", className: "text-sm font-semibold text-gray-800", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", title: "Th\u00EAm audio chung", onClick: () => setIsGroupAudioVisible(true), className: `inline-flex h-8 w-8 items-center justify-center rounded-md transition-colors ${audioUrl || isGroupAudioVisible
174
+ ? 'bg-violet-600 text-white shadow-sm'
175
+ : 'bg-violet-50 text-violet-600 hover:bg-violet-100'}`, children: _jsx(Volume2, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", title: "Th\u00EAm h\u00ECnh \u1EA3nh chung", onClick: () => setIsGroupImageVisible(true), className: `inline-flex h-8 w-8 items-center justify-center rounded-md transition-colors ${imageUrl || isGroupImageVisible
176
+ ? 'bg-indigo-600 text-white shadow-sm'
177
+ : 'bg-indigo-50 text-indigo-600 hover:bg-indigo-100'}`, children: _jsx(ImageIcon, { className: "h-4 w-4" }) })] })] }), _jsx(Input, { id: "group-instruction", value: instruction, onChange: (event) => setInstruction(event.target.value), placeholder: "Choose the correct answer.", className: "h-8 border-gray-200" })] }), isGroupAudioVisible && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-gray-200 bg-white p-1.5", children: [_jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-indigo-50", children: _jsx(Volume2, { className: "h-3.5 w-3.5 text-indigo-500" }) }), _jsx("div", { className: "min-w-0 flex-1", children: _jsx(FileUpload, { id: "choose-answer-group-audio", label: "", accept: "audio/*", value: audioUrl, onChange: setAudioUrl, maxSize: 20, placeholder: "Upload file audio ho\u1EB7c paste URL", autoUpload: true, prefix: "questions", showPlayButton: true }) })] })), isGroupImageVisible && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-gray-200 bg-white p-1.5", children: [_jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-indigo-50", children: _jsx(ImageIcon, { className: "h-3.5 w-3.5 text-indigo-500" }) }), _jsx("div", { className: "min-w-0 flex-1", children: _jsx(FileUpload, { id: "choose-answer-group-image", label: "", accept: "image/*", value: imageUrl, onChange: setImageUrl, maxSize: 5, placeholder: "Upload \u1EA3nh nh\u00F3m ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" }) })] })), !isPassageVisible ? (_jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setIsPassageVisible(true), className: "h-8 gap-1.5 border-gray-200 px-2.5 text-xs text-gray-600 hover:bg-gray-50", children: [_jsx(BookOpen, { className: "h-3.5 w-3.5" }), "Th\u00EAm n\u1ED9i dung b\u00E0i \u0111\u1ECDc"] })) : (_jsxs("div", { className: "relative space-y-1.5 pr-10", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setIsPassageVisible(false), className: "absolute right-0 top-0 z-10 h-7 px-2 text-xs text-gray-500 hover:text-gray-700", children: "\u1EA8n" }), _jsxs(Label, { className: "text-sm font-semibold text-gray-800", children: ["N\u1ED9i dung b\u00E0i \u0111\u1ECDc ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(tu\u1EF3 ch\u1ECDn)" })] }), _jsx(RichTextEditor, { value: passage, onChange: setPassage, variant: "compact", minHeightClassName: "min-h-[72px]" })] }))] }), items.map((item, itemIndex) => (_jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(HelpCircle, { className: "h-3.5 w-3.5" }), title: `Câu ${itemIndex + 1}`, points: !item.isExample ? {
178
+ value: item.points,
179
+ allowZero: true,
180
+ onValueChange: (value) => updateItem(itemIndex, { points: value }),
181
+ } : undefined, example: {
182
+ checked: item.isExample === true,
183
+ onChange: (checked) => updateItem(itemIndex, {
184
+ isExample: checked,
185
+ points: checked ? 0 : (item.points || 1),
186
+ }),
187
+ }, extraActions: _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeItem(itemIndex), disabled: items.length <= 1, className: "h-8 w-8 text-gray-400 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }) }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "C\u00E2u h\u1ECFi" }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", title: "Th\u00EAm audio", onClick: () => setVisibleItemAudio((prev) => ({ ...prev, [itemIndex]: true })), className: `inline-flex h-8 w-8 items-center justify-center rounded-md transition-colors ${item.audioUrl || visibleItemAudio[itemIndex]
188
+ ? 'bg-violet-600 text-white shadow-sm'
189
+ : 'bg-violet-50 text-violet-600 hover:bg-violet-100'}`, children: _jsx(Volume2, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", title: "Th\u00EAm h\u00ECnh \u1EA3nh", onClick: () => {
174
190
  const nextUrls = [...toEditableChooseAnswerGroupImageUrls(item.imageUrl), ''];
175
191
  updateItem(itemIndex, { imageUrl: nextUrls });
176
- }, className: "gap-1.5 border-teal-200 text-teal-600 hover:bg-teal-50 hover:text-teal-700", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm h\u00ECnh \u1EA3nh"] })] }), toEditableChooseAnswerGroupImageUrls(item.imageUrl).length === 0 ? (_jsx("div", { className: "rounded-lg border border-dashed border-gray-200 bg-gray-50/50 p-3 text-xs text-gray-500", children: "Ch\u01B0a c\u00F3 h\u00ECnh \u1EA3nh. Nh\u1EA5n \"Th\u00EAm h\u00ECnh \u1EA3nh\" \u0111\u1EC3 upload \u1EA3nh minh ho\u1EA1 cho c\u00E2u h\u1ECFi." })) : (_jsx("div", { className: "space-y-3", children: toEditableChooseAnswerGroupImageUrls(item.imageUrl).map((imageUrl, imageIndex) => (_jsx(ChooseAnswerGroupImageUploadItem, { index: imageIndex, itemIndex: itemIndex, imageUrl: imageUrl, onChange: (url) => {
177
- const nextUrls = toEditableChooseAnswerGroupImageUrls(item.imageUrl);
178
- nextUrls[imageIndex] = url;
179
- updateItem(itemIndex, { imageUrl: nextUrls });
180
- }, onRemove: () => {
181
- const nextUrls = toEditableChooseAnswerGroupImageUrls(item.imageUrl)
182
- .filter((_, idx) => idx !== imageIndex);
183
- updateItem(itemIndex, { imageUrl: fromChooseAnswerGroupImageUrls(nextUrls) });
184
- } }, `${itemIndex}-${imageIndex}`))) }))] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { children: "\u0110\u00E1p \u00E1n" }), _jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-3 py-1.5", children: [_jsx(Type, { className: `h-3.5 w-3.5 ${item.optionType === 'text' ? 'text-indigo-600' : 'text-gray-400'}` }), _jsx("span", { className: `text-xs font-medium ${item.optionType === 'text' ? 'text-indigo-600' : 'text-gray-400'}`, children: "Text" }), _jsx(Switch, { checked: item.optionType === 'image', onCheckedChange: (checked) => updateItem(itemIndex, {
185
- optionType: checked ? 'image' : 'text',
186
- }) }), _jsx(ImageIcon, { className: `h-3.5 w-3.5 ${item.optionType === 'image' ? 'text-indigo-600' : 'text-gray-400'}` }), _jsx("span", { className: `text-xs font-medium ${item.optionType === 'image' ? 'text-indigo-600' : 'text-gray-400'}`, children: "\u1EA2nh" })] })] }), item.options.map((option, optionIndex) => (_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("input", { type: "radio", name: `correct-${itemIndex}`, checked: item.correctAnswer === option.id, onChange: () => updateItem(itemIndex, { correctAnswer: option.id }), className: "mt-2" }), _jsx("span", { className: "mt-2 w-6 text-sm font-semibold text-gray-600", children: OPTION_LABELS[optionIndex] || optionIndex + 1 }), item.optionType === 'image' ? (_jsx(ChooseAnswerGroupOptionImageField, { itemIndex: itemIndex, optionIndex: optionIndex, imageUrl: getChooseAnswerGroupOptionImageSrc(option), onChange: (url) => updateOption(itemIndex, optionIndex, { imageUrl: url, text: '' }) })) : (_jsx(Input, { value: option.text, onChange: (event) => updateOption(itemIndex, optionIndex, { text: event.target.value }), placeholder: `Đáp án ${OPTION_LABELS[optionIndex]}` }))] }, option.id)))] }), !item.isExample && (_jsx(PointsInput, { value: item.points, allowZero: true, onChange: (event) => updateItem(itemIndex, {
187
- points: parsePointsValue(event.target.value, true),
188
- }) }))] })] }, `item-${itemIndex}`))), _jsxs(Button, { type: "button", variant: "outline", onClick: addItem, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), "Th\u00EAm c\u00E2u h\u1ECFi"] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Gi\u1EA3i th\u00EDch" }) }), _jsx(CardContent, { children: _jsx(Textarea, { id: "explanation", value: explanation, onChange: (event) => setExplanation(event.target.value), placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0... v\u00EC...", rows: 3 }) })] }), allErrors.length > 0 && (_jsx("div", { className: "rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-700", children: allErrors.map((error) => (_jsx("p", { children: error }, error))) }))] }));
192
+ }, className: `inline-flex h-8 w-8 items-center justify-center rounded-md transition-colors ${toEditableChooseAnswerGroupImageUrls(item.imageUrl).length > 0
193
+ ? 'bg-indigo-600 text-white shadow-sm'
194
+ : 'bg-indigo-50 text-indigo-600 hover:bg-indigo-100'}`, children: _jsx(ImageIcon, { className: "h-4 w-4" }) })] })] }), _jsx(Textarea, { value: item.question, onChange: (event) => updateItem(itemIndex, { question: event.target.value }), rows: 2, placeholder: "A place for someone to stay or live is called", className: "min-h-[56px] border-gray-200" })] }), (item.audioUrl || visibleItemAudio[itemIndex]) && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-gray-200 bg-white p-1.5", children: [_jsx("div", { className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-indigo-50", children: _jsx(Volume2, { className: "h-3.5 w-3.5 text-indigo-500" }) }), _jsx("div", { className: "min-w-0 flex-1", children: _jsx(FileUpload, { id: `choose-answer-item-audio-${itemIndex}`, label: "", accept: "audio/*", value: item.audioUrl || '', onChange: (url) => updateItem(itemIndex, { audioUrl: url }), maxSize: 20, placeholder: "Upload file audio ho\u1EB7c paste URL", autoUpload: true, prefix: "questions", showPlayButton: true }) })] })), toEditableChooseAnswerGroupImageUrls(item.imageUrl).length > 0 && (_jsx("div", { className: "space-y-2", children: toEditableChooseAnswerGroupImageUrls(item.imageUrl).map((imageUrl, imageIndex) => (_jsx(ChooseAnswerGroupImageUploadItem, { index: imageIndex, itemIndex: itemIndex, imageUrl: imageUrl, onChange: (url) => {
195
+ const nextUrls = toEditableChooseAnswerGroupImageUrls(item.imageUrl);
196
+ nextUrls[imageIndex] = url;
197
+ updateItem(itemIndex, { imageUrl: nextUrls });
198
+ }, onRemove: () => {
199
+ const nextUrls = toEditableChooseAnswerGroupImageUrls(item.imageUrl)
200
+ .filter((_, idx) => idx !== imageIndex);
201
+ updateItem(itemIndex, { imageUrl: fromChooseAnswerGroupImageUrls(nextUrls) });
202
+ } }, `${itemIndex}-${imageIndex}`))) })), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "\u0110\u00E1p \u00E1n" }), _jsxs("div", { className: "flex h-8 items-center gap-1.5 rounded-md border border-gray-200 bg-gray-50 px-2.5", children: [_jsx(Type, { className: `h-3.5 w-3.5 ${item.optionType === 'text' ? 'text-indigo-600' : 'text-gray-400'}` }), _jsx("span", { className: `text-xs font-medium ${item.optionType === 'text' ? 'text-indigo-600' : 'text-gray-400'}`, children: "Text" }), _jsx(Switch, { checked: item.optionType === 'image', onCheckedChange: (checked) => updateItem(itemIndex, {
203
+ optionType: checked ? 'image' : 'text',
204
+ }) }), _jsx(ImageIcon, { className: `h-3.5 w-3.5 ${item.optionType === 'image' ? 'text-indigo-600' : 'text-gray-400'}` }), _jsx("span", { className: `text-xs font-medium ${item.optionType === 'image' ? 'text-indigo-600' : 'text-gray-400'}`, children: "\u1EA2nh" })] })] }), item.options.map((option, optionIndex) => (_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("input", { type: "radio", name: `correct-${itemIndex}`, checked: item.correctAnswer === option.id, onChange: () => updateItem(itemIndex, { correctAnswer: option.id }), className: "mt-2" }), _jsx("span", { className: "mt-2 w-6 text-sm font-semibold text-gray-600", children: OPTION_LABELS[optionIndex] || optionIndex + 1 }), item.optionType === 'image' ? (_jsx(ChooseAnswerGroupOptionImageField, { itemIndex: itemIndex, optionIndex: optionIndex, imageUrl: getChooseAnswerGroupOptionImageSrc(option), onChange: (url) => updateOption(itemIndex, optionIndex, { imageUrl: url, text: '' }) })) : (_jsx(Input, { value: option.text, onChange: (event) => updateOption(itemIndex, optionIndex, { text: event.target.value }), placeholder: `Đáp án ${OPTION_LABELS[optionIndex]}`, className: "h-8 border-gray-200" }))] }, option.id)))] })] }, `item-${itemIndex}`))), _jsxs(Button, { type: "button", variant: "outline", onClick: addItem, className: "h-8 gap-1.5 border-dashed 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 c\u00E2u h\u1ECFi"] }), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) }), allErrors.length > 0 && (_jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-2 text-xs text-red-700", children: allErrors.map((error) => (_jsx("p", { children: error }, error))) }))] }));
189
205
  }
@@ -23,12 +23,12 @@ export function ChooseThenAnswerGroupClient({ questionData, isReviewMode = false
23
23
  text: next.text ?? current.text,
24
24
  });
25
25
  };
26
- return (_jsxs("div", { className: "space-y-2 rounded-xl border border-gray-200 bg-white p-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-slate-800 text-xs font-bold text-white", children: 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-[10px] font-semibold uppercase text-amber-800", children: [_jsx(Star, { className: "h-3 w-3" }), "Example"] }))] }), _jsxs("p", { className: "flex flex-wrap items-center gap-1.5 text-[15px] leading-relaxed text-slate-800", children: [_jsx("span", { children: parts[0] || '' }), _jsx("span", { className: "inline-flex items-center gap-1", children: item.options.map((option, optionIndex) => {
27
- const selected = current.optionId === option.id;
28
- const showReview = isReviewMode && !item.isExample && Boolean(expectedId);
29
- const isCorrect = showReview && option.id === expectedId;
30
- const isWrong = showReview && selected && option.id !== expectedId;
31
- return (_jsxs("span", { className: "inline-flex items-center gap-1", children: [optionIndex > 0 && _jsx("span", { className: "text-slate-400", children: "|" }), _jsx("button", { type: "button", disabled: !canEdit || item.isExample, onClick: () => setAnswer({ optionId: option.id }), className: cn('rounded-md border px-2.5 py-1 text-sm transition-colors', selected && !isCorrect && !isWrong && 'border-black bg-black text-white', !selected && !isCorrect && !isWrong && 'border-gray-300 bg-white text-gray-900', isCorrect && 'border-emerald-500 bg-emerald-50 text-emerald-900', isWrong && 'border-red-500 bg-red-50 text-red-900', (!canEdit || item.isExample) && 'cursor-default'), children: option.text || option.id })] }, option.id));
32
- }) }), _jsx("span", { children: parts.slice(1).join('{0}') })] }), _jsx("textarea", { value: current.text, disabled: !canEdit || item.isExample, onChange: (event) => setAnswer({ text: event.target.value }), placeholder: "Answer for you", rows: 3, className: "w-full rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm text-slate-800 outline-none focus:border-slate-400 disabled:bg-slate-50" })] }, `${item.questionNumber}-${itemIndex}`));
26
+ return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-gray-100 px-3 py-2", children: [_jsx("span", { className: "flex h-6 w-6 items-center justify-center rounded-full bg-slate-800 text-xs font-bold text-white", children: 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-[10px] font-semibold uppercase text-amber-800", children: [_jsx(Star, { className: "h-3 w-3" }), "Example"] }))] }), _jsxs("div", { className: "space-y-3 p-3", children: [_jsxs("p", { className: "flex flex-wrap items-center gap-1.5 text-[15px] leading-relaxed text-slate-800", children: [_jsx("span", { children: parts[0] || '' }), _jsx("span", { className: "inline-flex items-center gap-1", children: item.options.map((option, optionIndex) => {
27
+ const selected = current.optionId === option.id;
28
+ const showReview = isReviewMode && !item.isExample && Boolean(expectedId);
29
+ const isCorrect = showReview && option.id === expectedId;
30
+ const isWrong = showReview && selected && option.id !== expectedId;
31
+ return (_jsxs("span", { className: "inline-flex items-center gap-1", children: [optionIndex > 0 && _jsx("span", { className: "text-slate-400", children: "|" }), _jsx("button", { type: "button", disabled: !canEdit || item.isExample, onClick: () => setAnswer({ optionId: option.id }), className: cn('rounded-md border px-2.5 py-1 text-sm transition-colors', selected && !isCorrect && !isWrong && 'border-black bg-black text-white', !selected && !isCorrect && !isWrong && 'border-gray-300 bg-white text-gray-900', isCorrect && 'border-emerald-500 bg-emerald-50 text-emerald-900', isWrong && 'border-red-500 bg-red-50 text-red-900', (!canEdit || item.isExample) && 'cursor-default'), children: option.text || option.id })] }, option.id));
32
+ }) }), _jsx("span", { children: parts.slice(1).join('{0}') })] }), _jsx("textarea", { value: current.text, disabled: !canEdit || item.isExample, onChange: (event) => setAnswer({ text: event.target.value }), placeholder: "Answer for you", rows: 3, className: "w-full rounded-lg border border-slate-200 bg-white px-3 py-2 text-sm text-slate-800 outline-none focus:border-slate-400 disabled:bg-slate-50" })] })] }, `${item.questionNumber}-${itemIndex}`));
33
33
  }), 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] }))] }));
34
34
  }
@@ -1,15 +1,15 @@
1
1
  'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useEffect, useRef, useState } from 'react';
4
4
  import { Button } from '../../../../components/ui/button';
5
5
  import { Label } from '../../../../components/ui/label';
6
6
  import { Input } from '../../../../components/ui/input';
7
7
  import { PointsInput } from '../../../../components/ui/points-input';
8
- import { Textarea } from '../../../../components/ui/textarea';
9
8
  import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
10
9
  import { Switch } from '../../../../components/ui/switch';
11
10
  import { useDebouncedCallback } from '../../../../shared/lib/hooks';
12
- import { Eye, Pencil, Plus, Star, Trash2 } from 'lucide-react';
11
+ import { ListChecks, Plus, Star, Trash2 } from 'lucide-react';
12
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
13
13
  import { ChooseThenAnswerGroupClient } from './ChooseThenAnswerGroupClient';
14
14
  import { createEmptyItem, createEmptyOption, sumGradablePoints, } from './map-choose-then-answer-group-data';
15
15
  import { CHOOSE_THEN_ANSWER_GROUP_DEFAULT_INSTRUCTION } from '../../_shared/types/choose-then-answer-group.type';
@@ -168,7 +168,10 @@ export function ChooseThenAnswerGroupCreator({ initialData, onChange, externalEr
168
168
  })));
169
169
  };
170
170
  const displayErrors = [...errors, ...(externalErrors || [])];
171
- return (_jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "flex justify-end", children: _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setIsClientMode((v) => !v), children: [isClientMode ? _jsx(Pencil, { className: "mr-2 h-4 w-4" }) : _jsx(Eye, { className: "mr-2 h-4 w-4" }), isClientMode ? 'Sửa' : 'Xem trước'] }) }), isClientMode ? (_jsx(ChooseThenAnswerGroupClient, { questionData: buildPayload(), isReviewMode: true })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: "cta-title", children: "Ti\u00EAu \u0111\u1EC1 (optional)" }), _jsx(Input, { id: "cta-title", value: title, onChange: (event) => setTitle(event.target.value), placeholder: "GRAMMAR" })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "cta-instruction", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Input, { id: "cta-instruction", value: instruction, onChange: (event) => setInstruction(event.target.value), placeholder: CHOOSE_THEN_ANSWER_GROUP_DEFAULT_INSTRUCTION })] })] }), items.map((item, index) => (_jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between space-y-0", children: [_jsxs(CardTitle, { className: "text-base", children: ["C\u00E2u ", index + 1] }), items.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeItem(index), children: _jsx(Trash2, { className: "h-4 w-4 text-red-500" }) }))] }), _jsxs(CardContent, { className: "space-y-3", children: [_jsx(Input, { value: item.question, onChange: (event) => updateItem(index, { question: event.target.value }), placeholder: "Did you eat {0} for lunch today?" }), _jsxs("p", { className: "text-xs text-slate-500", children: ["D\u00F9ng ", '{0}', " cho ch\u1ED7 hi\u1EC7n chip ch\u1ECDn."] }), item.options.map((option, optionIndex) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { value: option.text, onChange: (event) => updateOption(index, optionIndex, event.target.value), placeholder: `Lựa chọn ${optionIndex + 1}` }), item.options.length > 2 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeOption(index, optionIndex), children: _jsx(Trash2, { className: "h-4 w-4 text-red-500" }) }))] }, option.id))), _jsxs(Button, { type: "button", size: "sm", variant: "outline", onClick: () => addOption(index), children: [_jsx(Plus, { className: "mr-1 h-4 w-4" }), " Th\u00EAm l\u1EF1a ch\u1ECDn"] }), _jsxs("div", { className: "flex flex-wrap items-center gap-3", children: [_jsx("select", { className: "h-10 rounded-md border border-input bg-background px-3 text-sm", value: item.optionId, onChange: (event) => updateItem(index, { optionId: event.target.value }), children: item.options.map((option) => (_jsx("option", { value: option.id, children: option.text || option.id }, option.id))) }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Switch, { id: `cta-example-${index}`, checked: item.isExample === true, onCheckedChange: (checked) => updateItem(index, { isExample: checked }) }), _jsxs(Label, { htmlFor: `cta-example-${index}`, className: "flex cursor-pointer items-center gap-1 text-xs", children: [_jsx(Star, { className: "h-3 w-3 text-amber-500" }), "Example"] })] }), !item.isExample && (_jsx(PointsInput, { value: item.points, allowZero: true, onChange: (event) => updateItem(index, {
171
+ if (isClientMode) {
172
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(ChooseThenAnswerGroupClient, { questionData: buildPayload(), isReviewMode: true })] }));
173
+ }
174
+ return (_jsxs("div", { className: "space-y-3", children: [displayErrors.length > 0 && (_jsx("ul", { className: "list-disc space-y-1 rounded-md border border-red-200 bg-red-50 p-2 pl-6 text-xs text-red-700", children: displayErrors.map((error) => (_jsx("li", { children: error }, error))) })), _jsx(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(ListChecks, { className: "h-3.5 w-3.5" }), title: "Ch\u1ECDn r\u1ED3i tr\u1EA3 l\u1EDDi (Nh\u00F3m)", preview: { onClick: () => setIsClientMode(true) } }), children: _jsxs("div", { className: "grid gap-2.5 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "cta-title", className: "text-sm font-semibold text-gray-800", children: "Ti\u00EAu \u0111\u1EC1 (optional)" }), _jsx(Input, { id: "cta-title", value: title, onChange: (event) => setTitle(event.target.value), placeholder: "GRAMMAR", className: "h-10 border-gray-200 bg-white" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "cta-instruction", className: "text-sm font-semibold text-gray-800", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Input, { id: "cta-instruction", value: instruction, onChange: (event) => setInstruction(event.target.value), placeholder: CHOOSE_THEN_ANSWER_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: ["Danh s\u00E1ch c\u00E2u (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addItem, className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), " Th\u00EAm c\u00E2u"] })] }) }), _jsxs(CardContent, { className: "space-y-2.5 px-3 pb-3", children: [items.map((item, index) => (_jsxs("div", { className: "space-y-2.5 rounded-md border border-gray-200 bg-white px-3 py-2.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", index + 1] }), items.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeItem(index), className: "h-7 w-7 text-red-500 hover:bg-red-50", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }), _jsx(Input, { value: item.question, onChange: (event) => updateItem(index, { question: event.target.value }), placeholder: "Did you eat {0} for lunch today?", className: "h-10 border-gray-200 bg-white" }), _jsxs("p", { className: "text-xs text-slate-500", children: ["D\u00F9ng ", '{0}', " cho ch\u1ED7 hi\u1EC7n chip ch\u1ECDn."] }), item.options.map((option, optionIndex) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { value: option.text, onChange: (event) => updateOption(index, optionIndex, event.target.value), placeholder: `Lựa chọn ${optionIndex + 1}`, className: "h-10 border-gray-200 bg-white" }), item.options.length > 2 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeOption(index, optionIndex), className: "h-7 w-7 text-red-500 hover:bg-red-50", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }, option.id))), _jsxs(Button, { type: "button", size: "sm", variant: "outline", onClick: () => addOption(index), className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), " Th\u00EAm l\u1EF1a ch\u1ECDn"] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("select", { className: "h-10 rounded-md border border-input bg-background px-3 text-sm", value: item.optionId, onChange: (event) => updateItem(index, { optionId: event.target.value }), children: item.options.map((option) => (_jsx("option", { value: option.id, children: option.text || option.id }, option.id))) }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Switch, { id: `cta-example-${index}`, checked: item.isExample === true, onCheckedChange: (checked) => updateItem(index, { isExample: checked }) }), _jsxs(Label, { htmlFor: `cta-example-${index}`, className: "flex cursor-pointer items-center gap-1 text-xs", children: [_jsx(Star, { className: "h-3 w-3 text-amber-500" }), "Example"] })] }), !item.isExample && (_jsx(PointsInput, { value: item.points, allowZero: true, onChange: (event) => updateItem(index, {
172
175
  points: Number.parseInt(event.target.value, 10) || 1,
173
- }) }))] })] })] }, `${item.questionNumber}-${index}`))), _jsxs(Button, { type: "button", variant: "outline", onClick: addItem, children: [_jsx(Plus, { className: "mr-1 h-4 w-4" }), " Th\u00EAm c\u00E2u"] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "cta-explanation", children: "Gi\u1EA3i th\u00EDch" }), _jsx(Textarea, { id: "cta-explanation", value: explanation, onChange: (event) => setExplanation(event.target.value) })] })] })), displayErrors.length > 0 && (_jsx("ul", { className: "list-disc space-y-1 rounded-md border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700", children: displayErrors.map((error) => (_jsx("li", { children: error }, error))) }))] }));
176
+ }) }))] })] }, `${item.questionNumber}-${index}`))), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] })] }));
174
177
  }
@@ -20,8 +20,8 @@ export function CrossOutWordGroupClient({ questionData, isReviewMode = false, us
20
20
  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) => {
21
21
  const userValue = userAnswers[itemIndex];
22
22
  const isReplace = item.type === 'CROSS_OUT_AND_REPLACE';
23
- return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-4 py-2.5", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 text-white", children: _jsx(CircleHelp, { className: "h-3.5 w-3.5" }) }), _jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", item.questionNumber || itemIndex + 1] }), item.isExample && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-0.5 text-xs font-semibold text-amber-700", children: [_jsx(Star, { className: "h-3 w-3" }), "Example"] })), _jsx("span", { className: cn('rounded-full px-2 py-0.5 text-xs font-semibold', isReplace
23
+ return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 text-white", children: _jsx(CircleHelp, { className: "h-3.5 w-3.5" }) }), _jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", item.questionNumber || itemIndex + 1] }), item.isExample && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-0.5 text-xs font-semibold text-amber-700", children: [_jsx(Star, { className: "h-3 w-3" }), "Example"] })), _jsx("span", { className: cn('rounded-full px-2 py-0.5 text-xs font-semibold', isReplace
24
24
  ? 'bg-violet-100 text-violet-700'
25
- : 'bg-sky-100 text-sky-700'), children: isReplace ? 'Gạch + thay' : 'Chỉ gạch' })] }), _jsxs("span", { className: "text-xs text-gray-500", children: [item.points || 0, " \u0111i\u1EC3m"] })] }), _jsxs("div", { className: "space-y-3 p-4", children: [_jsx("p", { className: "text-sm font-semibold leading-relaxed text-gray-800", children: item.question || 'Câu hỏi chưa được nhập' }), _jsxs("div", { className: "rounded-lg border border-gray-100 bg-gray-50 px-3 py-2 text-sm text-gray-700", children: [_jsx("span", { className: "font-medium text-gray-500", children: "\u0110\u00E1p \u00E1n: " }), formatAnswer(item.correctAnswer)] }), isReviewMode && (_jsxs("div", { className: "rounded-lg border border-blue-100 bg-blue-50 px-3 py-2 text-sm text-blue-800", children: [_jsx("span", { className: "font-medium", children: "H\u1ECDc sinh: " }), formatAnswer(userValue)] }))] })] }, `${item.questionNumber}-${itemIndex}`));
25
+ : 'bg-sky-100 text-sky-700'), children: isReplace ? 'Gạch + thay' : 'Chỉ gạch' })] }), _jsxs("span", { className: "text-xs text-gray-500", children: [item.points || 0, " \u0111i\u1EC3m"] })] }), _jsxs("div", { className: "space-y-3 p-3", children: [_jsx("p", { className: "text-sm font-semibold leading-relaxed text-gray-800", children: item.question || 'Câu hỏi chưa được nhập' }), _jsxs("div", { className: "rounded-lg border border-gray-100 bg-gray-50 px-3 py-2 text-sm text-gray-700", children: [_jsx("span", { className: "font-medium text-gray-500", children: "\u0110\u00E1p \u00E1n: " }), formatAnswer(item.correctAnswer)] }), isReviewMode && (_jsxs("div", { className: "rounded-lg border border-blue-100 bg-blue-50 px-3 py-2 text-sm text-blue-800", children: [_jsx("span", { className: "font-medium", children: "H\u1ECDc sinh: " }), formatAnswer(userValue)] }))] })] }, `${item.questionNumber}-${itemIndex}`));
26
26
  }), 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] }))] }));
27
27
  }
@@ -10,7 +10,8 @@ import { Card, CardContent, CardHeader, CardTitle } from '../../../../components
10
10
  import { Switch } from '../../../../components/ui/switch';
11
11
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../../../components/ui/select';
12
12
  import { useDebouncedCallback } from '../../../../shared/lib/hooks';
13
- import { Eye, Pencil, Plus, Star, Trash2 } from 'lucide-react';
13
+ import { HelpCircle, Plus, Star, Trash2 } from 'lucide-react';
14
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
14
15
  import { CrossOutWordGroupClient } from './CrossOutWordGroupClient';
15
16
  function parsePointsValue(raw, allowZero = false) {
16
17
  if (raw === '')
@@ -213,19 +214,22 @@ export function CrossOutWordGroupCreator({ initialData, onChange, externalErrors
213
214
  };
214
215
  const currentPayload = buildPayload();
215
216
  const displayErrors = externalErrors && externalErrors.length > 0 ? externalErrors : errors;
216
- 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 G\u1EA1ch t\u1EEB / S\u1EEDa t\u1EEB (Nh\u00F3m)" }), _jsx("p", { className: "text-sm text-gray-500", children: "Mix 2 d\u1EA1ng: ch\u1EC9 g\u1EA1ch t\u1EEB th\u1EEBa, ho\u1EB7c g\u1EA1ch t\u1EEB sai r\u1ED3i thay b\u1EB1ng t\u1EEB \u0111\u00FAng. Click v\u00E0o t\u1EEB trong c\u00E2u \u0111\u1EC3 g\u00E1n v\u1ECB tr\u00ED (wordIndex) ch\u00EDnh x\u00E1c." })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setIsClientMode((prev) => !prev), className: "gap-2", children: [isClientMode ? _jsx(Pencil, { className: "h-4 w-4" }) : _jsx(Eye, { className: "h-4 w-4" }), isClientMode ? 'Chế độ chỉnh sửa' : 'Xem trước học sinh'] })] }), displayErrors.length > 0 && (_jsxs("div", { className: "rounded-lg border border-red-200 bg-red-50 p-4", children: [_jsx("h3", { className: "text-sm font-medium text-red-800", children: "C\u1EA7n s\u1EEDa c\u00E1c l\u1ED7i sau:" }), _jsx("ul", { className: "mt-2 list-disc space-y-1 pl-5 text-sm text-red-700", children: displayErrors.map((err, i) => (_jsx("li", { children: err }, i))) })] })), isClientMode ? (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base font-semibold", children: "Xem tr\u01B0\u1EDBc hi\u1EC3n th\u1ECB h\u1ECDc sinh" }) }), _jsx(CardContent, { children: _jsx(CrossOutWordGroupClient, { 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 chung (Instruction)" }) }), _jsxs(CardContent, { children: [_jsx(Label, { htmlFor: "instruction", children: "H\u01B0\u1EDBng d\u1EABn b\u00E0i l\u00E0m" }), _jsx(Input, { id: "instruction", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: DEFAULT_INSTRUCTION, className: "mt-1" })] })] }), _jsxs(Card, { children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "text-base font-semibold", children: ["Danh s\u00E1ch c\u00E2u (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addItem, className: "gap-2", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm c\u00E2u"] })] }), _jsx(CardContent, { className: "space-y-4", children: items.map((item, index) => {
217
- const tokens = tokenize(item.question);
218
- const selectedWordIndex = item.correctAnswer.wordIndex;
219
- return (_jsxs("div", { className: "space-y-4 rounded-lg border border-gray-200 bg-gray-50/50 p-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-full bg-blue-100 text-xs font-semibold text-blue-700", children: item.questionNumber || index + 1 }), _jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", index + 1] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-4", children: [_jsx("div", { className: "w-52", children: _jsxs(Select, { value: item.type, onValueChange: (value) => updateItemType(index, value), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Lo\u1EA1i c\u00E2u" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "CROSS_OUT", children: "Ch\u1EC9 g\u1EA1ch (CROSS_OUT)" }), _jsx(SelectItem, { value: "CROSS_OUT_AND_REPLACE", children: "G\u1EA1ch + thay (CROSS_OUT_AND_REPLACE)" })] })] }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: `example-${index}`, checked: item.isExample || false, onCheckedChange: (checked) => updateItem(index, {
220
- isExample: checked,
221
- points: checked ? 0 : 1,
222
- }) }), _jsxs(Label, { htmlFor: `example-${index}`, className: "flex cursor-pointer items-center gap-1 text-xs", children: [_jsx(Star, { className: "h-3 w-3 text-amber-500" }), "Example"] })] }), !item.isExample && (_jsx("div", { className: "w-24", children: _jsx(PointsInput, { value: item.points, allowZero: true, onChange: (event) => updateItem(index, {
223
- points: parsePointsValue(event.target.value, true),
224
- }) }) })), items.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeItem(index), className: "text-red-500 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: `question-${index}`, children: "N\u1ED9i dung c\u00E2u" }), _jsx(Textarea, { id: `question-${index}`, value: item.question, onChange: (e) => updateItem(index, { question: e.target.value }), placeholder: "Nh\u1EADp c\u00E2u g\u1ED1c...", className: "mt-1 bg-white", rows: 2 })] }), tokens.length > 0 && (_jsxs("div", { className: "rounded-lg border border-slate-200 bg-white p-3 space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { className: "text-xs font-semibold text-slate-700", children: "Click t\u1EEB c\u1EA7n g\u1EA1ch trong c\u00E2u (t\u1EF1 \u0111\u1ED9ng g\u00E1n wordIndex):" }), typeof selectedWordIndex === 'number' && (_jsxs("span", { className: "text-[11px] font-bold text-rose-600", children: ["\u0110\u00E3 ch\u1ECDn token #", selectedWordIndex + 1, ": \"", tokens[selectedWordIndex], "\""] }))] }), _jsx("div", { className: "flex flex-wrap gap-1.5 pt-1", children: tokens.map((token, tokenIdx) => {
225
- const isSelected = selectedWordIndex === tokenIdx;
226
- return (_jsx("button", { type: "button", onClick: () => toggleSelectToken(index, tokenIdx, token), className: `rounded-md px-2 py-1 text-xs font-medium transition-all ${isSelected
227
- ? 'bg-rose-500 text-white font-bold line-through shadow-sm scale-105'
228
- : 'bg-slate-100 text-slate-700 hover:bg-blue-100 hover:text-blue-700'}`, children: token }, tokenIdx));
229
- }) })] })), _jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [_jsxs("div", { children: [_jsxs(Label, { htmlFor: `crossout-${index}`, children: ["T\u1EEB c\u1EA7n g\u1EA1ch (crossOut)", item.type === 'CROSS_OUT' && (_jsx("span", { className: "ml-1 text-xs font-normal text-gray-500", children: "\u2014 \u0111\u1EC3 tr\u1ED1ng n\u1EBFu c\u00E2u \u0111\u00FAng" }))] }), _jsxs("div", { className: "flex gap-2 items-center mt-1", children: [_jsx(Input, { id: `crossout-${index}`, value: item.correctAnswer.crossOut, onChange: (e) => updateItemAnswer(index, { crossOut: e.target.value }), placeholder: item.type === 'CROSS_OUT' ? 'VD: to (hoặc trống)' : 'VD: have', className: "bg-white" }), typeof selectedWordIndex === 'number' && (_jsxs("span", { className: "whitespace-nowrap rounded bg-slate-100 px-2 py-1 text-xs text-slate-600 font-mono", children: ["idx: ", selectedWordIndex] }))] })] }), item.type === 'CROSS_OUT_AND_REPLACE' && (_jsxs("div", { children: [_jsx(Label, { htmlFor: `replace-${index}`, children: "T\u1EEB thay th\u1EBF (replaceWith)" }), _jsx(Input, { id: `replace-${index}`, value: item.correctAnswer.replaceWith || '', onChange: (e) => updateItemAnswer(index, { replaceWith: e.target.value }), placeholder: "VD: has", className: "mt-1 bg-white" })] }))] })] }, index));
230
- }) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Gi\u1EA3i th\u00EDch" }) }), _jsx(CardContent, { children: _jsx(Textarea, { id: "explanation", value: explanation, onChange: (event) => setExplanation(event.target.value), placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0... v\u00EC...", rows: 3 }) })] })] }))] }));
217
+ if (isClientMode) {
218
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(CrossOutWordGroupClient, { questionData: currentPayload })] }));
219
+ }
220
+ 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(HelpCircle, { className: "h-3.5 w-3.5" }), title: "G\u1EA1ch t\u1EEB / S\u1EEDa t\u1EEB (Nh\u00F3m)", preview: { onClick: () => setIsClientMode(true) } }), children: _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "instruction", className: "text-sm font-semibold text-gray-800", children: "H\u01B0\u1EDBng d\u1EABn b\u00E0i l\u00E0m" }), _jsx(Input, { id: "instruction", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: 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: ["Danh s\u00E1ch c\u00E2u (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addItem, className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm c\u00E2u"] })] }) }), _jsxs(CardContent, { className: "space-y-2.5 px-3 pb-3", children: [items.map((item, index) => {
221
+ const tokens = tokenize(item.question);
222
+ const selectedWordIndex = item.correctAnswer.wordIndex;
223
+ return (_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 flex-wrap items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-full bg-blue-100 text-xs font-semibold text-blue-700", children: item.questionNumber || index + 1 }), _jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", index + 1] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("div", { className: "w-52", children: _jsxs(Select, { value: item.type, onValueChange: (value) => updateItemType(index, value), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Lo\u1EA1i c\u00E2u" }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "CROSS_OUT", children: "Ch\u1EC9 g\u1EA1ch (CROSS_OUT)" }), _jsx(SelectItem, { value: "CROSS_OUT_AND_REPLACE", children: "G\u1EA1ch + thay (CROSS_OUT_AND_REPLACE)" })] })] }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: `example-${index}`, checked: item.isExample || false, onCheckedChange: (checked) => updateItem(index, {
224
+ isExample: checked,
225
+ points: checked ? 0 : 1,
226
+ }) }), _jsxs(Label, { htmlFor: `example-${index}`, className: "flex cursor-pointer items-center gap-1 text-xs", children: [_jsx(Star, { className: "h-3 w-3 text-amber-500" }), "Example"] })] }), !item.isExample && (_jsx("div", { className: "w-24", children: _jsx(PointsInput, { value: item.points, allowZero: true, onChange: (event) => updateItem(index, {
227
+ points: parsePointsValue(event.target.value, true),
228
+ }) }) })), items.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeItem(index), className: "h-7 w-7 text-red-500 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: `question-${index}`, children: "N\u1ED9i dung c\u00E2u" }), _jsx(Textarea, { id: `question-${index}`, value: item.question, onChange: (e) => updateItem(index, { question: e.target.value }), placeholder: "Nh\u1EADp c\u00E2u g\u1ED1c...", className: "mt-1 bg-white", rows: 2 })] }), tokens.length > 0 && (_jsxs("div", { className: "rounded-lg border border-slate-200 bg-white p-3 space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { className: "text-xs font-semibold text-slate-700", children: "Click t\u1EEB c\u1EA7n g\u1EA1ch trong c\u00E2u (t\u1EF1 \u0111\u1ED9ng g\u00E1n wordIndex):" }), typeof selectedWordIndex === 'number' && (_jsxs("span", { className: "text-[11px] font-bold text-rose-600", children: ["\u0110\u00E3 ch\u1ECDn token #", selectedWordIndex + 1, ": \"", tokens[selectedWordIndex], "\""] }))] }), _jsx("div", { className: "flex flex-wrap gap-1.5 pt-1", children: tokens.map((token, tokenIdx) => {
229
+ const isSelected = selectedWordIndex === tokenIdx;
230
+ return (_jsx("button", { type: "button", onClick: () => toggleSelectToken(index, tokenIdx, token), className: `rounded-md px-2 py-1 text-xs font-medium transition-all ${isSelected
231
+ ? 'bg-rose-500 text-white font-bold line-through shadow-sm scale-105'
232
+ : 'bg-slate-100 text-slate-700 hover:bg-blue-100 hover:text-blue-700'}`, children: token }, tokenIdx));
233
+ }) })] })), _jsxs("div", { className: "grid gap-2.5 md:grid-cols-2", children: [_jsxs("div", { children: [_jsxs(Label, { htmlFor: `crossout-${index}`, children: ["T\u1EEB c\u1EA7n g\u1EA1ch (crossOut)", item.type === 'CROSS_OUT' && (_jsx("span", { className: "ml-1 text-xs font-normal text-gray-500", children: "\u2014 \u0111\u1EC3 tr\u1ED1ng n\u1EBFu c\u00E2u \u0111\u00FAng" }))] }), _jsxs("div", { className: "flex gap-2 items-center mt-1", children: [_jsx(Input, { id: `crossout-${index}`, value: item.correctAnswer.crossOut, onChange: (e) => updateItemAnswer(index, { crossOut: e.target.value }), placeholder: item.type === 'CROSS_OUT' ? 'VD: to (hoặc trống)' : 'VD: have', className: "bg-white" }), typeof selectedWordIndex === 'number' && (_jsxs("span", { className: "whitespace-nowrap rounded bg-slate-100 px-2 py-1 text-xs text-slate-600 font-mono", children: ["idx: ", selectedWordIndex] }))] })] }), item.type === 'CROSS_OUT_AND_REPLACE' && (_jsxs("div", { children: [_jsx(Label, { htmlFor: `replace-${index}`, children: "T\u1EEB thay th\u1EBF (replaceWith)" }), _jsx(Input, { id: `replace-${index}`, value: item.correctAnswer.replaceWith || '', onChange: (e) => updateItemAnswer(index, { replaceWith: e.target.value }), placeholder: "VD: has", className: "mt-1 bg-white" })] }))] })] }, index));
234
+ }), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] })] }));
231
235
  }
@@ -30,7 +30,7 @@ export function CrosswordPuzzleClient({ content, correctAnswer, isReviewMode = f
30
30
  }
31
31
  return { cellMap: cMap, clueNumberMap: numMap };
32
32
  }, [acrossClues, downClues]);
33
- return (_jsxs("div", { className: "flex flex-col gap-6 p-4 bg-white rounded-xl border border-gray-200", children: [content?.title && (_jsx("h3", { className: "text-lg font-bold text-gray-800 text-center", children: content.title })), _jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-12 gap-6 items-start", children: [_jsxs("div", { className: "lg:col-span-5 flex flex-col gap-4", children: [_jsxs("div", { className: "rounded-lg border border-blue-200 bg-blue-50/50 p-4", children: [_jsxs("h4", { className: "font-bold text-blue-900 mb-2.5 flex items-center gap-2", children: [_jsx("span", { className: "px-2 py-0.5 rounded bg-blue-600 text-white text-xs font-semibold", children: "Across" }), _jsx("span", { children: "H\u00E0ng ngang" })] }), _jsx("div", { className: "flex flex-col gap-2 max-h-60 overflow-y-auto pr-1", children: acrossClues.map((c) => {
33
+ return (_jsxs("div", { className: "flex flex-col gap-3 p-3 bg-white rounded-xl border border-gray-200 shadow-sm", children: [content?.title && (_jsx("h3", { className: "text-lg font-bold text-gray-800 text-center", children: content.title })), _jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-12 gap-6 items-start", children: [_jsxs("div", { className: "lg:col-span-5 flex flex-col gap-4", children: [_jsxs("div", { className: "rounded-lg border border-blue-200 bg-blue-50/50 p-4", children: [_jsxs("h4", { className: "font-bold text-blue-900 mb-2.5 flex items-center gap-2", children: [_jsx("span", { className: "px-2 py-0.5 rounded bg-blue-600 text-white text-xs font-semibold", children: "Across" }), _jsx("span", { children: "H\u00E0ng ngang" })] }), _jsx("div", { className: "flex flex-col gap-2 max-h-60 overflow-y-auto pr-1", children: acrossClues.map((c) => {
34
34
  const answer = correctAnswer?.answers?.[`across-${c.number}`];
35
35
  const userAns = userAnswers[`across-${c.number}`];
36
36
  return (_jsxs("div", { className: "flex items-start gap-2 text-sm bg-white p-2 rounded border border-blue-100", children: [_jsxs("span", { className: "font-bold text-blue-700 min-w-[20px]", children: [c.number, "."] }), _jsxs("div", { className: "flex-1", children: [_jsx("span", { className: "text-gray-800", children: c.clue }), isReviewMode && answer && (_jsxs("div", { className: "text-xs mt-1", children: [_jsx("span", { className: "text-gray-500", children: "\u0110\u00E1p \u00E1n: " }), _jsx("span", { className: "font-bold text-green-700", children: answer }), userAns && userAns !== answer && (_jsx("span", { className: "ml-2 line-through text-red-500 font-semibold", children: userAns }))] }))] }), _jsxs("span", { className: "text-xs text-gray-400", children: ["(", c.length, " ch\u1EEF)"] })] }, `across-${c.number}`));
@@ -1,7 +1,9 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useEffect, useMemo } from 'react';
4
- import { Plus, Trash2, Edit2, Check, AlertCircle } from 'lucide-react';
4
+ import { Plus, Trash2, Edit2, Check, AlertCircle, Grid3X3 } from 'lucide-react';
5
+ import { CompactCreatorHeader, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
6
+ import { CrosswordPuzzleClient } from './CrosswordPuzzleClient';
5
7
  export function CrosswordPuzzleCreator({ initialData, onChange, onUnsavedChangesChange, }) {
6
8
  const [title, setTitle] = useState(() => {
7
9
  return initialData?.content?.title || 'Nationalities Crossword';
@@ -18,6 +20,7 @@ export function CrosswordPuzzleCreator({ initialData, onChange, onUnsavedChanges
18
20
  const [points, setPoints] = useState(() => {
19
21
  return initialData?.points ?? 11;
20
22
  });
23
+ const [isClientMode, setIsClientMode] = useState(false);
21
24
  const [clues, setClues] = useState(() => {
22
25
  if (Array.isArray(initialData?.content?.clues)) {
23
26
  return initialData.content.clues;
@@ -182,32 +185,66 @@ export function CrosswordPuzzleCreator({ initialData, onChange, onUnsavedChanges
182
185
  setFormAnswer('');
183
186
  }
184
187
  };
185
- return (_jsxs("div", { className: "flex flex-col gap-6 p-4 bg-white rounded-xl border border-gray-200", children: [_jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-4 gap-4 p-4 bg-gray-50 rounded-lg border border-gray-200", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-xs font-bold text-gray-700 mb-1", children: "Ti\u00EAu \u0111\u1EC1 \u00F4 ch\u1EEF" }), _jsx("input", { type: "text", value: title, onChange: (e) => setTitle(e.target.value), placeholder: "vd: Nationalities Crossword", className: "w-full px-3 py-1.5 text-sm border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 outline-none" })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-bold text-gray-700 mb-1", children: "K\u00EDch th\u01B0\u1EDBc l\u01B0\u1EDBi (H\u00E0ng x C\u1ED9t)" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { type: "number", min: 3, max: 30, value: rows, onChange: (e) => setRows(Math.max(3, parseInt(e.target.value) || 12)), className: "w-20 px-2 py-1.5 text-sm border border-gray-300 rounded outline-none" }), _jsx("span", { className: "text-gray-500 font-bold", children: "x" }), _jsx("input", { type: "number", min: 3, max: 30, value: cols, onChange: (e) => setCols(Math.max(3, parseInt(e.target.value) || 12)), className: "w-20 px-2 py-1.5 text-sm border border-gray-300 rounded outline-none" })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-bold text-gray-700 mb-1", children: "\u0110i\u1EC3m s\u1ED1" }), _jsx("input", { type: "number", min: 0, step: 0.5, value: points, onChange: (e) => setPoints(parseFloat(e.target.value) || 1), className: "w-full px-3 py-1.5 text-sm border border-gray-300 rounded outline-none" })] }), _jsx("div", { className: "flex items-center pt-5", children: _jsxs("label", { className: "flex items-center gap-2 text-sm text-gray-700 cursor-pointer", children: [_jsx("input", { type: "checkbox", checked: caseSensitive, onChange: (e) => setCaseSensitive(e.target.checked), className: "rounded text-blue-600 focus:ring-blue-500 h-4 w-4" }), _jsx("span", { children: "Ph\u00E2n bi\u1EC7t hoa/th\u01B0\u1EDDng" })] }) })] }), _jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-12 gap-6", children: [_jsxs("div", { className: "lg:col-span-5 flex flex-col gap-4", children: [_jsxs("form", { onSubmit: handleSaveClue, className: "p-4 rounded-xl border border-blue-200 bg-blue-50/40 flex flex-col gap-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-sm font-bold text-blue-900", children: editingClueId ? 'Chỉnh sửa gợi ý' : 'Thêm gợi ý mới' }), editingClueId && (_jsx("button", { type: "button", onClick: () => {
186
- setEditingClueId(null);
187
- setFormClue('');
188
- setFormAnswer('');
189
- }, className: "text-xs text-gray-500 hover:text-gray-700 underline", children: "H\u1EE7y ch\u1EC9nh s\u1EEDa" }))] }), _jsxs("div", { className: "grid grid-cols-3 gap-2", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-[11px] font-semibold text-gray-600 mb-0.5", children: "S\u1ED1 Clue" }), _jsx("input", { type: "number", min: 1, value: formNumber, onChange: (e) => setFormNumber(parseInt(e.target.value) || 1), className: "w-full px-2 py-1 text-sm border border-gray-300 rounded outline-none" })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[11px] font-semibold text-gray-600 mb-0.5", children: "H\u01B0\u1EDBng" }), _jsxs("select", { value: formDirection, onChange: (e) => setFormDirection(e.target.value), className: "w-full px-2 py-1 text-sm border border-gray-300 rounded outline-none bg-white", children: [_jsx("option", { value: "across", children: "Across (Ngang)" }), _jsx("option", { value: "down", children: "Down (D\u1ECDc)" })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[11px] font-semibold text-gray-600 mb-0.5", children: "To\u1EA1 \u0111\u1ED9 (R, C)" }), _jsxs("div", { className: "flex gap-1", children: [_jsx("input", { type: "number", min: 1, max: rows, value: formStartRow, onChange: (e) => setFormStartRow(parseInt(e.target.value) || 1), placeholder: "R", className: "w-1/2 px-1 py-1 text-xs border border-gray-300 rounded text-center" }), _jsx("input", { type: "number", min: 1, max: cols, value: formStartCol, onChange: (e) => setFormStartCol(parseInt(e.target.value) || 1), placeholder: "C", className: "w-1/2 px-1 py-1 text-xs border border-gray-300 rounded text-center" })] })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[11px] font-semibold text-gray-600 mb-0.5", children: "C\u00E2u g\u1EE3i \u00FD (Clue)" }), _jsx("input", { type: "text", value: formClue, onChange: (e) => setFormClue(e.target.value), placeholder: "vd: Spain, Capital of France...", className: "w-full px-3 py-1.5 text-sm border border-gray-300 rounded outline-none", required: true })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[11px] font-semibold text-gray-600 mb-0.5", children: "T\u1EEB \u0111\u00E1p \u00E1n (Answer Word)" }), _jsx("input", { type: "text", value: formAnswer, onChange: (e) => setFormAnswer(e.target.value), placeholder: "vd: SPANISH", className: "w-full px-3 py-1.5 text-sm border border-gray-300 rounded outline-none uppercase font-semibold", required: true })] }), _jsxs("button", { type: "submit", className: "mt-1 flex items-center justify-center gap-1.5 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded text-sm font-semibold transition", children: [editingClueId ? _jsx(Check, { className: "h-4 w-4" }) : _jsx(Plus, { className: "h-4 w-4" }), _jsx("span", { children: editingClueId ? 'Cập nhật gợi ý' : 'Thêm vào danh sách' })] })] }), _jsxs("div", { className: "flex flex-col gap-2 max-h-[420px] overflow-y-auto pr-1", children: [_jsxs("span", { className: "text-xs font-bold text-gray-600", children: ["Danh s\u00E1ch (", clues.length, " t\u1EEB):"] }), clues.length === 0 && (_jsx("p", { className: "text-xs text-gray-400 italic", children: "Ch\u01B0a c\u00F3 g\u1EE3i \u00FD n\u00E0o. H\u00E3y th\u00EAm g\u1EE3i \u00FD \u1EDF tr\u00EAn." })), clues.map((c) => (_jsxs("div", { className: "flex items-center justify-between p-2.5 bg-white rounded-lg border border-gray-200 hover:border-blue-300 transition text-sm shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 flex-1 min-w-0", children: [_jsx("span", { className: `flex items-center justify-center h-6 w-6 rounded text-xs font-bold text-white ${c.direction === 'across' ? 'bg-blue-600' : 'bg-purple-600'}`, children: c.number }), _jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [_jsx("span", { className: "font-semibold text-gray-800 truncate", children: c.clue }), _jsxs("span", { className: "text-xs text-gray-500 flex items-center gap-2", children: [_jsx("span", { className: "font-bold text-blue-700", children: c.answer }), _jsxs("span", { children: ["(", c.length, " ch\u1EEF)"] }), _jsxs("span", { children: ["[", c.direction === 'across' ? '→ Ngang' : '↓ Dọc', " (", c.startRow, ",", c.startCol, ")]"] })] })] })] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => handleEditClue(c), className: "p-1 text-gray-500 hover:text-blue-600 rounded", title: "S\u1EEDa", children: _jsx(Edit2, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", onClick: () => handleDeleteClue(c.id), className: "p-1 text-gray-500 hover:text-red-600 rounded", title: "X\u00F3a", children: _jsx(Trash2, { className: "h-4 w-4" }) })] })] }, c.id)))] })] }), _jsxs("div", { className: "lg:col-span-7 flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-xs font-bold text-gray-700", children: "Ma tr\u1EADn l\u01B0\u1EDBi \u00F4 ch\u1EEF tr\u1EF1c quan:" }), conflicts.length > 0 && (_jsxs("span", { className: "flex items-center gap-1 text-xs font-bold text-red-600 bg-red-50 px-2 py-0.5 rounded border border-red-200", children: [_jsx(AlertCircle, { className: "h-3.5 w-3.5" }), _jsxs("span", { children: ["C\u00F3 ", conflicts.length, " \u00F4 b\u1ECB xung \u0111\u1ED9t ch\u1EEF c\u00E1i!"] })] }))] }), _jsx("div", { className: "flex justify-center overflow-x-auto p-4 bg-gray-100 rounded-xl border border-gray-300 min-h-[380px]", children: _jsx("div", { className: "inline-grid gap-1", style: {
190
- gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`,
191
- }, children: Array.from({ length: rows }).map((_, rIdx) => {
192
- const r = rIdx + 1;
193
- return Array.from({ length: cols }).map((_, cIdx) => {
194
- const c = cIdx + 1;
195
- const key = `${r}-${c}`;
196
- const cellEntries = cellMap.get(key);
197
- const clueNums = clueNumberMap.get(key);
198
- const isConflict = conflicts.includes(key);
199
- if (!cellEntries || cellEntries.length === 0) {
200
- return (_jsx("button", { type: "button", onClick: () => {
201
- setFormStartRow(r);
202
- setFormStartCol(c);
203
- }, title: `Ô trống (${r},${c}) - Click để chọn toạ độ bắt đầu`, className: "h-8 w-8 sm:h-9 sm:w-9 md:h-10 md:w-10 bg-transparent hover:bg-gray-200/60 rounded flex items-center justify-center transition text-[10px] text-gray-300 hover:text-gray-500", children: "\u00B7" }, key));
204
- }
205
- const displayLetter = cellEntries[0]?.letter || '';
206
- return (_jsxs("div", { className: `relative flex items-center justify-center rounded border-2 font-bold shadow-sm h-8 w-8 sm:h-9 sm:w-9 md:h-10 md:w-10 text-sm sm:text-base md:text-lg select-none transition ${isConflict
207
- ? 'border-red-500 bg-red-100 text-red-700 animate-pulse'
208
- : cellEntries.length > 1
209
- ? 'border-indigo-400 bg-indigo-50 text-indigo-900'
210
- : 'border-gray-400 bg-white text-gray-800'}`, children: [clueNums && clueNums.length > 0 && (_jsx("span", { className: "absolute top-0.5 left-0.5 text-[9px] font-bold text-gray-500 leading-none", children: clueNums.join('/') })), _jsx("span", { children: displayLetter })] }, key));
211
- });
212
- }) }) })] })] })] }));
188
+ if (isClientMode) {
189
+ const across = clues
190
+ .filter((c) => c.direction === 'across')
191
+ .map((c) => ({
192
+ number: c.number,
193
+ clue: c.clue.trim(),
194
+ startRow: c.startRow,
195
+ startCol: c.startCol,
196
+ length: c.length || c.answer.trim().length,
197
+ }));
198
+ const down = clues
199
+ .filter((c) => c.direction === 'down')
200
+ .map((c) => ({
201
+ number: c.number,
202
+ clue: c.clue.trim(),
203
+ startRow: c.startRow,
204
+ startCol: c.startCol,
205
+ length: c.length || c.answer.trim().length,
206
+ }));
207
+ const answers = {};
208
+ for (const c of clues) {
209
+ if (c.answer) {
210
+ answers[`${c.direction}-${c.number}`] = c.answer.trim().toUpperCase();
211
+ }
212
+ }
213
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(CrosswordPuzzleClient, { content: {
214
+ title,
215
+ gridSize: { rows, cols },
216
+ clues: { across, down },
217
+ }, correctAnswer: { answers, caseSensitive }, isReviewMode: true, userAnswers: answers })] }));
218
+ }
219
+ return (_jsx("div", { className: "space-y-3", children: _jsx(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Grid3X3, { className: "h-3.5 w-3.5" }), title: "\u00D4 ch\u1EEF (Crossword)", points: {
220
+ value: points,
221
+ onValueChange: setPoints,
222
+ }, preview: { onClick: () => setIsClientMode(true) } }), children: _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4 p-3 bg-gray-50 rounded-lg border border-gray-200", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-xs font-bold text-gray-700 mb-1", children: "Ti\u00EAu \u0111\u1EC1 \u00F4 ch\u1EEF" }), _jsx("input", { type: "text", value: title, onChange: (e) => setTitle(e.target.value), placeholder: "vd: Nationalities Crossword", className: "w-full px-3 py-1.5 text-sm border border-gray-300 rounded focus:ring-1 focus:ring-blue-500 outline-none" })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-xs font-bold text-gray-700 mb-1", children: "K\u00EDch th\u01B0\u1EDBc l\u01B0\u1EDBi (H\u00E0ng x C\u1ED9t)" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { type: "number", min: 3, max: 30, value: rows, onChange: (e) => setRows(Math.max(3, parseInt(e.target.value) || 12)), className: "w-20 px-2 py-1.5 text-sm border border-gray-300 rounded outline-none" }), _jsx("span", { className: "text-gray-500 font-bold", children: "x" }), _jsx("input", { type: "number", min: 3, max: 30, value: cols, onChange: (e) => setCols(Math.max(3, parseInt(e.target.value) || 12)), className: "w-20 px-2 py-1.5 text-sm border border-gray-300 rounded outline-none" })] })] }), _jsx("div", { className: "flex items-center pt-5", children: _jsxs("label", { className: "flex items-center gap-2 text-sm text-gray-700 cursor-pointer", children: [_jsx("input", { type: "checkbox", checked: caseSensitive, onChange: (e) => setCaseSensitive(e.target.checked), className: "rounded text-blue-600 focus:ring-blue-500 h-4 w-4" }), _jsx("span", { children: "Ph\u00E2n bi\u1EC7t hoa/th\u01B0\u1EDDng" })] }) })] }), _jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-12 gap-6", children: [_jsxs("div", { className: "lg:col-span-5 flex flex-col gap-4", children: [_jsxs("form", { onSubmit: handleSaveClue, className: "p-4 rounded-xl border border-blue-200 bg-blue-50/40 flex flex-col gap-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-sm font-bold text-blue-900", children: editingClueId ? 'Chỉnh sửa gợi ý' : 'Thêm gợi ý mới' }), editingClueId && (_jsx("button", { type: "button", onClick: () => {
223
+ setEditingClueId(null);
224
+ setFormClue('');
225
+ setFormAnswer('');
226
+ }, className: "text-xs text-gray-500 hover:text-gray-700 underline", children: "H\u1EE7y ch\u1EC9nh s\u1EEDa" }))] }), _jsxs("div", { className: "grid grid-cols-3 gap-2", children: [_jsxs("div", { children: [_jsx("label", { className: "block text-[11px] font-semibold text-gray-600 mb-0.5", children: "S\u1ED1 Clue" }), _jsx("input", { type: "number", min: 1, value: formNumber, onChange: (e) => setFormNumber(parseInt(e.target.value) || 1), className: "w-full px-2 py-1 text-sm border border-gray-300 rounded outline-none" })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[11px] font-semibold text-gray-600 mb-0.5", children: "H\u01B0\u1EDBng" }), _jsxs("select", { value: formDirection, onChange: (e) => setFormDirection(e.target.value), className: "w-full px-2 py-1 text-sm border border-gray-300 rounded outline-none bg-white", children: [_jsx("option", { value: "across", children: "Across (Ngang)" }), _jsx("option", { value: "down", children: "Down (D\u1ECDc)" })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[11px] font-semibold text-gray-600 mb-0.5", children: "To\u1EA1 \u0111\u1ED9 (R, C)" }), _jsxs("div", { className: "flex gap-1", children: [_jsx("input", { type: "number", min: 1, max: rows, value: formStartRow, onChange: (e) => setFormStartRow(parseInt(e.target.value) || 1), placeholder: "R", className: "w-1/2 px-1 py-1 text-xs border border-gray-300 rounded text-center" }), _jsx("input", { type: "number", min: 1, max: cols, value: formStartCol, onChange: (e) => setFormStartCol(parseInt(e.target.value) || 1), placeholder: "C", className: "w-1/2 px-1 py-1 text-xs border border-gray-300 rounded text-center" })] })] })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[11px] font-semibold text-gray-600 mb-0.5", children: "C\u00E2u g\u1EE3i \u00FD (Clue)" }), _jsx("input", { type: "text", value: formClue, onChange: (e) => setFormClue(e.target.value), placeholder: "vd: Spain, Capital of France...", className: "w-full px-3 py-1.5 text-sm border border-gray-300 rounded outline-none", required: true })] }), _jsxs("div", { children: [_jsx("label", { className: "block text-[11px] font-semibold text-gray-600 mb-0.5", children: "T\u1EEB \u0111\u00E1p \u00E1n (Answer Word)" }), _jsx("input", { type: "text", value: formAnswer, onChange: (e) => setFormAnswer(e.target.value), placeholder: "vd: SPANISH", className: "w-full px-3 py-1.5 text-sm border border-gray-300 rounded outline-none uppercase font-semibold", required: true })] }), _jsxs("button", { type: "submit", className: "mt-1 flex items-center justify-center gap-1.5 px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded text-sm font-semibold transition", children: [editingClueId ? _jsx(Check, { className: "h-4 w-4" }) : _jsx(Plus, { className: "h-4 w-4" }), _jsx("span", { children: editingClueId ? 'Cập nhật gợi ý' : 'Thêm vào danh sách' })] })] }), _jsxs("div", { className: "flex flex-col gap-2 max-h-[420px] overflow-y-auto pr-1", children: [_jsxs("span", { className: "text-xs font-bold text-gray-600", children: ["Danh s\u00E1ch (", clues.length, " t\u1EEB):"] }), clues.length === 0 && (_jsx("p", { className: "text-xs text-gray-400 italic", children: "Ch\u01B0a c\u00F3 g\u1EE3i \u00FD n\u00E0o. H\u00E3y th\u00EAm g\u1EE3i \u00FD \u1EDF tr\u00EAn." })), clues.map((c) => (_jsxs("div", { className: "flex items-center justify-between p-2.5 bg-white rounded-lg border border-gray-200 hover:border-blue-300 transition text-sm shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 flex-1 min-w-0", children: [_jsx("span", { className: `flex items-center justify-center h-6 w-6 rounded text-xs font-bold text-white ${c.direction === 'across' ? 'bg-blue-600' : 'bg-purple-600'}`, children: c.number }), _jsxs("div", { className: "flex flex-col min-w-0 flex-1", children: [_jsx("span", { className: "font-semibold text-gray-800 truncate", children: c.clue }), _jsxs("span", { className: "text-xs text-gray-500 flex items-center gap-2", children: [_jsx("span", { className: "font-bold text-blue-700", children: c.answer }), _jsxs("span", { children: ["(", c.length, " ch\u1EEF)"] }), _jsxs("span", { children: ["[", c.direction === 'across' ? '→ Ngang' : '↓ Dọc', " (", c.startRow, ",", c.startCol, ")]"] })] })] })] }), _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => handleEditClue(c), className: "p-1 text-gray-500 hover:text-blue-600 rounded", title: "S\u1EEDa", children: _jsx(Edit2, { className: "h-4 w-4" }) }), _jsx("button", { type: "button", onClick: () => handleDeleteClue(c.id), className: "p-1 text-gray-500 hover:text-red-600 rounded", title: "X\u00F3a", children: _jsx(Trash2, { className: "h-4 w-4" }) })] })] }, c.id)))] })] }), _jsxs("div", { className: "lg:col-span-7 flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-xs font-bold text-gray-700", children: "Ma tr\u1EADn l\u01B0\u1EDBi \u00F4 ch\u1EEF tr\u1EF1c quan:" }), conflicts.length > 0 && (_jsxs("span", { className: "flex items-center gap-1 text-xs font-bold text-red-600 bg-red-50 px-2 py-0.5 rounded border border-red-200", children: [_jsx(AlertCircle, { className: "h-3.5 w-3.5" }), _jsxs("span", { children: ["C\u00F3 ", conflicts.length, " \u00F4 b\u1ECB xung \u0111\u1ED9t ch\u1EEF c\u00E1i!"] })] }))] }), _jsx("div", { className: "flex justify-center overflow-x-auto p-4 bg-gray-100 rounded-xl border border-gray-300 min-h-[380px]", children: _jsx("div", { className: "inline-grid gap-1", style: {
227
+ gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`,
228
+ }, children: Array.from({ length: rows }).map((_, rIdx) => {
229
+ const r = rIdx + 1;
230
+ return Array.from({ length: cols }).map((_, cIdx) => {
231
+ const c = cIdx + 1;
232
+ const key = `${r}-${c}`;
233
+ const cellEntries = cellMap.get(key);
234
+ const clueNums = clueNumberMap.get(key);
235
+ const isConflict = conflicts.includes(key);
236
+ if (!cellEntries || cellEntries.length === 0) {
237
+ return (_jsx("button", { type: "button", onClick: () => {
238
+ setFormStartRow(r);
239
+ setFormStartCol(c);
240
+ }, title: `Ô trống (${r},${c}) - Click để chọn toạ độ bắt đầu`, className: "h-8 w-8 sm:h-9 sm:w-9 md:h-10 md:w-10 bg-transparent hover:bg-gray-200/60 rounded flex items-center justify-center transition text-[10px] text-gray-300 hover:text-gray-500", children: "\u00B7" }, key));
241
+ }
242
+ const displayLetter = cellEntries[0]?.letter || '';
243
+ return (_jsxs("div", { className: `relative flex items-center justify-center rounded border-2 font-bold shadow-sm h-8 w-8 sm:h-9 sm:w-9 md:h-10 md:w-10 text-sm sm:text-base md:text-lg select-none transition ${isConflict
244
+ ? 'border-red-500 bg-red-100 text-red-700 animate-pulse'
245
+ : cellEntries.length > 1
246
+ ? 'border-indigo-400 bg-indigo-50 text-indigo-900'
247
+ : 'border-gray-400 bg-white text-gray-800'}`, children: [clueNums && clueNums.length > 0 && (_jsx("span", { className: "absolute top-0.5 left-0.5 text-[9px] font-bold text-gray-500 leading-none", children: clueNums.join('/') })), _jsx("span", { children: displayLetter })] }, key));
248
+ });
249
+ }) }) })] })] })] }) }) }));
213
250
  }