@tinyweb_dev/oe-exam-sdk 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/dist/components/exams/ExamCreator.js +1 -16
  2. package/dist/components/exams/take/ExamTakingPageContainer.js +0 -12
  3. package/dist/components/exams/take/components/QuestionRenderer.js +2 -15
  4. package/dist/components/exams/take/components/question-renderers/index.d.ts +0 -3
  5. package/dist/components/exams/take/components/question-renderers/index.js +0 -3
  6. package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionRenderer.js +0 -4
  7. package/dist/components/exams/take/types/speaking.types.d.ts +0 -31
  8. package/dist/components/exams/take/types.d.ts +0 -65
  9. package/dist/components/exams/take/utils/answer-transformers.js +6 -60
  10. package/dist/components/exams/take/utils/question-transformers.d.ts +2 -15
  11. package/dist/components/exams/take/utils/question-transformers.js +0 -143
  12. package/dist/components/questions/_shared/components/compact/CompactCreatorHeader.d.ts +23 -0
  13. package/dist/components/questions/_shared/components/compact/CompactCreatorHeader.js +9 -0
  14. package/dist/components/questions/_shared/components/compact/CompactExampleToggle.d.ts +6 -0
  15. package/dist/components/questions/_shared/components/compact/CompactExampleToggle.js +8 -0
  16. package/dist/components/questions/_shared/components/compact/CompactExplanationToggle.d.ts +7 -0
  17. package/dist/components/questions/_shared/components/compact/CompactExplanationToggle.js +14 -0
  18. package/dist/components/questions/_shared/components/compact/CompactFieldCard.d.ts +9 -0
  19. package/dist/components/questions/_shared/components/compact/CompactFieldCard.js +7 -0
  20. package/dist/components/questions/_shared/components/compact/CompactPreviewBanner.d.ts +7 -0
  21. package/dist/components/questions/_shared/components/compact/CompactPreviewBanner.js +7 -0
  22. package/dist/components/questions/_shared/components/compact/CompactPreviewButton.d.ts +6 -0
  23. package/dist/components/questions/_shared/components/compact/CompactPreviewButton.js +7 -0
  24. package/dist/components/questions/_shared/components/compact/index.d.ts +6 -0
  25. package/dist/components/questions/_shared/components/compact/index.js +6 -0
  26. package/dist/components/questions/_shared/config/question-types.config.js +0 -24
  27. package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +0 -9
  28. package/dist/components/questions/_shared/types/index.d.ts +0 -1
  29. package/dist/components/questions/_shared/types/index.js +0 -1
  30. package/dist/components/questions/_shared/types/look-and-choose-answer.type.d.ts +0 -13
  31. package/dist/components/questions/_shared/utils/answer-builder.js +0 -11
  32. package/dist/components/questions/components/ColorRegionCreator.js +66 -63
  33. package/dist/components/questions/components/QuestionSearchDropdown.js +0 -8
  34. package/dist/components/questions/components/QuestionTypeSelector.js +0 -2
  35. package/dist/components/questions/components/index.d.ts +1 -0
  36. package/dist/components/questions/components/index.js +1 -0
  37. package/dist/components/questions/creator/question-type-registry.js +0 -8
  38. package/dist/components/questions/groups/ArticlesGroupCard.js +2 -2
  39. package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -1
  40. package/dist/components/questions/groups/DocumentGroupCard.js +1 -1
  41. package/dist/components/questions/groups/ImageLabelGroupCard.js +1 -1
  42. package/dist/components/questions/groups/MatchAnswerGroupCard.js +2 -2
  43. package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +0 -3
  44. package/dist/components/questions/question-bank/QuestionEditorRenderer.js +1 -10
  45. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.d.ts +0 -2
  46. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.js +0 -41
  47. package/dist/components/questions/question-bank/editor-adapters/index.d.ts +2 -2
  48. package/dist/components/questions/question-bank/editor-adapters/index.js +2 -2
  49. package/dist/components/questions/question-bank/editor-adapters/media-adapters.d.ts +0 -1
  50. package/dist/components/questions/question-bank/editor-adapters/media-adapters.js +0 -28
  51. package/dist/components/questions/question-bank/question-bank-picker.utils.js +0 -7
  52. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +16 -16
  53. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +36 -30
  54. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupClient.js +1 -1
  55. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupCreator.js +7 -6
  56. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +2 -2
  57. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +11 -7
  58. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -1
  59. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +11 -5
  60. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +5 -5
  61. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +34 -28
  62. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseAnswerGroupImageUpload.js +2 -2
  63. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +1 -1
  64. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupCreator.js +44 -28
  65. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupClient.js +7 -7
  66. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupCreator.js +8 -5
  67. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +2 -2
  68. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +20 -16
  69. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.js +1 -1
  70. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleCreator.js +66 -29
  71. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +5 -5
  72. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +45 -37
  73. package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupCreator.js +24 -20
  74. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +2 -2
  75. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +68 -65
  76. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixClient.js +1 -1
  77. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixCreator.js +45 -5
  78. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +2 -2
  79. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +18 -14
  80. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +2 -2
  81. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +30 -30
  82. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +2 -2
  83. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +24 -31
  84. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +2 -2
  85. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +29 -32
  86. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -1
  87. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +27 -29
  88. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -1
  89. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +11 -15
  90. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -1
  91. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +22 -24
  92. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -1
  93. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +17 -24
  94. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -1
  95. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +31 -31
  96. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -1
  97. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +11 -15
  98. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -1
  99. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +14 -15
  100. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +2 -2
  101. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +27 -25
  102. package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesClient.js +12 -12
  103. package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesCreator.js +8 -5
  104. package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureClient.js +8 -8
  105. package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureCreator.js +25 -20
  106. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupClient.js +1 -1
  107. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupCreator.js +13 -10
  108. package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupCreator.js +8 -5
  109. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +7 -11
  110. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -1
  111. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +15 -12
  112. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesClient.js +1 -1
  113. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesCreator.js +5 -5
  114. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +35 -39
  115. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +2 -2
  116. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardClient.js +1 -1
  117. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +7 -8
  118. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -1
  119. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +30 -23
  120. package/dist/components/questions/types/spontaneous-qa/SpontaneousQAClient.js +1 -1
  121. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +26 -19
  122. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.js +1 -1
  123. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.js +13 -11
  124. package/dist/components/questions/types/true-false/TrueFalseClient.js +2 -2
  125. package/dist/components/questions/types/true-false/TrueFalseCreator.js +35 -29
  126. package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupClient.js +1 -1
  127. package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupCreator.js +11 -7
  128. package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +1 -1
  129. package/dist/components/questions/types/true-false-group/TrueFalseGroupCreator.js +11 -7
  130. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +2 -2
  131. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +21 -56
  132. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -1
  133. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +30 -24
  134. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.js +3 -3
  135. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupCreator.js +12 -14
  136. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -1
  137. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +27 -18
  138. package/dist/components/questions/types/word-ordering-group/WordOrderingGroupClient.js +7 -7
  139. package/dist/components/questions/types/word-ordering-group/WordOrderingGroupCreator.js +16 -13
  140. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +2 -2
  141. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +34 -28
  142. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +2 -2
  143. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +22 -22
  144. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +21 -18
  145. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -1
  146. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +7 -4
  147. package/dist/components/questions/viewer/QuestionViewer.js +0 -98
  148. package/dist/components/results/renderers/review-question-body-movers.js +2 -26
  149. package/dist/components/results/review-data.js +0 -4
  150. package/dist/components/ui/points-input.d.ts +1 -0
  151. package/dist/components/ui/points-input.js +12 -2
  152. package/dist/shared/constants/question-skills.js +0 -11
  153. package/dist/shared/lib/i18n/messages/en/admin.d.ts +0 -3
  154. package/dist/shared/lib/i18n/messages/en/admin.js +0 -3
  155. package/dist/shared/lib/i18n/messages/en.d.ts +0 -3
  156. package/dist/shared/lib/i18n/messages/vi/admin.d.ts +0 -3
  157. package/dist/shared/lib/i18n/messages/vi/admin.js +0 -3
  158. package/dist/shared/lib/i18n/messages/vi.d.ts +0 -3
  159. package/dist/shared/lib/utils/question-reverse-transform.js +0 -19
  160. package/dist/shared/lib/utils/question-transform-types.d.ts +1 -23
  161. package/dist/shared/lib/utils/question-transform-validation.js +0 -40
  162. package/dist/shared/lib/utils/question-transform.js +0 -9
  163. package/dist/shared/types/common.types.d.ts +1 -1
  164. package/dist/shared/types/entities/question.types.d.ts +1 -11
  165. package/dist/shared/types/questions/index.d.ts +0 -3
  166. package/dist/shared/types/questions/index.js +0 -6
  167. package/package.json +1 -1
  168. package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.d.ts +0 -2
  169. package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.js +0 -131
  170. package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.d.ts +0 -2
  171. package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.js +0 -76
  172. package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.d.ts +0 -2
  173. package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.js +0 -68
  174. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.d.ts +0 -16
  175. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.js +0 -212
  176. package/dist/components/questions/_shared/types/gn-speaking-interview.type.d.ts +0 -32
  177. package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +0 -1
  178. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.d.ts +0 -47
  179. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +0 -1
  180. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.d.ts +0 -2
  181. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +0 -262
  182. package/dist/components/questions/types/gn-speaking-interview/register.d.ts +0 -2
  183. package/dist/components/questions/types/gn-speaking-interview/register.js +0 -121
  184. package/dist/components/questions/types/gn-speaking-interview/transform.d.ts +0 -2
  185. package/dist/components/questions/types/gn-speaking-interview/transform.js +0 -48
  186. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.d.ts +0 -20
  187. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +0 -72
  188. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.d.ts +0 -15
  189. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +0 -230
  190. package/dist/components/questions/types/look-picture-choose-correct-answer/index.d.ts +0 -1
  191. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +0 -1
  192. package/dist/components/questions/types/look-picture-choose-correct-answer/register.d.ts +0 -2
  193. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +0 -66
  194. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.d.ts +0 -2
  195. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +0 -42
  196. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.d.ts +0 -6
  197. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +0 -96
  198. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.d.ts +0 -15
  199. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +0 -238
  200. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.d.ts +0 -2
  201. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +0 -2
  202. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.d.ts +0 -2
  203. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +0 -76
  204. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.d.ts +0 -2
  205. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +0 -19
  206. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.d.ts +0 -7
  207. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +0 -134
  208. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.d.ts +0 -3
  209. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +0 -617
  210. package/dist/components/questions/types/look-picture-fill-word-hint/index.d.ts +0 -2
  211. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +0 -2
  212. package/dist/components/questions/types/look-picture-fill-word-hint/register.d.ts +0 -2
  213. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +0 -82
  214. package/dist/components/questions/types/look-picture-fill-word-hint/transform.d.ts +0 -2
  215. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +0 -29
  216. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.d.ts +0 -9
  217. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +0 -12
  218. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.d.ts +0 -17
  219. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +0 -47
  220. package/dist/shared/types/questions/look-picture-choose-correct-answer.d.ts +0 -38
  221. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +0 -5
  222. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.d.ts +0 -63
  223. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +0 -9
  224. package/dist/shared/types/questions/look-picture-fill-word-hint.d.ts +0 -57
  225. package/dist/shared/types/questions/look-picture-fill-word-hint.js +0 -7
@@ -187,7 +187,7 @@ export function FillInBlankClient({ questionData, questionConfig, onSubmit, isRe
187
187
  const explText = explanation || questionData.explanation;
188
188
  if (!isReviewMode || !explText)
189
189
  return null;
190
- return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-blue-200 bg-gradient-to-br from-blue-50 to-indigo-50 shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2.5 border-b border-blue-100 bg-white/60 px-5 py-3", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-400 to-orange-500 text-white shadow-sm", children: _jsx(Lightbulb, { className: "h-4 w-4" }) }), _jsx("h4", { className: "text-sm font-semibold text-slate-800", children: "Gi\u1EA3i th\u00EDch" })] }), _jsx("div", { className: "p-5", children: _jsx("p", { className: "text-sm text-slate-700 leading-relaxed", children: explText }) })] }));
190
+ return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-blue-200 bg-gradient-to-br from-blue-50 to-indigo-50 shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-blue-100 bg-white/60 px-3 py-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gradient-to-br from-amber-400 to-orange-500 text-white", children: _jsx(Lightbulb, { className: "h-3.5 w-3.5" }) }), _jsx("h4", { className: "text-sm font-semibold text-slate-800", children: "Gi\u1EA3i th\u00EDch" })] }), _jsx("div", { className: "p-3", children: _jsx("p", { className: "text-sm text-slate-700 leading-relaxed", children: explText }) })] }));
191
191
  };
192
192
  /** Question-level image (non-group) */
193
193
  const renderQuestionImage = () => {
@@ -208,11 +208,11 @@ export function FillInBlankClient({ questionData, questionConfig, onSubmit, isRe
208
208
  // =====================================================
209
209
  if (!isLegacyFormat) {
210
210
  const hasAnyAnswer = placeholderIndices.some(idx => (answers[idx] || '').trim());
211
- return (_jsxs("div", { className: "space-y-4", children: [renderGroupBlock(), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm transition-all hover:shadow-md", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-5 py-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-orange-500 to-amber-600 text-white shadow-sm", children: _jsx(PenLine, { className: "h-4 w-4" }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-sm font-semibold text-gray-700", children: "\u0110i\u1EC1n v\u00E0o ch\u1ED7 tr\u1ED1ng" }), _jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-orange-100 px-2 py-0.5 text-xs font-medium text-orange-700 ring-1 ring-inset ring-orange-200/60", children: [_jsx(Hash, { className: "h-3 w-3" }), placeholderIndices.length] })] })] }), isReviewMode && score && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-bold', score.correct === score.total
211
+ return (_jsxs("div", { className: "space-y-4", children: [renderGroupBlock(), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm transition-all hover:shadow-md", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gradient-to-br from-orange-500 to-amber-600 text-white", children: _jsx(PenLine, { className: "h-3.5 w-3.5" }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-sm font-semibold text-gray-700", children: "\u0110i\u1EC1n v\u00E0o ch\u1ED7 tr\u1ED1ng" }), _jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-orange-100 px-2 py-0.5 text-xs font-medium text-orange-700 ring-1 ring-inset ring-orange-200/60", children: [_jsx(Hash, { className: "h-3 w-3" }), placeholderIndices.length] })] })] }), isReviewMode && score && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-bold', score.correct === score.total
212
212
  ? 'bg-emerald-100 text-emerald-700 ring-1 ring-emerald-200/60'
213
213
  : score.correct >= score.total / 2
214
214
  ? 'bg-yellow-100 text-yellow-700 ring-1 ring-yellow-200/60'
215
- : 'bg-red-100 text-red-700 ring-1 ring-red-200/60'), children: [score.correct === score.total && _jsx(Check, { className: "h-3 w-3" }), score.correct, "/", score.total, " \u0111\u00FAng"] }))] }), _jsxs("div", { className: "p-5 space-y-4", children: [showAudio && cleanAudioUrl && (_jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-indigo-100 bg-gradient-to-r from-indigo-50 to-blue-50 p-3", children: [_jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-indigo-100", children: _jsx(Volume2, { className: "h-4 w-4 text-indigo-600" }) }), _jsx("audio", { controls: true, className: "h-9 w-full", src: audioPreviewUrl || cleanAudioUrl, preload: "metadata", children: "Tr\u00ECnh duy\u1EC7t kh\u00F4ng h\u1ED7 tr\u1EE3 audio." })] })), _jsx("div", { className: "rounded-xl bg-gradient-to-r from-orange-50/80 to-amber-50/50 p-5 border border-orange-100/80", children: renderQuestionWithBlanks() }), renderQuestionImage(), renderHints(), isReviewMode && (_jsxs("div", { className: "rounded-xl border border-gray-200 bg-white overflow-hidden shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-gray-100 bg-gradient-to-r from-slate-50 to-gray-50 px-4 py-2.5", children: [_jsx("div", { className: "flex h-5 w-5 items-center justify-center rounded-full bg-slate-500 text-white", children: _jsx(ClipboardList, { className: "h-3 w-3" }) }), _jsx("h4", { className: "text-xs font-semibold text-gray-600 uppercase tracking-wider", children: "Chi ti\u1EBFt \u0111\u00E1p \u00E1n" })] }), _jsx("div", { className: "p-4 space-y-2.5", children: placeholderIndices.map((idx) => {
215
+ : 'bg-red-100 text-red-700 ring-1 ring-red-200/60'), children: [score.correct === score.total && _jsx(Check, { className: "h-3 w-3" }), score.correct, "/", score.total, " \u0111\u00FAng"] }))] }), _jsxs("div", { className: "space-y-3 p-3", children: [showAudio && cleanAudioUrl && (_jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-indigo-100 bg-gradient-to-r from-indigo-50 to-blue-50 p-3", children: [_jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-indigo-100", children: _jsx(Volume2, { className: "h-4 w-4 text-indigo-600" }) }), _jsx("audio", { controls: true, className: "h-9 w-full", src: audioPreviewUrl || cleanAudioUrl, preload: "metadata", children: "Tr\u00ECnh duy\u1EC7t kh\u00F4ng h\u1ED7 tr\u1EE3 audio." })] })), _jsx("div", { className: "rounded-xl bg-gradient-to-r from-orange-50/80 to-amber-50/50 p-5 border border-orange-100/80", children: renderQuestionWithBlanks() }), renderQuestionImage(), renderHints(), isReviewMode && (_jsxs("div", { className: "rounded-xl border border-gray-200 bg-white overflow-hidden shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-gray-100 bg-gradient-to-r from-slate-50 to-gray-50 px-4 py-2.5", children: [_jsx("div", { className: "flex h-5 w-5 items-center justify-center rounded-full bg-slate-500 text-white", children: _jsx(ClipboardList, { className: "h-3 w-3" }) }), _jsx("h4", { className: "text-xs font-semibold text-gray-600 uppercase tracking-wider", children: "Chi ti\u1EBFt \u0111\u00E1p \u00E1n" })] }), _jsx("div", { className: "p-4 space-y-2.5", children: placeholderIndices.map((idx) => {
216
216
  const userInput = answers[idx] || '';
217
217
  const correctAnswer = formatBlankAnswersDisplay(correctAnswersMatrix[idx]) || '—';
218
218
  const isCorrect = isAnswerCorrect(idx, userInput);
@@ -231,9 +231,9 @@ export function FillInBlankClient({ questionData, questionConfig, onSubmit, isRe
231
231
  const userInput = answers[index] || '';
232
232
  const isCorrect = isReviewMode && isAnswerCorrect(index, userInput);
233
233
  const hasAnswered = userInput.trim() !== '';
234
- return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm transition-all hover:shadow-md", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-5 py-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-orange-500 to-amber-600 text-white shadow-sm", children: _jsx(PenLine, { className: "h-4 w-4" }) }), _jsxs("span", { className: "text-sm font-semibold text-gray-700", children: ["C\u00E2u ", index + 1, ": \u0110i\u1EC1n v\u00E0o ch\u1ED7 tr\u1ED1ng"] })] }), isReviewMode && hasAnswered && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-bold', isCorrect
234
+ return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm transition-all hover:shadow-md", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gradient-to-br from-orange-500 to-amber-600 text-white", children: _jsx(PenLine, { className: "h-3.5 w-3.5" }) }), _jsxs("span", { className: "text-sm font-semibold text-gray-700", children: ["C\u00E2u ", index + 1, ": \u0110i\u1EC1n v\u00E0o ch\u1ED7 tr\u1ED1ng"] })] }), isReviewMode && hasAnswered && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-bold', isCorrect
235
235
  ? 'bg-emerald-100 text-emerald-700 ring-1 ring-emerald-200/60'
236
- : 'bg-red-100 text-red-700 ring-1 ring-red-200/60'), children: [isCorrect ? _jsx(Check, { className: "h-3.5 w-3.5" }) : _jsx(X, { className: "h-3.5 w-3.5" }), isCorrect ? 'Đúng' : 'Sai'] }))] }), _jsxs("div", { className: "p-5 space-y-4", children: [index === 0 && showAudio && cleanAudioUrl && (_jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-indigo-100 bg-gradient-to-r from-indigo-50 to-blue-50 p-3", children: [_jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-indigo-100", children: _jsx(Volume2, { className: "h-4 w-4 text-indigo-600" }) }), _jsx("audio", { controls: true, className: "h-9 w-full", src: audioPreviewUrl || cleanAudioUrl, preload: "metadata", children: "Tr\u00ECnh duy\u1EC7t kh\u00F4ng h\u1ED7 tr\u1EE3 audio." })] })), _jsx("div", { className: "rounded-xl bg-gradient-to-r from-orange-50/80 to-amber-50/50 p-5 border border-orange-100/80", children: renderLegacyBlank(blank.text, blank.id, index) }), index === 0 && renderQuestionImage(), index === 0 && renderHints(), isReviewMode && (_jsx("div", { className: cn('rounded-xl border p-3 transition-colors', isCorrect
236
+ : 'bg-red-100 text-red-700 ring-1 ring-red-200/60'), children: [isCorrect ? _jsx(Check, { className: "h-3.5 w-3.5" }) : _jsx(X, { className: "h-3.5 w-3.5" }), isCorrect ? 'Đúng' : 'Sai'] }))] }), _jsxs("div", { className: "space-y-3 p-3", children: [index === 0 && showAudio && cleanAudioUrl && (_jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-indigo-100 bg-gradient-to-r from-indigo-50 to-blue-50 p-3", children: [_jsx("div", { className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg bg-indigo-100", children: _jsx(Volume2, { className: "h-4 w-4 text-indigo-600" }) }), _jsx("audio", { controls: true, className: "h-9 w-full", src: audioPreviewUrl || cleanAudioUrl, preload: "metadata", children: "Tr\u00ECnh duy\u1EC7t kh\u00F4ng h\u1ED7 tr\u1EE3 audio." })] })), _jsx("div", { className: "rounded-xl bg-gradient-to-r from-orange-50/80 to-amber-50/50 p-5 border border-orange-100/80", children: renderLegacyBlank(blank.text, blank.id, index) }), index === 0 && renderQuestionImage(), index === 0 && renderHints(), isReviewMode && (_jsx("div", { className: cn('rounded-xl border p-3 transition-colors', isCorrect
237
237
  ? 'border-emerald-200 bg-emerald-50/50'
238
238
  : hasAnswered
239
239
  ? 'border-red-200 bg-red-50/50'
@@ -6,10 +6,9 @@ import { Button } from '../../../../components/ui/button';
6
6
  import { Label } from '../../../../components/ui/label';
7
7
  import { Input } from '../../../../components/ui/input';
8
8
  import { Textarea } from '../../../../components/ui/textarea';
9
- import { PointsInput } from '../../../../components/ui/points-input';
10
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
11
9
  import { Switch } from '../../../../components/ui/switch';
12
- import { Pencil, Eye, Lightbulb, Type, MessageSquare, Plus, Trash2, ListChecks, ImageIcon, Volume2, BookOpen } from 'lucide-react';
10
+ import { Eye, Type, MessageSquare, Plus, Trash2, ListChecks, ImageIcon, Volume2 } from 'lucide-react';
11
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
13
12
  import { FillInBlankClient } from './FillInBlankClient';
14
13
  import { blankHasAnswer, compactFillInBlankAnswers, formatBlankAnswersDisplay, extractPlaceholderIndices, getMaxPlaceholderIndex, normalizeFillInBlankAnswers, } from './answer-utils';
15
14
  import { useDebouncedCallback, usePresignedFileUrl } from '../../../../shared/lib/hooks';
@@ -151,6 +150,8 @@ function FillInBlankCreatorContent({ initialData, onChange, externalErrors, vali
151
150
  // UI State
152
151
  const [isClientMode, setIsClientMode] = useState(false);
153
152
  const [imagePreviewUrl, setImagePreviewUrl] = useState('');
153
+ const [isImageInputVisible, setIsImageInputVisible] = useState(() => Boolean(initialData?.content?.imageUrl || initialData?.imageUrl));
154
+ const [isAudioInputVisible, setIsAudioInputVisible] = useState(() => Boolean(initialData?.audioUrl || initialData?.content?.audioUrl));
154
155
  // Presigned URL for per-question image preview (non-grouped only)
155
156
  const { previewUrl: generatedPreviewUrl } = usePresignedFileUrl(imageUrl);
156
157
  const displayPreviewUrl = imagePreviewUrl || generatedPreviewUrl || '';
@@ -418,42 +419,49 @@ function FillInBlankCreatorContent({ initialData, onChange, externalErrors, vali
418
419
  groupNumber: isGrouped ? currentGroupNumber : undefined,
419
420
  ...(isGrouped && isImageLabelGroup ? { imageLabels } : {}),
420
421
  };
421
- return (_jsxs("div", { className: "space-y-4", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(CardTitle, { children: "Fill In Blank (Preview)" }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(false), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), "Quay l\u1EA1i Edit Mode"] })] }) }) }), _jsx(FillInBlankClient, { questionData: questionData, questionConfig: questionConfig, autoFillCorrectAnswers: true, isReviewMode: false, explanation: explanation })] }));
422
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(FillInBlankClient, { questionData: questionData, questionConfig: questionConfig, autoFillCorrectAnswers: true, isReviewMode: false, explanation: explanation })] }));
422
423
  }
423
424
  // ============ CREATOR UI ============
424
- return (_jsxs("div", { className: "space-y-4", children: [isGrouped && isImageLabelGroup && (_jsx(ImageLabelGroupCard, { groupNumber: currentGroupNumber, imageLabels: imageLabels, onImageLabelsChange: setImageLabels, title: groupTitle, onTitleChange: setGroupTitle, isReadOnly: !isFirstInGroup, uploadIdPrefix: `fill-in-blank-${partId ?? 'image-label'}` })), isGrouped && !isImageLabelGroup && (_jsx(BasicQuestionGroupCard, { groupNumber: currentGroupNumber, title: groupTitle, subtitle: groupSubtitle, content: groupContent, imageUrl: groupImageUrl, onTitleChange: setGroupTitle, onSubtitleChange: setGroupSubtitle, onContentChange: setGroupContent, onImageChange: setGroupImageUrl, uploadIdPrefix: `fill-in-blank-${partId ?? 'group'}` })), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50", children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 shadow-sm", children: _jsx(Type, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-base font-bold text-gray-900", children: "\u0110i\u1EC1n v\u00E0o ch\u1ED7 tr\u1ED1ng" }), _jsx("p", { className: "text-xs text-gray-500", children: "H\u1ED7 tr\u1EE3 nhi\u1EC1u ch\u1ED7 tr\u1ED1ng trong m\u1ED9t c\u00E2u h\u1ECFi" })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [headerExtra, _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: !question?.trim() || placeholderIndices.length === 0, className: "gap-2 border-indigo-200 text-indigo-600 hover:bg-indigo-50 hover:text-indigo-700", children: [_jsx(Eye, { className: "h-4 w-4" }), "Xem tr\u01B0\u1EDBc"] })] })] }) }), _jsxs(CardContent, { className: "space-y-4 px-4 pb-4", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-6", children: [_jsx(PointsInput, { value: points, onChange: (e) => setPoints(parseFloat(e.target.value) || 1) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: "show-suggested-words", checked: showHints, onCheckedChange: setShowHints }), _jsx(Label, { htmlFor: "show-suggested-words", className: "cursor-pointer text-sm font-medium text-gray-700", children: "Hi\u1EC3n th\u1ECB t\u1EEB g\u1EE3i \u00FD" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: "isExample", checked: isExample, onCheckedChange: setIsExample }), _jsx(Label, { htmlFor: "isExample", className: "cursor-pointer text-sm font-medium text-gray-700", children: "\u0110\u00E1nh d\u1EA5u l\u00E0 c\u00E2u h\u1ECFi Example" })] })] }), _jsx("div", { className: "rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-5 shadow-sm", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-blue-100", children: _jsx(Lightbulb, { className: "h-4 w-4 text-blue-600" }) }), _jsxs("div", { className: "text-sm text-blue-900", children: [_jsx("p", { className: "font-semibold text-blue-800", children: "H\u01B0\u1EDBng d\u1EABn t\u1EA1o c\u00E2u h\u1ECFi" }), _jsxs("ul", { className: "mt-2 space-y-1.5 text-blue-700", children: [_jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-blue-400" }), "S\u1EED d\u1EE5ng ", _jsx("code", { className: "mx-1 rounded bg-blue-100 px-1.5 py-0.5 font-mono text-blue-800", children: '{0}' }), ", ", _jsx("code", { className: "mx-1 rounded bg-blue-100 px-1.5 py-0.5 font-mono text-blue-800", children: '{1}' }), ", ", _jsx("code", { className: "mx-1 rounded bg-blue-100 px-1.5 py-0.5 font-mono text-blue-800", children: '{2}' }), "... \u0111\u1EC3 \u0111\u00E1nh d\u1EA5u c\u00E1c ch\u1ED7 tr\u1ED1ng"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-blue-400" }), "H\u1EC7 th\u1ED1ng s\u1EBD t\u1EF1 \u0111\u1ED9ng t\u1EA1o \u00F4 nh\u1EADp \u0111\u00E1p \u00E1n cho m\u1ED7i ch\u1ED7 tr\u1ED1ng"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-blue-400" }), "V\u00ED d\u1EE5: \u201CHe ", '{0}', " 10 years old and she ", '{1}', " 12.\u201D"] })] })] })] }) }), !isGrouped && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-indigo-500" }), "H\u00ECnh \u1EA3nh ", _jsx("span", { className: "text-sm font-normal text-gray-500", children: "(T\u00F9y ch\u1ECDn)" })] }), _jsx(FileUpload, { id: "fill-in-blank-image", label: "", accept: "image/*", value: imageUrl || '', onChange: (url) => {
425
- setImageUrl(url);
426
- }, onPresignedUrlChange: setImagePreviewUrl, maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" }), displayPreviewUrl && (_jsx(ImagePreview, { src: displayPreviewUrl, alt: "Preview", className: "mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200" }))] })), includesAudio && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(Volume2, { className: "h-4 w-4 text-indigo-500" }), "Audio c\u00E2u h\u1ECFi"] }), _jsx(FileUpload, { id: "fill-in-blank-audio", label: "", accept: "audio/*", value: audioUrl || '', onChange: (url) => {
427
- setAudioUrl(url);
428
- }, maxSize: 20, placeholder: "Upload file audio", autoUpload: true, prefix: "questions", showPlayButton: true })] })), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "question", className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-4 w-4 text-indigo-500" }), "C\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Textarea, { id: "question", value: question, onChange: (e) => {
429
- setQuestion(e.target.value);
430
- if (validationErrors.question) {
431
- setValidationErrors(prev => ({ ...prev, question: undefined }));
432
- }
433
- }, placeholder: "V\u00ED d\u1EE5: He {0} 10 years old and she {1} 12.", rows: 3, className: `min-h-[100px] border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${validationErrors.question ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.question && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.question })), placeholderIndices.length > 0 && (_jsx("div", { className: "flex items-center gap-2 rounded-lg bg-green-50 px-3 py-2", children: _jsxs("span", { className: "text-sm text-green-700", children: ["\u2713 \u0110\u00E3 ph\u00E1t hi\u1EC7n ", _jsx("strong", { children: placeholderIndices.length }), " ch\u1ED7 tr\u1ED1ng: ", placeholderIndices.map(i => (_jsx("code", { className: "mx-0.5 rounded bg-green-100 px-1.5 py-0.5 font-mono text-green-800", children: `{${i}}` }, i)))] }) }))] }), _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100", children: _jsx(Type, { className: "h-3.5 w-3.5 text-indigo-600" }) }), _jsxs(Label, { className: "text-base font-semibold text-gray-800", children: ["\u0110\u00E1p \u00E1n cho c\u00E1c ch\u1ED7 tr\u1ED1ng ", _jsx("span", { className: "text-red-500", children: "*" })] })] }), _jsxs("div", { className: "grid gap-6 lg:grid-cols-2", children: [_jsx("div", { className: "space-y-3", children: (placeholderIndices.length > 0 ? placeholderIndices : [0]).map((idx) => {
434
- const blankAlts = answers[idx]?.length ? answers[idx] : [''];
435
- const hasAnswer = blankHasAnswer(blankAlts);
436
- return (_jsxs("div", { className: `rounded-xl border-2 p-4 transition-all ${hasAnswer
437
- ? 'border-green-200 bg-gradient-to-r from-green-50 to-emerald-50'
438
- : 'border-gray-200 bg-white'}`, children: [_jsxs("div", { className: "mb-3 flex items-center gap-3", children: [_jsx("div", { className: `flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl font-mono font-bold transition-all ${hasAnswer
439
- ? 'bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md'
440
- : 'bg-indigo-100 text-indigo-700'}`, children: `{${idx}}` }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("p", { className: "text-sm font-medium text-gray-700", children: ["Ch\u1ED7 tr\u1ED1ng ", idx + 1] }), _jsx("p", { className: "text-xs text-gray-500", children: "C\u00F3 th\u1EC3 th\u00EAm nhi\u1EC1u \u0111\u00E1p \u00E1n \u0111\u00FAng t\u01B0\u01A1ng \u0111\u01B0\u01A1ng" })] })] }), _jsx("div", { className: "space-y-2", children: blankAlts.map((alt, altIdx) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { value: alt, onChange: (e) => updateBlankAnswer(idx, altIdx, e.target.value), placeholder: altIdx === 0 ? `Đáp án chính cho {${idx}}` : `Đáp án thay thế ${altIdx + 1}`, className: `border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${validationErrors.answers?.[idx] ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), blankAlts.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeBlankAlternative(idx, altIdx), className: "h-9 w-9 flex-shrink-0 text-red-500 hover:bg-red-50 hover:text-red-600", title: "X\u00F3a \u0111\u00E1p \u00E1n n\u00E0y", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }, altIdx))) }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => addBlankAlternative(idx), className: "mt-2 gap-1.5 border-dashed border-indigo-200 text-indigo-600 hover:bg-indigo-50", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm \u0111\u00E1p \u00E1n \u0111\u00FAng"] }), validationErrors.answers?.[idx] && (_jsx("p", { className: "mt-2 text-sm text-red-600", children: validationErrors.answers[idx] }))] }, idx));
441
- }) }), showHints && (_jsxs("div", { className: "space-y-4 rounded-xl border-2 border-purple-200 bg-gradient-to-r from-purple-50 to-indigo-50 p-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-purple-100", children: _jsx(ListChecks, { className: "h-3.5 w-3.5 text-purple-600" }) }), _jsxs(Label, { className: "text-base font-semibold text-gray-800", children: ["Danh s\u00E1ch t\u1EEB g\u1EE3i \u00FD ", _jsxs("span", { className: "text-sm font-normal text-gray-500", children: ["(", hints.length, " t\u1EEB)"] })] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { value: newHint, onChange: (e) => setNewHint(e.target.value), placeholder: "Nh\u1EADp t\u1EEB g\u1EE3i \u00FD m\u1EDBi...", className: "flex-1 border-gray-200 bg-white", onKeyDown: (e) => {
442
- if (e.key === 'Enter' && newHint.trim()) {
443
- e.preventDefault();
444
- if (!hints.includes(newHint.trim())) {
445
- setHints([...hints, newHint.trim()]);
446
- }
447
- setNewHint('');
448
- }
449
- } }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => {
450
- if (newHint.trim() && !hints.includes(newHint.trim())) {
451
- setHints([...hints, newHint.trim()]);
452
- setNewHint('');
453
- }
454
- }, disabled: !newHint.trim(), className: "gap-2 border-green-200 text-green-600 hover:bg-green-50 hover:text-green-700", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm"] })] }), hints.length > 0 ? (_jsx("div", { className: "flex flex-wrap gap-2", children: hints.map((word, index) => (_jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-purple-200 bg-white px-3 py-2 shadow-sm transition-all hover:border-purple-300 hover:shadow", children: [_jsx("span", { className: "text-sm font-medium text-gray-700", children: word }), _jsx("button", { type: "button", onClick: () => {
455
- setHints(hints.filter((_, i) => i !== index));
456
- }, className: "rounded-full p-0.5 text-gray-400 transition-colors hover:bg-red-100 hover:text-red-500", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }, index))) })) : (_jsx("p", { className: "text-center text-sm text-gray-500 italic", children: "Ch\u01B0a c\u00F3 t\u1EEB g\u1EE3i \u00FD n\u00E0o. Th\u00EAm t\u1EEB \u0111\u1EC3 hi\u1EC3n th\u1ECB cho h\u1ECDc sinh." }))] }))] })] }), question && (_jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2 text-xs font-semibold text-gray-600", children: [_jsx(Eye, { className: "h-3.5 w-3.5" }), "Xem tr\u01B0\u1EDBc c\u00E2u ho\u00E0n ch\u1EC9nh"] }), _jsx("p", { className: "text-sm text-gray-700", children: getPreviewText() })] })), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100", children: _jsx("span", { className: "text-lg", children: "\u274C" }) }), _jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("ul", { className: "mt-1 list-inside list-disc text-red-700", children: externalErrors.map((error, index) => (_jsx("li", { children: error }, index))) })] })] }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50", children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm", children: _jsx(BookOpen, { className: "h-3.5 w-3.5 text-white" }) }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-base font-semibold text-gray-800", children: "Gi\u1EA3i th\u00EDch" }), _jsx("span", { className: "inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-600", children: "Kh\u00F4ng b\u1EAFt bu\u1ED9c" })] }), _jsx("p", { className: "text-xs text-gray-500 mt-0.5", children: "Gi\u1EA3i th\u00EDch cho \u0111\u00E1p \u00E1n \u0111\u00FAng \u2014 hi\u1EC3n th\u1ECB sau khi h\u1ECDc sinh ho\u00E0n th\u00E0nh c\u00E2u h\u1ECFi" })] })] }) }), _jsx(CardContent, { className: "px-4 pb-4", children: _jsx(Textarea, { id: "explanation", value: explanation, onChange: (e) => setExplanation(e.target.value), placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0... v\u00EC...", rows: 3, className: "min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none" }) })] })] }));
425
+ return (_jsxs("div", { className: "space-y-3", children: [isGrouped && isImageLabelGroup && (_jsx(ImageLabelGroupCard, { groupNumber: currentGroupNumber, imageLabels: imageLabels, onImageLabelsChange: setImageLabels, title: groupTitle, onTitleChange: setGroupTitle, isReadOnly: !isFirstInGroup, uploadIdPrefix: `fill-in-blank-${partId ?? 'image-label'}` })), isGrouped && !isImageLabelGroup && (_jsx(BasicQuestionGroupCard, { groupNumber: currentGroupNumber, title: groupTitle, subtitle: groupSubtitle, content: groupContent, imageUrl: groupImageUrl, onTitleChange: setGroupTitle, onSubtitleChange: setGroupSubtitle, onContentChange: setGroupContent, onImageChange: setGroupImageUrl, uploadIdPrefix: `fill-in-blank-${partId ?? 'group'}` })), _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Type, { className: "h-3.5 w-3.5" }), title: "\u0110i\u1EC1n v\u00E0o ch\u1ED7 tr\u1ED1ng", points: { value: points, onValueChange: setPoints }, example: { checked: isExample, onChange: setIsExample }, extraActions: _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(Switch, { id: "show-suggested-words", checked: showHints, onCheckedChange: setShowHints }), _jsx(Label, { htmlFor: "show-suggested-words", className: "cursor-pointer text-xs font-medium text-gray-700", children: "T\u1EEB g\u1EE3i \u00FD" })] }), headerExtra: headerExtra, preview: {
426
+ onClick: () => setIsClientMode(true),
427
+ disabled: !question?.trim() || placeholderIndices.length === 0,
428
+ } }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs(Label, { htmlFor: "question", className: "flex items-center gap-1.5 text-sm font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-3.5 w-3.5 text-indigo-500" }), "C\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsxs("div", { className: "flex items-center gap-1", children: [!isGrouped && (_jsx("button", { type: "button", title: "Th\u00EAm h\u00ECnh \u1EA3nh", onClick: () => setIsImageInputVisible(true), className: `inline-flex h-8 w-8 items-center justify-center rounded-md transition-colors ${imageUrl || isImageInputVisible
429
+ ? 'bg-indigo-600 text-white shadow-sm'
430
+ : 'bg-indigo-50 text-indigo-600 hover:bg-indigo-100'}`, children: _jsx(ImageIcon, { className: "h-4 w-4" }) })), includesAudio && (_jsx("button", { type: "button", title: "Th\u00EAm audio", onClick: () => setIsAudioInputVisible(true), className: `inline-flex h-8 w-8 items-center justify-center rounded-md transition-colors ${audioUrl || isAudioInputVisible
431
+ ? 'bg-violet-600 text-white shadow-sm'
432
+ : 'bg-violet-50 text-violet-600 hover:bg-violet-100'}`, children: _jsx(Volume2, { className: "h-4 w-4" }) }))] })] }), _jsx(Textarea, { id: "question", value: question, onChange: (e) => {
433
+ setQuestion(e.target.value);
434
+ if (validationErrors.question) {
435
+ setValidationErrors(prev => ({ ...prev, question: undefined }));
436
+ }
437
+ }, placeholder: "V\u00ED d\u1EE5: He {0} 10 years old and she {1} 12.", rows: 3, className: `min-h-[72px] border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${validationErrors.question ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.question && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.question })), placeholderIndices.length > 0 && (_jsx("div", { className: "flex items-center gap-2 rounded-md bg-green-50 px-2.5 py-1.5", children: _jsxs("span", { className: "text-xs text-green-700", children: ["\u2713 ", _jsx("strong", { children: placeholderIndices.length }), " ch\u1ED7 tr\u1ED1ng: ", placeholderIndices.map(i => (_jsx("code", { className: "mx-0.5 rounded bg-green-100 px-1.5 py-0.5 font-mono text-green-800", children: `{${i}}` }, i)))] }) }))] }), !isGrouped && isImageInputVisible && (_jsxs("div", { className: "space-y-2", children: [_jsx(FileUpload, { id: "fill-in-blank-image", label: "", accept: "image/*", value: imageUrl || '', onChange: (url) => {
438
+ setImageUrl(url);
439
+ }, onPresignedUrlChange: setImagePreviewUrl, maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" }), displayPreviewUrl && (_jsx(ImagePreview, { src: displayPreviewUrl, alt: "Preview", className: "mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200" }))] })), includesAudio && isAudioInputVisible && (_jsx(FileUpload, { id: "fill-in-blank-audio", label: "", accept: "audio/*", value: audioUrl || '', onChange: (url) => {
440
+ setAudioUrl(url);
441
+ }, maxSize: 20, placeholder: "Upload file audio", autoUpload: true, prefix: "questions", showPlayButton: true })), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100", children: _jsx(Type, { className: "h-3.5 w-3.5 text-indigo-600" }) }), _jsxs(Label, { className: "text-sm font-semibold text-gray-800", children: ["\u0110\u00E1p \u00E1n cho c\u00E1c ch\u1ED7 tr\u1ED1ng ", _jsx("span", { className: "text-red-500", children: "*" })] })] }), _jsxs("div", { className: "grid gap-3 lg:grid-cols-2", children: [_jsx("div", { className: "space-y-2", children: (placeholderIndices.length > 0 ? placeholderIndices : [0]).map((idx) => {
442
+ const blankAlts = answers[idx]?.length ? answers[idx] : [''];
443
+ const hasAnswer = blankHasAnswer(blankAlts);
444
+ return (_jsxs("div", { className: `rounded-md border p-2.5 transition-all ${hasAnswer
445
+ ? 'border-green-200 bg-gradient-to-r from-green-50 to-emerald-50'
446
+ : 'border-gray-200 bg-white'}`, children: [_jsxs("div", { className: "mb-2 flex items-center gap-2", children: [_jsx("div", { className: `flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md font-mono text-xs font-bold ${hasAnswer
447
+ ? 'bg-gradient-to-br from-green-500 to-emerald-600 text-white'
448
+ : 'bg-indigo-100 text-indigo-700'}`, children: `{${idx}}` }), _jsxs("p", { className: "text-xs font-medium text-gray-700", children: ["Ch\u1ED7 tr\u1ED1ng ", idx + 1] })] }), _jsx("div", { className: "space-y-1.5", children: blankAlts.map((alt, altIdx) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { value: alt, onChange: (e) => updateBlankAnswer(idx, altIdx, e.target.value), placeholder: altIdx === 0 ? `Đáp án chính cho {${idx}}` : `Đáp án thay thế ${altIdx + 1}`, className: `h-8 border-gray-200 bg-white ${validationErrors.answers?.[idx] ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), blankAlts.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeBlankAlternative(idx, altIdx), className: "h-7 w-7 flex-shrink-0 text-red-500 hover:bg-red-50 hover:text-red-600", title: "X\u00F3a \u0111\u00E1p \u00E1n n\u00E0y", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }, altIdx))) }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => addBlankAlternative(idx), className: "mt-2 h-8 gap-1.5 border-dashed border-indigo-200 px-2.5 text-xs text-indigo-600 hover:bg-indigo-50", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm \u0111\u00E1p \u00E1n \u0111\u00FAng"] }), validationErrors.answers?.[idx] && (_jsx("p", { className: "mt-2 text-sm text-red-600", children: validationErrors.answers[idx] }))] }, idx));
449
+ }) }), showHints && (_jsxs("div", { className: "space-y-2.5 rounded-md border border-purple-200 bg-purple-50/70 p-2.5", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-purple-100", children: _jsx(ListChecks, { className: "h-3.5 w-3.5 text-purple-600" }) }), _jsxs(Label, { className: "text-sm font-semibold text-gray-800", children: ["T\u1EEB g\u1EE3i \u00FD ", _jsxs("span", { className: "text-xs font-normal text-gray-500", children: ["(", hints.length, " t\u1EEB)"] })] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { value: newHint, onChange: (e) => setNewHint(e.target.value), placeholder: "Nh\u1EADp t\u1EEB g\u1EE3i \u00FD m\u1EDBi...", className: "h-8 flex-1 border-gray-200 bg-white", onKeyDown: (e) => {
450
+ if (e.key === 'Enter' && newHint.trim()) {
451
+ e.preventDefault();
452
+ if (!hints.includes(newHint.trim())) {
453
+ setHints([...hints, newHint.trim()]);
454
+ }
455
+ setNewHint('');
456
+ }
457
+ } }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => {
458
+ if (newHint.trim() && !hints.includes(newHint.trim())) {
459
+ setHints([...hints, newHint.trim()]);
460
+ setNewHint('');
461
+ }
462
+ }, disabled: !newHint.trim(), className: "h-8 gap-1.5 border-green-200 px-2.5 text-xs text-green-600 hover:bg-green-50 hover:text-green-700", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm"] })] }), hints.length > 0 ? (_jsx("div", { className: "flex flex-wrap gap-1.5", children: hints.map((word, index) => (_jsxs("div", { className: "flex items-center gap-1.5 rounded-md border border-purple-200 bg-white px-2 py-1", children: [_jsx("span", { className: "text-xs font-medium text-gray-700", children: word }), _jsx("button", { type: "button", onClick: () => {
463
+ setHints(hints.filter((_, i) => i !== index));
464
+ }, className: "rounded-full p-0.5 text-gray-400 transition-colors hover:bg-red-100 hover:text-red-500", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }, index))) })) : (_jsx("p", { className: "text-center text-xs text-gray-500 italic", children: "Ch\u01B0a c\u00F3 t\u1EEB g\u1EE3i \u00FD n\u00E0o. Th\u00EAm t\u1EEB \u0111\u1EC3 hi\u1EC3n th\u1ECB cho h\u1ECDc sinh." }))] }))] })] }), question && (_jsxs("div", { className: "rounded-md border border-gray-200 bg-gray-50 p-2.5", children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 text-xs font-semibold text-gray-600", children: [_jsx(Eye, { className: "h-3.5 w-3.5" }), "Xem tr\u01B0\u1EDBc c\u00E2u ho\u00E0n ch\u1EC9nh"] }), _jsx("p", { className: "text-sm text-gray-700", children: getPreviewText() })] })), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-red-200 bg-red-50 p-2 text-xs text-red-700", children: [_jsx("span", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("span", { children: externalErrors.join(' · ') })] })), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] }));
457
465
  }
458
466
  export function FillInBlankCreator(props) {
459
467
  return _jsx(FillInBlankCreatorContent, { ...props });
@@ -10,7 +10,8 @@ import { Card, CardContent, CardHeader, CardTitle } from '../../../../components
10
10
  import { Switch } from '../../../../components/ui/switch';
11
11
  import { RichTextEditor } from '../../../../components/shared/RichTextEditor';
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 { FillInBlankGroupClient } from './FillInBlankGroupClient';
15
16
  import { FillInBlankGroupMediaField } from './FillInBlankGroupMediaField';
16
17
  import { createEmptyFillInBlankGroupItem } from './map-fill-in-blank-group-data';
@@ -168,23 +169,26 @@ export function FillInBlankGroupCreator({ initialData, onChange, externalErrors,
168
169
  };
169
170
  const currentPayload = buildPayload();
170
171
  const displayErrors = externalErrors && externalErrors.length > 0 ? externalErrors : errors;
171
- 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 \u0110i\u1EC1n ch\u1ED7 tr\u1ED1ng (Nh\u00F3m)" }), _jsxs("p", { className: "text-sm text-gray-500", children: ["M\u1ED7i d\u00F2ng l\u00E0 1 stem c\u00F3 ", '{0}', ". C\u00F3 th\u1EC3 th\u00EAm audio/\u1EA3nh chung ho\u1EB7c t\u1EEBng item."] })] }), _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(FillInBlankGroupClient, { 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 v\u00E0 media chung" }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { 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: FILL_IN_BLANK_GROUP_DEFAULT_INSTRUCTION, className: "mt-1" })] }), _jsxs("div", { children: [_jsx(Label, { children: "N\u1ED9i dung b\u00E0i \u0111\u1ECDc (Passage)" }), _jsx("div", { className: "mt-1", children: _jsx(RichTextEditor, { value: passage, onChange: setPassage, minHeightClassName: "min-h-[160px]" }) })] }), _jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [_jsx(FillInBlankGroupMediaField, { id: "fib-group-audio", label: "Audio chung (optional)", accept: "audio/*", value: audioUrl, onChange: setAudioUrl }), _jsx(FillInBlankGroupMediaField, { id: "fib-group-image", label: "\u1EA2nh chung (optional)", accept: "image/*", value: toFillInBlankImageUrls(imageUrl)[0] || '', onChange: setImageUrl })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: "show-hints", checked: showHints, onCheckedChange: setShowHints }), _jsx(Label, { htmlFor: "show-hints", children: "Hi\u1EC7n h\u1ED9p t\u1EEB g\u1EE3i \u00FD chung" })] }), showHints && (_jsx(Input, { value: hintsText, onChange: (e) => setHintsText(e.target.value), placeholder: "am / is / are" }))] })] }), _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) => (_jsxs("div", { className: "space-y-3 rounded-lg border border-gray-200 bg-gray-50/50 p-4", children: [_jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsxs("span", { className: "flex items-center gap-2 text-sm font-medium text-gray-700", 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 }), "C\u00E2u ", index + 1] }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: `example-${index}`, checked: item.isExample || false, onCheckedChange: (checked) => updateItem(index, { isExample: checked, points: checked ? 0 : 1 }) }), _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, {
172
- points: parsePointsValue(event.target.value, true),
173
- }) }) })), items.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeItem(index), className: "text-red-500 hover:bg-red-50", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] })] }), _jsxs("div", { children: [_jsxs(Label, { htmlFor: `question-${index}`, children: ["Stem (d\u00F9ng ", '{0}', ", ", '{1}', "...)"] }), _jsx(Textarea, { id: `question-${index}`, value: item.question, onChange: (e) => updateItem(index, { question: e.target.value }), placeholder: "{0}", className: "mt-1" })] }), extractPlaceholderIndices(item.question).map((blankIndex) => (_jsxs("div", { className: "grid gap-3 md:grid-cols-2", children: [_jsxs("div", { children: [_jsxs(Label, { htmlFor: `answer-${index}-${blankIndex}`, children: ["\u0110\u00E1p \u00E1n blank ", '{' + blankIndex + '}', " (c\u00E1ch nhau b\u1EDFi /)"] }), _jsx(Input, { id: `answer-${index}-${blankIndex}`, value: joinSlashAnswers(item.answers[blankIndex]), onChange: (e) => {
174
- const next = [...item.answers];
175
- next[blankIndex] = parseSlashAnswers(e.target.value);
176
- updateItem(index, { answers: next });
177
- }, placeholder: "is / 's", className: "mt-1" })] }), _jsxs("div", { children: [_jsxs(Label, { htmlFor: `options-${index}-${blankIndex}`, children: ["Options blank ", '{' + blankIndex + '}', " (dropdown, c\u00E1ch nhau /)"] }), _jsx(Input, { id: `options-${index}-${blankIndex}`, value: joinSlashAnswers(item.wordBank?.[blankIndex]?.options), onChange: (e) => {
178
- const requiredLength = blankSlotCount(item.question);
179
- const next = [...(item.wordBank || [])];
180
- while (next.length < requiredLength)
181
- next.push(null);
182
- const options = parseSlashAnswers(e.target.value);
183
- next[blankIndex] = options.length >= 2 ? { options } : null;
184
- updateItem(index, {
185
- wordBank: next.some((entry) => entry && entry.options.length >= 2)
186
- ? next
187
- : undefined,
188
- });
189
- }, placeholder: "can / can't", className: "mt-1" })] })] }, blankIndex))), _jsxs("div", { className: "grid gap-4 md:grid-cols-2", children: [_jsx(FillInBlankGroupMediaField, { id: `fib-item-image-${index}`, label: "\u1EA2nh item (optional)", accept: "image/*", value: Array.isArray(item.imageUrl) ? (item.imageUrl[0] || '') : (item.imageUrl || ''), onChange: (url) => updateItem(index, { imageUrl: url }) }), !audioUrl && (_jsx(FillInBlankGroupMediaField, { id: `fib-item-audio-${index}`, label: "Audio item (khi kh\u00F4ng c\u00F3 audio nh\u00F3m)", accept: "audio/*", value: item.audioUrl || '', onChange: (url) => updateItem(index, { audioUrl: url }) }))] })] }, index))) })] }), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-base", children: "Gi\u1EA3i th\u00EDch" }) }), _jsx(CardContent, { children: _jsx(Textarea, { id: "explanation", value: explanation, onChange: (event) => setExplanation(event.target.value), placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0... v\u00EC...", rows: 3 }) })] })] }))] }));
172
+ if (isClientMode) {
173
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(FillInBlankGroupClient, { questionData: currentPayload })] }));
174
+ }
175
+ return (_jsxs("div", { className: "space-y-3", children: [displayErrors.length > 0 && (_jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-2 text-xs text-red-700", children: _jsx("ul", { className: "list-disc space-y-1 pl-4", children: displayErrors.map((err, i) => (_jsx("li", { children: err }, i))) }) })), _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(HelpCircle, { className: "h-3.5 w-3.5" }), title: "\u0110i\u1EC1n ch\u1ED7 tr\u1ED1ng (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: FILL_IN_BLANK_GROUP_DEFAULT_INSTRUCTION, className: "h-10 border-gray-200 bg-white" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "N\u1ED9i dung b\u00E0i \u0111\u1ECDc (Passage)" }), _jsx(RichTextEditor, { value: passage, onChange: setPassage, minHeightClassName: "min-h-[160px]" })] }), _jsxs("div", { className: "grid gap-2.5 md:grid-cols-2", children: [_jsx(FillInBlankGroupMediaField, { id: "fib-group-audio", label: "Audio chung (optional)", accept: "audio/*", value: audioUrl, onChange: setAudioUrl }), _jsx(FillInBlankGroupMediaField, { id: "fib-group-image", label: "\u1EA2nh chung (optional)", accept: "image/*", value: toFillInBlankImageUrls(imageUrl)[0] || '', onChange: setImageUrl })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: "show-hints", checked: showHints, onCheckedChange: setShowHints }), _jsx(Label, { htmlFor: "show-hints", children: "Hi\u1EC7n h\u1ED9p t\u1EEB g\u1EE3i \u00FD chung" })] }), showHints && (_jsx(Input, { value: hintsText, onChange: (e) => setHintsText(e.target.value), placeholder: "am / is / are", 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: "flex items-center gap-2 text-sm font-medium text-gray-700", 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 }), "C\u00E2u ", index + 1] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: `example-${index}`, checked: item.isExample || false, onCheckedChange: (checked) => updateItem(index, { isExample: checked, points: checked ? 0 : 1 }) }), _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, {
176
+ points: parsePointsValue(event.target.value, true),
177
+ }) }) })), items.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeItem(index), className: "h-7 w-7 text-red-500 hover:bg-red-50", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] })] }), _jsxs("div", { children: [_jsxs(Label, { htmlFor: `question-${index}`, children: ["Stem (d\u00F9ng ", '{0}', ", ", '{1}', "...)"] }), _jsx(Textarea, { id: `question-${index}`, value: item.question, onChange: (e) => updateItem(index, { question: e.target.value }), placeholder: "{0}", className: "mt-1 min-h-[56px] border-gray-200 bg-white" })] }), extractPlaceholderIndices(item.question).map((blankIndex) => (_jsxs("div", { className: "grid gap-2.5 md:grid-cols-2", children: [_jsxs("div", { children: [_jsxs(Label, { htmlFor: `answer-${index}-${blankIndex}`, children: ["\u0110\u00E1p \u00E1n blank ", '{' + blankIndex + '}', " (c\u00E1ch nhau b\u1EDFi /)"] }), _jsx(Input, { id: `answer-${index}-${blankIndex}`, value: joinSlashAnswers(item.answers[blankIndex]), onChange: (e) => {
178
+ const next = [...item.answers];
179
+ next[blankIndex] = parseSlashAnswers(e.target.value);
180
+ updateItem(index, { answers: next });
181
+ }, placeholder: "is / 's", className: "mt-1 h-10 border-gray-200 bg-white" })] }), _jsxs("div", { children: [_jsxs(Label, { htmlFor: `options-${index}-${blankIndex}`, children: ["Options blank ", '{' + blankIndex + '}', " (dropdown, c\u00E1ch nhau /)"] }), _jsx(Input, { id: `options-${index}-${blankIndex}`, value: joinSlashAnswers(item.wordBank?.[blankIndex]?.options), onChange: (e) => {
182
+ const requiredLength = blankSlotCount(item.question);
183
+ const next = [...(item.wordBank || [])];
184
+ while (next.length < requiredLength)
185
+ next.push(null);
186
+ const options = parseSlashAnswers(e.target.value);
187
+ next[blankIndex] = options.length >= 2 ? { options } : null;
188
+ updateItem(index, {
189
+ wordBank: next.some((entry) => entry && entry.options.length >= 2)
190
+ ? next
191
+ : undefined,
192
+ });
193
+ }, placeholder: "can / can't", className: "mt-1 h-10 border-gray-200 bg-white" })] })] }, blankIndex))), _jsxs("div", { className: "grid gap-2.5 md:grid-cols-2", children: [_jsx(FillInBlankGroupMediaField, { id: `fib-item-image-${index}`, label: "\u1EA2nh item (optional)", accept: "image/*", value: Array.isArray(item.imageUrl) ? (item.imageUrl[0] || '') : (item.imageUrl || ''), onChange: (url) => updateItem(index, { imageUrl: url }) }), !audioUrl && (_jsx(FillInBlankGroupMediaField, { id: `fib-item-audio-${index}`, label: "Audio item (khi kh\u00F4ng c\u00F3 audio nh\u00F3m)", accept: "audio/*", value: item.audioUrl || '', onChange: (url) => updateItem(index, { audioUrl: url }) }))] })] }, index))), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] })] }));
190
194
  }
@@ -78,13 +78,13 @@ function FillMissingWordsInGridClientContent({ questionData, onSubmit, isReviewM
78
78
  }, [missingPositions, userAnswers, correctAnswers, caseSensitive]);
79
79
  const score = isReviewMode ? calculateScore() : null;
80
80
  const scorePercentage = score ? Math.round((score.correct / score.total) * 100) : 0;
81
- 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-5 py-4", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-teal-500 to-cyan-600 text-white shadow-sm", children: _jsx(Grid3X3, { className: "h-5 w-5" }) }), _jsxs("div", { children: [_jsx("h3", { className: "text-base font-semibold text-gray-900", children: "\u0110i\u1EC1n t\u1EEB c\u00F2n thi\u1EBFu v\u00E0o b\u1EA3ng" }), _jsx("p", { className: "text-xs text-gray-500", children: "\u0110i\u1EC1n c\u00E1c t\u1EEB c\u00F2n thi\u1EBFu v\u00E0o c\u00E1c \u00F4 \u0111\u01B0\u1EE3c \u0111\u00E1nh d\u1EA5u m\u00E0u xanh" })] })] }), isReviewMode && score && (_jsxs("div", { className: cn('flex items-center gap-3 rounded-xl px-4 py-2', scorePercentage >= 80 ? 'bg-green-50 border border-green-200' :
81
+ 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-3", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-teal-500 to-cyan-600 text-white shadow-sm", children: _jsx(Grid3X3, { className: "h-5 w-5" }) }), _jsxs("div", { children: [_jsx("h3", { className: "text-base font-semibold text-gray-900", children: "\u0110i\u1EC1n t\u1EEB c\u00F2n thi\u1EBFu v\u00E0o b\u1EA3ng" }), _jsx("p", { className: "text-xs text-gray-500", children: "\u0110i\u1EC1n c\u00E1c t\u1EEB c\u00F2n thi\u1EBFu v\u00E0o c\u00E1c \u00F4 \u0111\u01B0\u1EE3c \u0111\u00E1nh d\u1EA5u m\u00E0u xanh" })] })] }), isReviewMode && score && (_jsxs("div", { className: cn('flex items-center gap-3 rounded-xl px-4 py-2', scorePercentage >= 80 ? 'bg-green-50 border border-green-200' :
82
82
  scorePercentage >= 50 ? 'bg-yellow-50 border border-yellow-200' :
83
83
  'bg-red-50 border border-red-200'), children: [_jsx(Trophy, { className: cn('h-5 w-5', scorePercentage >= 80 ? 'text-green-500' :
84
84
  scorePercentage >= 50 ? 'text-yellow-500' :
85
85
  'text-red-500') }), _jsxs("div", { className: "text-right", children: [_jsxs("div", { className: cn('text-lg font-bold', scorePercentage >= 80 ? 'text-green-600' :
86
86
  scorePercentage >= 50 ? 'text-yellow-600' :
87
- 'text-red-600'), children: [score.correct, "/", score.total] }), _jsxs("div", { className: "text-xs text-gray-500", children: [scorePercentage, "% ch\u00EDnh x\u00E1c"] })] })] }))] }), _jsxs("div", { className: "p-5 space-y-5", children: [_jsx("div", { className: "overflow-x-auto rounded-lg border border-gray-200 bg-gradient-to-br from-slate-50 to-gray-100 p-4", children: _jsxs("div", { className: "inline-block min-w-full", children: [_jsxs("div", { className: "grid gap-1.5 mb-2", style: { gridTemplateColumns: `48px repeat(${columns}, minmax(80px, 1fr))` }, children: [_jsx("div", { className: "h-8" }), Array.from({ length: columns }, (_, i) => (_jsx("div", { className: "flex h-8 items-center justify-center rounded-lg bg-gradient-to-br from-gray-100 to-gray-200 text-xs font-bold text-gray-600 shadow-sm", children: i + 1 }, `col-header-${i}`)))] }), _jsx("div", { className: "grid gap-1.5", children: Array.from({ length: rows }, (_, rowIndex) => (_jsxs("div", { className: "grid gap-1.5", style: { gridTemplateColumns: `48px repeat(${columns}, minmax(80px, 1fr))` }, children: [_jsx("div", { className: "flex h-14 items-center justify-center rounded-lg bg-gradient-to-br from-gray-100 to-gray-200 text-xs font-bold text-gray-600 shadow-sm", children: rowIndex + 1 }), Array.from({ length: columns }, (_, colIndex) => {
87
+ 'text-red-600'), children: [score.correct, "/", score.total] }), _jsxs("div", { className: "text-xs text-gray-500", children: [scorePercentage, "% ch\u00EDnh x\u00E1c"] })] })] }))] }), _jsxs("div", { className: "p-3 space-y-3", children: [_jsx("div", { className: "overflow-x-auto rounded-lg border border-gray-200 bg-gradient-to-br from-slate-50 to-gray-100 p-4", children: _jsxs("div", { className: "inline-block min-w-full", children: [_jsxs("div", { className: "grid gap-1.5 mb-2", style: { gridTemplateColumns: `48px repeat(${columns}, minmax(80px, 1fr))` }, children: [_jsx("div", { className: "h-8" }), Array.from({ length: columns }, (_, i) => (_jsx("div", { className: "flex h-8 items-center justify-center rounded-lg bg-gradient-to-br from-gray-100 to-gray-200 text-xs font-bold text-gray-600 shadow-sm", children: i + 1 }, `col-header-${i}`)))] }), _jsx("div", { className: "grid gap-1.5", children: Array.from({ length: rows }, (_, rowIndex) => (_jsxs("div", { className: "grid gap-1.5", style: { gridTemplateColumns: `48px repeat(${columns}, minmax(80px, 1fr))` }, children: [_jsx("div", { className: "flex h-14 items-center justify-center rounded-lg bg-gradient-to-br from-gray-100 to-gray-200 text-xs font-bold text-gray-600 shadow-sm", children: rowIndex + 1 }), Array.from({ length: columns }, (_, colIndex) => {
88
88
  const isMissing = isMissingCell(rowIndex, colIndex);
89
89
  const isDisabled = isDisabledCell(rowIndex, colIndex);
90
90
  const cellValue = grid[rowIndex]?.[colIndex] || '';
@@ -4,10 +4,8 @@ import { useState, useRef, useEffect, useCallback } from 'react';
4
4
  import { Button } from '../../../../components/ui/button';
5
5
  import { Label } from '../../../../components/ui/label';
6
6
  import { Input } from '../../../../components/ui/input';
7
- import { Textarea } from '../../../../components/ui/textarea';
8
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
9
- import { PointsInput } from '../../../../components/ui/points-input';
10
- import { Pencil, Eye, Trash2, Grid3X3, Lightbulb, CheckCircle2, BookOpen } from 'lucide-react';
7
+ import { Trash2, Grid3X3, CheckCircle2 } from 'lucide-react';
8
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
11
9
  import { toast } from 'sonner';
12
10
  import { t } from '../../../../shared/lib/i18n';
13
11
  import { FillMissingWordsInGridClient } from './FillMissingWordsInGridClient';
@@ -594,74 +592,79 @@ onChange, onUnsavedChangesChange, externalErrors = [], }) {
594
592
  acc[`${item.row + 1}-${item.column + 1}`] = item.correctAnswer;
595
593
  return acc;
596
594
  }, {});
597
- return (_jsx("div", { className: "space-y-6", children: _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Fill Missing Words In Grid (Preview)" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Ch\u1EBF \u0111\u1ED9 xem tr\u01B0\u1EDBc - Hi\u1EC3n th\u1ECB \u0111\u00E1p \u00E1n \u0111\u00FAng" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(false), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), "Quay l\u1EA1i Edit Mode"] })] }) }), _jsx(CardContent, { children: _jsx(FillMissingWordsInGridClient, { questionData: questionData, isReviewMode: true, userAnswer: correctAnswers, explanation: explanation }) })] }) }));
595
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(FillMissingWordsInGridClient, { questionData: questionData, isReviewMode: true, userAnswer: correctAnswers, explanation: explanation })] }));
598
596
  }
599
- return (_jsxs("div", { className: "space-y-6", children: [_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50", children: [_jsx("div", { className: "h-1.5 bg-gradient-to-r from-cyan-500 via-blue-500 to-indigo-500" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-cyan-500 to-blue-600 shadow-md", children: _jsx(Grid3X3, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold text-gray-900", children: "\u0110i\u1EC1n t\u1EEB c\u00F2n thi\u1EBFu v\u00E0o grid" }), _jsx("p", { className: "text-sm text-gray-500", children: "T\u1EA1o grid v\u00E0 nh\u1EADp c\u00E1c t\u1EEB, \u0111\u00E1nh d\u1EA5u c\u00E1c \u00F4 c\u00F2n thi\u1EBFu" })] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handlePreview, className: "gap-2 border-blue-200 text-blue-600 hover:bg-blue-50 hover:text-blue-700", children: [_jsx(Eye, { className: "h-4 w-4" }), "Xem tr\u01B0\u1EDBc"] })] }) }), _jsxs(CardContent, { className: "space-y-6", children: [_jsx("div", { className: "rounded-xl border border-cyan-200 bg-gradient-to-r from-cyan-50 to-blue-50 p-5 shadow-sm", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-cyan-100", children: _jsx(Lightbulb, { className: "h-4 w-4 text-cyan-600" }) }), _jsxs("div", { className: "text-sm text-cyan-900", children: [_jsx("p", { className: "font-semibold text-cyan-800", children: "H\u01B0\u1EDBng d\u1EABn t\u1EA1o c\u00E2u h\u1ECFi" }), _jsxs("ul", { className: "mt-2 space-y-1.5 text-cyan-700", children: [_jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-cyan-400" }), "Nh\u1EADp t\u1EEB v\u00E0o c\u00E1c \u00F4 trong grid (4 h\u00E0ng \u00D7 7 c\u1ED9t)"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-cyan-400" }), _jsxs("span", { children: [_jsx("strong", { children: "Double-click:" }), " \u0110\u00E1nh d\u1EA5u \u00F4 c\u00F2n thi\u1EBFu (m\u00E0u xanh d\u01B0\u01A1ng)"] })] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-cyan-400" }), _jsxs("span", { children: [_jsx("strong", { children: "Triple-click:" }), " V\u00F4 hi\u1EC7u h\u00F3a \u00F4 (m\u00E0u \u0111\u1ECF - kh\u00F4ng c\u1EA7n \u0111i\u1EC1n)"] })] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-cyan-400" }), "Nh\u1EADp \u0111\u00E1p \u00E1n \u0111\u00FAng cho m\u1ED7i \u00F4 c\u00F2n thi\u1EBFu"] })] })] })] }) }), _jsx("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: _jsx(PointsInput, { defaultValue: points, onChange: (e) => setPoints(Number(e.target.value) || 1) }, `points-input-${points}`) }), _jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "flex items-center justify-between", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100", children: _jsx(Grid3X3, { className: "h-3.5 w-3.5 text-indigo-600" }) }), _jsx(Label, { className: "text-base font-semibold text-gray-800", children: "Grid - Nh\u1EADp t\u1EEB v\u00E0o c\u00E1c \u00F4" })] }) }), _jsx("div", { className: "overflow-x-auto rounded-xl border-2 border-indigo-200 bg-gradient-to-br from-indigo-50/50 to-blue-50/50 p-5 shadow-sm", children: _jsxs("div", { className: "inline-block min-w-full", children: [_jsxs("div", { className: "grid gap-1 mb-1", style: { gridTemplateColumns: `auto repeat(${columns}, minmax(0, 1fr))` }, children: [_jsx("div", { className: "h-8" }), Array.from({ length: columns }, (_, i) => (_jsx("div", { className: "flex h-8 items-center justify-center text-xs font-semibold text-gray-600", children: i + 1 }, `col-header-${i}`)))] }), _jsx("div", { className: "grid gap-1", children: grid.map((row, rowIndex) => (_jsxs("div", { className: "grid gap-1", style: { gridTemplateColumns: `auto repeat(${columns}, minmax(0, 1fr))` }, children: [_jsx("div", { className: "flex h-12 items-center justify-center text-xs font-semibold text-gray-600 border-2 border-transparent", children: rowIndex + 1 }), row.map((cell, colIndex) => {
600
- const isMissing = missingLetters.some((item) => item.row === rowIndex && item.column === colIndex);
601
- const isDisabled = disabledCells.includes(`${rowIndex + 1}-${colIndex + 1}`); // NEW
602
- const isInvalid = shouldShowValidation() && isCellInvalid(rowIndex, colIndex);
603
- return (_jsxs("div", { className: `relative flex h-12 items-center justify-center border-2 rounded transition-colors ${isDisabled
604
- ? 'border-red-400 bg-red-100 hover:bg-red-150 cursor-pointer' // NEW: Red styling for disabled
605
- : isMissing
606
- ? 'border-blue-500 bg-blue-50 hover:bg-blue-100 cursor-pointer'
607
- : isInvalid
608
- ? 'border-red-500 bg-red-50 hover:bg-red-100'
609
- : cell
610
- ? 'border-gray-400 bg-white hover:bg-gray-50'
611
- : 'border-gray-300 bg-gray-100 hover:bg-gray-200'}`, onClick: (e) => {
612
- // Triple-click to toggle disabled
613
- if (e.detail === 3) {
614
- e.preventDefault();
615
- e.stopPropagation();
616
- toggleDisabledCell(rowIndex, colIndex);
597
+ return (_jsxs("div", { className: "space-y-3", children: [_jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Grid3X3, { className: "h-3.5 w-3.5" }), title: "\u0110i\u1EC1n t\u1EEB c\u00F2n thi\u1EBFu v\u00E0o grid", points: {
598
+ value: points,
599
+ onValueChange: setPoints,
600
+ }, preview: {
601
+ onClick: handlePreview,
602
+ } }), children: [_jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "flex items-center justify-between", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100", children: _jsx(Grid3X3, { className: "h-3.5 w-3.5 text-indigo-600" }) }), _jsx(Label, { className: "text-base font-semibold text-gray-800", children: "Grid - Nh\u1EADp t\u1EEB v\u00E0o c\u00E1c \u00F4" })] }) }), _jsx("div", { className: "overflow-x-auto rounded-xl border-2 border-indigo-200 bg-gradient-to-br from-indigo-50/50 to-blue-50/50 p-5 shadow-sm", children: _jsxs("div", { className: "inline-block min-w-full", children: [_jsxs("div", { className: "grid gap-1 mb-1", style: { gridTemplateColumns: `auto repeat(${columns}, minmax(0, 1fr))` }, children: [_jsx("div", { className: "h-8" }), Array.from({ length: columns }, (_, i) => (_jsx("div", { className: "flex h-8 items-center justify-center text-xs font-semibold text-gray-600", children: i + 1 }, `col-header-${i}`)))] }), _jsx("div", { className: "grid gap-1", children: grid.map((row, rowIndex) => (_jsxs("div", { className: "grid gap-1", style: { gridTemplateColumns: `auto repeat(${columns}, minmax(0, 1fr))` }, children: [_jsx("div", { className: "flex h-12 items-center justify-center text-xs font-semibold text-gray-600 border-2 border-transparent", children: rowIndex + 1 }), row.map((cell, colIndex) => {
603
+ const isMissing = missingLetters.some((item) => item.row === rowIndex && item.column === colIndex);
604
+ const isDisabled = disabledCells.includes(`${rowIndex + 1}-${colIndex + 1}`); // NEW
605
+ const isInvalid = shouldShowValidation() && isCellInvalid(rowIndex, colIndex);
606
+ return (_jsxs("div", { className: `relative flex h-12 items-center justify-center border-2 rounded transition-colors ${isDisabled
607
+ ? 'border-red-400 bg-red-100 hover:bg-red-150 cursor-pointer' // NEW: Red styling for disabled
608
+ : isMissing
609
+ ? 'border-blue-500 bg-blue-50 hover:bg-blue-100 cursor-pointer'
610
+ : isInvalid
611
+ ? 'border-red-500 bg-red-50 hover:bg-red-100'
612
+ : cell
613
+ ? 'border-gray-400 bg-white hover:bg-gray-50'
614
+ : 'border-gray-300 bg-gray-100 hover:bg-gray-200'}`, onClick: (e) => {
615
+ // Triple-click to toggle disabled
616
+ if (e.detail === 3) {
617
+ e.preventDefault();
618
+ e.stopPropagation();
619
+ toggleDisabledCell(rowIndex, colIndex);
620
+ }
621
+ }, onDoubleClick: (e) => {
622
+ e.preventDefault();
623
+ e.stopPropagation();
624
+ if (!isDisabled) {
625
+ toggleMissingLetter(rowIndex, colIndex);
626
+ }
627
+ }, children: [_jsx(Input, { type: "text", maxLength: 10, value: cell || '', onChange: (e) => {
628
+ if (!isMissing && !isDisabled) { // NEW: Also check disabled
629
+ // Trim whitespace and limit length
630
+ const value = e.target.value.trim().slice(0, 10);
631
+ updateGridCell(rowIndex, colIndex, value);
617
632
  }
618
633
  }, onDoubleClick: (e) => {
619
634
  e.preventDefault();
620
635
  e.stopPropagation();
621
- if (!isDisabled) {
636
+ if (!isDisabled) { // NEW: Only toggle missing if not disabled
622
637
  toggleMissingLetter(rowIndex, colIndex);
623
638
  }
624
- }, children: [_jsx(Input, { type: "text", maxLength: 10, value: cell || '', onChange: (e) => {
625
- if (!isMissing && !isDisabled) { // NEW: Also check disabled
626
- // Trim whitespace and limit length
627
- const value = e.target.value.trim().slice(0, 10);
628
- updateGridCell(rowIndex, colIndex, value);
629
- }
630
- }, onDoubleClick: (e) => {
631
- e.preventDefault();
632
- e.stopPropagation();
633
- if (!isDisabled) { // NEW: Only toggle missing if not disabled
634
- toggleMissingLetter(rowIndex, colIndex);
635
- }
636
- }, disabled: isMissing || isDisabled, readOnly: isMissing || isDisabled, className: `h-full border-0 bg-transparent text-center text-lg font-semibold focus:ring-2 focus:ring-blue-500 ${isMissing || isDisabled ? 'cursor-not-allowed pointer-events-none' : ''} ${isDisabled ? 'text-gray-500' : isMissing ? 'text-blue-600' : ''}`, placeholder: "" }), isMissing && (_jsx("div", { className: "absolute -top-1 -right-1 flex h-5 w-5 items-center justify-center rounded-full bg-blue-500 text-xs font-bold text-white", children: "?" })), isDisabled && (_jsx("div", { className: "absolute -top-1 -right-1 flex h-5 w-5 items-center justify-center rounded-full bg-red-500 text-xs font-bold text-white", children: "\uD83D\uDD12" }))] }, `${rowIndex}-${colIndex}`));
637
- })] }, `row-${rowIndex}`))) })] }) })] }), missingLetters.length > 0 && (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-blue-100", children: _jsx(CheckCircle2, { className: "h-3.5 w-3.5 text-blue-600" }) }), _jsxs(Label, { className: "text-base font-semibold text-gray-800", children: ["Danh s\u00E1ch \u00F4 c\u00F2n thi\u1EBFu", _jsx("span", { className: "ml-2 rounded-full bg-blue-100 px-2 py-0.5 text-xs font-medium text-blue-700", children: missingLetters.length })] })] }), _jsx("div", { className: "space-y-3", children: [...missingLetters]
638
- .sort((a, b) => {
639
- if (a.row !== b.row)
640
- return a.row - b.row;
641
- return a.column - b.column;
642
- })
643
- .map((letter, index) => {
644
- const cellValue = grid[letter.row]?.[letter.column];
645
- return (_jsx("div", { className: "rounded-xl border-2 border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-4 shadow-sm transition-all hover:border-blue-300 hover:shadow-md", children: _jsxs("div", { className: "flex items-start gap-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-blue-500 text-sm font-bold text-white shadow-sm", children: index + 1 }), _jsxs("div", { className: "flex-1 space-y-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: "text-sm font-semibold text-gray-700", children: ["V\u1ECB tr\u00ED (", letter.row + 1, ", ", letter.column + 1, ")"] }), cellValue && (_jsxs("span", { className: "rounded-full bg-gray-100 px-2 py-0.5 text-xs text-gray-500", children: ["Hi\u1EC7n t\u1EA1i: \"", cellValue, "\""] }))] }), _jsxs("div", { children: [_jsxs(Label, { htmlFor: `answer-${letter.id}`, className: "text-sm font-medium text-gray-700", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: `answer-${letter.id}`, type: "text", maxLength: 100, value: letter.correctAnswer, onChange: (e) => updateMissingLetter(letter.id, 'correctAnswer', e.target.value.trim()), onBlur: () => {
646
- if (!letter.correctAnswer || letter.correctAnswer.trim() === '') {
647
- setValidationErrors((prev) => ({
648
- ...prev,
649
- [letter.id]: 'Vui lòng nhập đáp án đúng',
650
- }));
651
- }
652
- else if (letter.correctAnswer.length > 100) {
653
- setValidationErrors((prev) => ({
654
- ...prev,
655
- [letter.id]: 'Đáp án không được vượt quá 100 ký tự',
656
- }));
657
- }
658
- }, placeholder: "Nh\u1EADp \u0111\u00E1p \u00E1n \u0111\u00FAng...", className: `mt-1.5 border-gray-200 bg-white transition-all focus:border-blue-300 focus:ring-blue-200 ${validationErrors[letter.id]
659
- ? 'border-red-400 focus:border-red-500 focus:ring-red-200'
660
- : ''}` }), validationErrors[letter.id] && (_jsx("p", { className: "mt-1.5 text-sm text-red-600", children: validationErrors[letter.id] }))] })] }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeMissingLetter(letter.id), className: "h-8 w-8 text-red-400 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-4 w-4" }) })] }) }, letter.id));
661
- }) })] }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-amber-400 to-orange-400" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm", children: _jsx(BookOpen, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-lg font-semibold text-gray-800", children: "Gi\u1EA3i th\u00EDch" }), _jsx("span", { className: "inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-600", children: "Kh\u00F4ng b\u1EAFt bu\u1ED9c" })] }), _jsx("p", { className: "text-sm text-gray-500 mt-0.5", children: "Gi\u1EA3i th\u00EDch cho \u0111\u00E1p \u00E1n \u0111\u00FAng \u2014 hi\u1EC3n th\u1ECB sau khi h\u1ECDc sinh ho\u00E0n th\u00E0nh c\u00E2u h\u1ECFi" })] })] }) }), _jsx(CardContent, { children: _jsx(Textarea, { id: "explanation", value: explanation, onChange: (e) => {
662
- setExplanation(e.target.value);
663
- onUnsavedChangesChange?.(true);
664
- }, placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0... v\u00EC...", rows: 3, className: "min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none" }) })] })] }));
639
+ }, disabled: isMissing || isDisabled, readOnly: isMissing || isDisabled, className: `h-full border-0 bg-transparent text-center text-lg font-semibold focus:ring-2 focus:ring-blue-500 ${isMissing || isDisabled ? 'cursor-not-allowed pointer-events-none' : ''} ${isDisabled ? 'text-gray-500' : isMissing ? 'text-blue-600' : ''}`, placeholder: "" }), isMissing && (_jsx("div", { className: "absolute -top-1 -right-1 flex h-5 w-5 items-center justify-center rounded-full bg-blue-500 text-xs font-bold text-white", children: "?" })), isDisabled && (_jsx("div", { className: "absolute -top-1 -right-1 flex h-5 w-5 items-center justify-center rounded-full bg-red-500 text-xs font-bold text-white", children: "\uD83D\uDD12" }))] }, `${rowIndex}-${colIndex}`));
640
+ })] }, `row-${rowIndex}`))) })] }) })] }), missingLetters.length > 0 && (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-blue-100", children: _jsx(CheckCircle2, { className: "h-3.5 w-3.5 text-blue-600" }) }), _jsxs(Label, { className: "text-base font-semibold text-gray-800", children: ["Danh s\u00E1ch \u00F4 c\u00F2n thi\u1EBFu", _jsx("span", { className: "ml-2 rounded-full bg-blue-100 px-2 py-0.5 text-xs font-medium text-blue-700", children: missingLetters.length })] })] }), _jsx("div", { className: "space-y-3", children: [...missingLetters]
641
+ .sort((a, b) => {
642
+ if (a.row !== b.row)
643
+ return a.row - b.row;
644
+ return a.column - b.column;
645
+ })
646
+ .map((letter, index) => {
647
+ const cellValue = grid[letter.row]?.[letter.column];
648
+ return (_jsx("div", { className: "rounded-xl border-2 border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-4 shadow-sm transition-all hover:border-blue-300 hover:shadow-md", children: _jsxs("div", { className: "flex items-start gap-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-blue-500 text-sm font-bold text-white shadow-sm", children: index + 1 }), _jsxs("div", { className: "flex-1 space-y-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: "text-sm font-semibold text-gray-700", children: ["V\u1ECB tr\u00ED (", letter.row + 1, ", ", letter.column + 1, ")"] }), cellValue && (_jsxs("span", { className: "rounded-full bg-gray-100 px-2 py-0.5 text-xs text-gray-500", children: ["Hi\u1EC7n t\u1EA1i: \"", cellValue, "\""] }))] }), _jsxs("div", { children: [_jsxs(Label, { htmlFor: `answer-${letter.id}`, className: "text-sm font-medium text-gray-700", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: `answer-${letter.id}`, type: "text", maxLength: 100, value: letter.correctAnswer, onChange: (e) => updateMissingLetter(letter.id, 'correctAnswer', e.target.value.trim()), onBlur: () => {
649
+ if (!letter.correctAnswer || letter.correctAnswer.trim() === '') {
650
+ setValidationErrors((prev) => ({
651
+ ...prev,
652
+ [letter.id]: 'Vui lòng nhập đáp án đúng',
653
+ }));
654
+ }
655
+ else if (letter.correctAnswer.length > 100) {
656
+ setValidationErrors((prev) => ({
657
+ ...prev,
658
+ [letter.id]: 'Đáp án không được vượt quá 100 ký tự',
659
+ }));
660
+ }
661
+ }, placeholder: "Nh\u1EADp \u0111\u00E1p \u00E1n \u0111\u00FAng...", className: `mt-1.5 border-gray-200 bg-white transition-all focus:border-blue-300 focus:ring-blue-200 ${validationErrors[letter.id]
662
+ ? 'border-red-400 focus:border-red-500 focus:ring-red-200'
663
+ : ''}` }), validationErrors[letter.id] && (_jsx("p", { className: "mt-1.5 text-sm text-red-600", children: validationErrors[letter.id] }))] })] }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeMissingLetter(letter.id), className: "h-8 w-8 text-red-400 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-4 w-4" }) })] }) }, letter.id));
664
+ }) })] }))] }), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: (value) => {
665
+ setExplanation(value);
666
+ onUnsavedChangesChange?.(true);
667
+ } })] }));
665
668
  }
666
669
  // Export with both new and legacy names for backward compatibility
667
670
  export function FillMissingWordsInGridCreator(props) {