@tinyweb_dev/oe-exam-sdk 1.0.6 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/dist/components/exams/ExamCreator.js +1 -16
  2. package/dist/components/exams/take/ExamTakingPageContainer.js +0 -12
  3. package/dist/components/exams/take/components/QuestionRenderer.js +2 -15
  4. package/dist/components/exams/take/components/question-renderers/index.d.ts +0 -3
  5. package/dist/components/exams/take/components/question-renderers/index.js +0 -3
  6. package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionRenderer.js +0 -4
  7. package/dist/components/exams/take/types/speaking.types.d.ts +0 -31
  8. package/dist/components/exams/take/types.d.ts +0 -65
  9. package/dist/components/exams/take/utils/answer-transformers.js +6 -60
  10. package/dist/components/exams/take/utils/question-transformers.d.ts +2 -15
  11. package/dist/components/exams/take/utils/question-transformers.js +0 -143
  12. package/dist/components/questions/_shared/components/compact/CompactCreatorHeader.d.ts +23 -0
  13. package/dist/components/questions/_shared/components/compact/CompactCreatorHeader.js +9 -0
  14. package/dist/components/questions/_shared/components/compact/CompactExampleToggle.d.ts +6 -0
  15. package/dist/components/questions/_shared/components/compact/CompactExampleToggle.js +8 -0
  16. package/dist/components/questions/_shared/components/compact/CompactExplanationToggle.d.ts +7 -0
  17. package/dist/components/questions/_shared/components/compact/CompactExplanationToggle.js +14 -0
  18. package/dist/components/questions/_shared/components/compact/CompactFieldCard.d.ts +9 -0
  19. package/dist/components/questions/_shared/components/compact/CompactFieldCard.js +7 -0
  20. package/dist/components/questions/_shared/components/compact/CompactPreviewBanner.d.ts +7 -0
  21. package/dist/components/questions/_shared/components/compact/CompactPreviewBanner.js +7 -0
  22. package/dist/components/questions/_shared/components/compact/CompactPreviewButton.d.ts +6 -0
  23. package/dist/components/questions/_shared/components/compact/CompactPreviewButton.js +7 -0
  24. package/dist/components/questions/_shared/components/compact/index.d.ts +6 -0
  25. package/dist/components/questions/_shared/components/compact/index.js +6 -0
  26. package/dist/components/questions/_shared/config/question-types.config.js +0 -24
  27. package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +0 -9
  28. package/dist/components/questions/_shared/types/choose-the-correct-answer-group.type.d.ts +2 -2
  29. package/dist/components/questions/_shared/types/choose-the-correct-answer-group.type.js +5 -1
  30. package/dist/components/questions/_shared/types/index.d.ts +0 -1
  31. package/dist/components/questions/_shared/types/index.js +0 -1
  32. package/dist/components/questions/_shared/types/look-and-choose-answer.type.d.ts +0 -13
  33. package/dist/components/questions/_shared/utils/answer-builder.js +0 -11
  34. package/dist/components/questions/components/ColorRegionCreator.js +66 -63
  35. package/dist/components/questions/components/QuestionSearchDropdown.js +0 -8
  36. package/dist/components/questions/components/QuestionTypeSelector.js +0 -2
  37. package/dist/components/questions/components/index.d.ts +1 -0
  38. package/dist/components/questions/components/index.js +1 -0
  39. package/dist/components/questions/creator/question-type-registry.js +0 -8
  40. package/dist/components/questions/groups/ArticlesGroupCard.js +2 -2
  41. package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -1
  42. package/dist/components/questions/groups/DocumentGroupCard.js +1 -1
  43. package/dist/components/questions/groups/ImageLabelGroupCard.js +1 -1
  44. package/dist/components/questions/groups/MatchAnswerGroupCard.js +2 -2
  45. package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +0 -3
  46. package/dist/components/questions/question-bank/QuestionEditorRenderer.js +1 -10
  47. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.d.ts +0 -2
  48. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.js +0 -41
  49. package/dist/components/questions/question-bank/editor-adapters/index.d.ts +2 -2
  50. package/dist/components/questions/question-bank/editor-adapters/index.js +2 -2
  51. package/dist/components/questions/question-bank/editor-adapters/media-adapters.d.ts +0 -1
  52. package/dist/components/questions/question-bank/editor-adapters/media-adapters.js +0 -28
  53. package/dist/components/questions/question-bank/question-bank-picker.utils.js +0 -7
  54. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +16 -16
  55. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +36 -30
  56. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupClient.js +1 -1
  57. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupCreator.js +7 -6
  58. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +2 -2
  59. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +11 -7
  60. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -1
  61. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +11 -5
  62. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +5 -5
  63. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +34 -28
  64. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseAnswerGroupImageUpload.d.ts +7 -0
  65. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseAnswerGroupImageUpload.js +5 -2
  66. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +9 -5
  67. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupCreator.js +65 -62
  68. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupItemEditor.d.ts +6 -0
  69. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupItemEditor.js +145 -0
  70. package/dist/components/questions/types/choose-the-correct-answer-group/map-choose-the-correct-answer-group-data.js +6 -15
  71. package/dist/components/questions/types/choose-the-correct-answer-group/transform.js +5 -4
  72. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupClient.js +7 -7
  73. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupCreator.js +8 -5
  74. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +2 -2
  75. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +20 -16
  76. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.js +1 -1
  77. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleCreator.js +66 -29
  78. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +5 -5
  79. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +45 -37
  80. package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupCreator.js +24 -20
  81. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +2 -2
  82. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +68 -65
  83. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixClient.js +1 -1
  84. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixCreator.js +45 -5
  85. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +2 -2
  86. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +18 -14
  87. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +2 -2
  88. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +30 -30
  89. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +2 -2
  90. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +24 -31
  91. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +2 -2
  92. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +29 -32
  93. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -1
  94. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +27 -29
  95. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -1
  96. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +11 -15
  97. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -1
  98. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +22 -24
  99. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -1
  100. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +17 -24
  101. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -1
  102. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +31 -31
  103. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -1
  104. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +11 -15
  105. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -1
  106. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +14 -15
  107. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +2 -2
  108. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +27 -25
  109. package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesClient.js +12 -12
  110. package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesCreator.js +8 -5
  111. package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureClient.js +8 -8
  112. package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureCreator.js +25 -20
  113. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupClient.js +1 -1
  114. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupCreator.js +13 -10
  115. package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupCreator.js +8 -5
  116. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +7 -11
  117. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -1
  118. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +15 -12
  119. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesClient.js +1 -1
  120. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesCreator.js +5 -5
  121. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +35 -39
  122. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +2 -2
  123. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardClient.js +1 -1
  124. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +7 -8
  125. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -1
  126. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +30 -23
  127. package/dist/components/questions/types/spontaneous-qa/SpontaneousQAClient.js +1 -1
  128. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +26 -19
  129. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.d.ts +1 -1
  130. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.js +79 -3
  131. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.js +13 -11
  132. package/dist/components/questions/types/spontaneous-qa-group/index.d.ts +1 -0
  133. package/dist/components/questions/types/spontaneous-qa-group/index.js +1 -0
  134. package/dist/components/questions/types/spontaneous-qa-group/map-spontaneous-qa-group-data.d.ts +2 -0
  135. package/dist/components/questions/types/spontaneous-qa-group/map-spontaneous-qa-group-data.js +74 -0
  136. package/dist/components/questions/types/true-false/TrueFalseClient.js +2 -2
  137. package/dist/components/questions/types/true-false/TrueFalseCreator.js +35 -29
  138. package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupClient.js +1 -1
  139. package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupCreator.js +11 -7
  140. package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +1 -1
  141. package/dist/components/questions/types/true-false-group/TrueFalseGroupCreator.js +11 -7
  142. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +2 -2
  143. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +21 -56
  144. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -1
  145. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +30 -24
  146. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.js +3 -3
  147. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupCreator.js +12 -14
  148. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -1
  149. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +27 -18
  150. package/dist/components/questions/types/word-ordering-group/WordOrderingGroupClient.js +7 -7
  151. package/dist/components/questions/types/word-ordering-group/WordOrderingGroupCreator.js +16 -13
  152. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +2 -2
  153. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +34 -28
  154. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +2 -2
  155. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +22 -22
  156. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +21 -18
  157. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -1
  158. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +7 -4
  159. package/dist/components/questions/viewer/QuestionViewer.js +0 -98
  160. package/dist/components/questions/viewer/SpontaneousQaGroupViewer.js +4 -79
  161. package/dist/components/results/renderers/review-question-body-movers.js +2 -26
  162. package/dist/components/results/review-data.js +0 -4
  163. package/dist/components/ui/points-input.d.ts +1 -0
  164. package/dist/components/ui/points-input.js +12 -2
  165. package/dist/shared/constants/question-skills.js +0 -11
  166. package/dist/shared/lib/i18n/messages/en/admin.d.ts +0 -3
  167. package/dist/shared/lib/i18n/messages/en/admin.js +0 -3
  168. package/dist/shared/lib/i18n/messages/en.d.ts +0 -3
  169. package/dist/shared/lib/i18n/messages/vi/admin.d.ts +0 -3
  170. package/dist/shared/lib/i18n/messages/vi/admin.js +0 -3
  171. package/dist/shared/lib/i18n/messages/vi.d.ts +0 -3
  172. package/dist/shared/lib/utils/question-reverse-transform.js +4 -23
  173. package/dist/shared/lib/utils/question-transform-types.d.ts +1 -23
  174. package/dist/shared/lib/utils/question-transform-validation.js +0 -40
  175. package/dist/shared/lib/utils/question-transform.js +0 -9
  176. package/dist/shared/types/common.types.d.ts +1 -1
  177. package/dist/shared/types/entities/question.types.d.ts +1 -11
  178. package/dist/shared/types/questions/choose-the-correct-answer-group.d.ts +1 -1
  179. package/dist/shared/types/questions/index.d.ts +0 -3
  180. package/dist/shared/types/questions/index.js +0 -6
  181. package/package.json +1 -1
  182. package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.d.ts +0 -2
  183. package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.js +0 -131
  184. package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.d.ts +0 -2
  185. package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.js +0 -76
  186. package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.d.ts +0 -2
  187. package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.js +0 -68
  188. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.d.ts +0 -16
  189. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.js +0 -212
  190. package/dist/components/questions/_shared/types/gn-speaking-interview.type.d.ts +0 -32
  191. package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +0 -1
  192. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.d.ts +0 -47
  193. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +0 -1
  194. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.d.ts +0 -2
  195. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +0 -262
  196. package/dist/components/questions/types/gn-speaking-interview/register.d.ts +0 -2
  197. package/dist/components/questions/types/gn-speaking-interview/register.js +0 -121
  198. package/dist/components/questions/types/gn-speaking-interview/transform.d.ts +0 -2
  199. package/dist/components/questions/types/gn-speaking-interview/transform.js +0 -48
  200. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.d.ts +0 -20
  201. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +0 -72
  202. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.d.ts +0 -15
  203. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +0 -230
  204. package/dist/components/questions/types/look-picture-choose-correct-answer/index.d.ts +0 -1
  205. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +0 -1
  206. package/dist/components/questions/types/look-picture-choose-correct-answer/register.d.ts +0 -2
  207. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +0 -66
  208. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.d.ts +0 -2
  209. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +0 -42
  210. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.d.ts +0 -6
  211. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +0 -96
  212. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.d.ts +0 -15
  213. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +0 -238
  214. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.d.ts +0 -2
  215. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +0 -2
  216. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.d.ts +0 -2
  217. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +0 -76
  218. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.d.ts +0 -2
  219. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +0 -19
  220. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.d.ts +0 -7
  221. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +0 -134
  222. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.d.ts +0 -3
  223. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +0 -617
  224. package/dist/components/questions/types/look-picture-fill-word-hint/index.d.ts +0 -2
  225. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +0 -2
  226. package/dist/components/questions/types/look-picture-fill-word-hint/register.d.ts +0 -2
  227. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +0 -82
  228. package/dist/components/questions/types/look-picture-fill-word-hint/transform.d.ts +0 -2
  229. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +0 -29
  230. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.d.ts +0 -9
  231. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +0 -12
  232. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.d.ts +0 -17
  233. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +0 -47
  234. package/dist/shared/types/questions/look-picture-choose-correct-answer.d.ts +0 -38
  235. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +0 -5
  236. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.d.ts +0 -63
  237. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +0 -9
  238. package/dist/shared/types/questions/look-picture-fill-word-hint.d.ts +0 -57
  239. package/dist/shared/types/questions/look-picture-fill-word-hint.js +0 -7
@@ -4,10 +4,9 @@ import { useState, useRef, useEffect } from 'react';
4
4
  import { Button } from '../../../../components/ui/button';
5
5
  import { Label } from '../../../../components/ui/label';
6
6
  import { Input } from '../../../../components/ui/input';
7
- import { Textarea } from '../../../../components/ui/textarea';
8
7
  import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
9
- import { PointsInput } from '../../../../components/ui/points-input';
10
- import { Pencil, Eye, Plus, X, Image, Lightbulb, Tags, BookOpen } from 'lucide-react';
8
+ import { Plus, X, Image, Tags } from 'lucide-react';
9
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
11
10
  import { FileUpload } from '../../../../components/ui/file-upload';
12
11
  import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../../../components/ui/select';
13
12
  import { LabelThePictureClient } from './LabelThePictureClient';
@@ -313,20 +312,25 @@ function LabelThePictureCreatorContent({ initialData, onSave, onCancel, onChange
313
312
  acc[label.id] = label.correctAnswer;
314
313
  return acc;
315
314
  }, {});
316
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Label the Picture (Preview)" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Ch\u1EBF \u0111\u1ED9 xem tr\u01B0\u1EDBc - Hi\u1EC3n th\u1ECB \u0111\u00E1p \u00E1n \u0111\u00FAng" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(false), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), "Quay l\u1EA1i Edit Mode"] })] }) }) }), _jsx(LabelThePictureClient, { questionData: questionData, userAnswer: correctAnswers, isReviewMode: true, explanation: explanation })] }));
315
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(LabelThePictureClient, { questionData: questionData, userAnswer: correctAnswers, isReviewMode: true, explanation: explanation })] }));
317
316
  }
318
- 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-blue-500 via-indigo-500 to-purple-500" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-blue-500 to-indigo-600 shadow-md", children: _jsx(Image, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold text-gray-900", children: "G\u1EAFn nh\u00E3n h\u00ECnh \u1EA3nh" }), _jsx("p", { className: "text-sm text-gray-500", children: "T\u1EA1o c\u00E2u h\u1ECFi v\u1EDBi h\u00ECnh \u1EA3nh, word bank v\u00E0 c\u00E1c v\u1ECB tr\u00ED c\u1EA7n \u0111i\u1EC1n" })] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handlePreview, 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-6", children: [_jsx("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: _jsx(PointsInput, { defaultValue: points, onChange: (e) => setPoints(Number(e.target.value) || 1) }) }), _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" }), "Upload h\u00ECnh \u1EA3nh c\u1EA7n g\u1EAFn nh\u00E3n"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-blue-400" }), "Th\u00EAm c\u00E1c t\u1EEB v\u00E0o Word Bank (danh s\u00E1ch t\u1EEB g\u1EE3i \u00FD)"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-blue-400" }), "T\u1EA1o c\u00E1c v\u1ECB tr\u00ED c\u1EA7n \u0111i\u1EC1n v\u00E0 ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng t\u1EEB Word Bank"] })] })] })] }) }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(Image, { className: "h-4 w-4 text-indigo-500" }), "H\u00ECnh \u1EA3nh ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(FileUpload, { id: "label-picture-image", label: "H\u00ECnh \u1EA3nh", accept: "image/*", value: imageUrl, onChange: (url) => {
319
- setImageUrl(url);
320
- form.clearErrors();
321
- }, onPresignedUrlChange: (presignedUrl) => {
322
- setImagePreviewUrl(presignedUrl);
323
- }, maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", required: true, autoUpload: true, prefix: "questions" }), errors.imageUrl && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: errors.imageUrl })), imagePreviewUrl && (_jsx("div", { className: "relative mt-2 h-64 w-full max-w-2xl overflow-hidden rounded border", children: _jsx("img", { src: imagePreviewUrl, alt: "Preview for question", className: "h-full w-full object-contain", onError: () => {
324
- console.error('Failed to load image:', imageUrl);
325
- } }) }))] })] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-indigo-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-indigo-400 to-purple-400" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center 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(Tags, { className: "h-3.5 w-3.5 text-indigo-600" }) }), _jsxs(CardTitle, { className: "text-lg font-semibold text-gray-800", children: ["Word Bank ", _jsxs("span", { className: "text-sm font-normal text-gray-500", children: ["(", wordBank.filter(w => w.trim()).length, " t\u1EEB)"] })] })] }), _jsxs(Button, { type: "button", size: "sm", onClick: addWordToBank, className: "gap-2 border-green-200 text-green-600 hover:bg-green-50 hover:text-green-700", variant: "outline", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm t\u1EEB"] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [errors.wordBank && (_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("span", { className: "text-lg", children: "\u274C" }), _jsx("p", { className: "text-sm text-red-600", children: errors.wordBank })] })), _jsx("div", { className: "grid gap-3 sm:grid-cols-2", children: wordBank.map((word, index) => (_jsxs("div", { className: `group relative rounded-xl border-2 p-3 transition-all duration-200 ${word.trim()
317
+ return (_jsxs("div", { className: "space-y-3", children: [_jsx(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Image, { "aria-label": "G\u1EAFn nh\u00E3n h\u00ECnh \u1EA3nh", className: "h-3.5 w-3.5" }), title: "G\u1EAFn nh\u00E3n h\u00ECnh \u1EA3nh", points: {
318
+ value: points,
319
+ onValueChange: setPoints,
320
+ }, preview: {
321
+ onClick: handlePreview,
322
+ } }), children: _jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { className: "flex items-center gap-1.5 text-sm font-semibold text-gray-800", children: [_jsx(Image, { "aria-label": "H\u00ECnh \u1EA3nh", className: "h-3.5 w-3.5 text-indigo-500" }), "H\u00ECnh \u1EA3nh ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(FileUpload, { id: "label-picture-image", label: "H\u00ECnh \u1EA3nh", accept: "image/*", value: imageUrl, onChange: (url) => {
323
+ setImageUrl(url);
324
+ form.clearErrors();
325
+ }, onPresignedUrlChange: (presignedUrl) => {
326
+ setImagePreviewUrl(presignedUrl);
327
+ }, maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", required: true, autoUpload: true, prefix: "questions" }), errors.imageUrl && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: errors.imageUrl })), imagePreviewUrl && (_jsx("div", { className: "relative mt-2 h-64 w-full max-w-2xl overflow-hidden rounded border", children: _jsx("img", { src: imagePreviewUrl, alt: "Preview for question", className: "h-full w-full object-contain", onError: () => {
328
+ console.error('Failed to load image:', imageUrl);
329
+ } }) }))] }) }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100", children: _jsx(Tags, { className: "h-3.5 w-3.5 text-indigo-600" }) }), _jsxs(CardTitle, { className: "text-lg font-semibold text-gray-800", children: ["Word Bank ", _jsxs("span", { className: "text-sm font-normal text-gray-500", children: ["(", wordBank.filter(w => w.trim()).length, " t\u1EEB)"] })] })] }), _jsxs(Button, { type: "button", size: "sm", onClick: addWordToBank, className: "gap-2 border-green-200 text-green-600 hover:bg-green-50 hover:text-green-700", variant: "outline", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm t\u1EEB"] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [errors.wordBank && (_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("span", { className: "text-lg", children: "\u274C" }), _jsx("p", { className: "text-sm text-red-600", children: errors.wordBank })] })), _jsx("div", { className: "grid gap-3 sm:grid-cols-2", children: wordBank.map((word, index) => (_jsxs("div", { className: `group relative rounded-xl border-2 p-3 transition-all duration-200 ${word.trim()
326
330
  ? 'border-green-200 bg-gradient-to-r from-green-50 to-emerald-50'
327
331
  : 'border-gray-200 bg-white hover:border-gray-300'}`, children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: `flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg font-bold transition-all ${word.trim()
328
332
  ? 'bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md'
329
- : 'bg-gray-100 text-gray-600'}`, children: index + 1 }), _jsx(Input, { value: word, onChange: (e) => updateWordInBank(index, e.target.value), placeholder: `Từ ${index + 1}`, className: `flex-1 border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${errors.wordBankItems?.[index] ? 'border-red-400' : ''}` }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeWordFromBank(index), className: "text-gray-400 hover:bg-red-50 hover:text-red-500", children: _jsx(X, { className: "h-4 w-4" }) })] }), errors.wordBankItems?.[index] && (_jsx("p", { className: "mt-2 ml-11 text-sm text-red-600", children: errors.wordBankItems[index] }))] }, index))) }), wordBank.length === 0 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4", children: [_jsx("span", { className: "text-lg", children: "\u26A0\uFE0F" }), _jsx("p", { className: "text-sm text-amber-800", children: "Ch\u01B0a c\u00F3 t\u1EEB n\u00E0o. Nh\u1EA5n \"Th\u00EAm t\u1EEB\" \u0111\u1EC3 b\u1EAFt \u0111\u1EA7u." })] }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-purple-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-purple-400 to-pink-400" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-purple-100", children: _jsx(Tags, { className: "h-3.5 w-3.5 text-purple-600" }) }), _jsxs(CardTitle, { className: "text-lg font-semibold text-gray-800", children: ["V\u1ECB tr\u00ED c\u1EA7n \u0111i\u1EC1n ", _jsxs("span", { className: "text-sm font-normal text-gray-500", children: ["(", labels.length, " v\u1ECB tr\u00ED)"] })] })] }), _jsxs(Button, { type: "button", size: "sm", onClick: addLabel, className: "gap-2 border-purple-200 text-purple-600 hover:bg-purple-50 hover:text-purple-700", variant: "outline", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm v\u1ECB tr\u00ED"] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [errors.labels && (_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("span", { className: "text-lg", children: "\u274C" }), _jsx("p", { className: "text-sm text-red-600", children: errors.labels })] })), _jsx("div", { className: "space-y-4", children: labels
333
+ : 'bg-gray-100 text-gray-600'}`, children: index + 1 }), _jsx(Input, { value: word, onChange: (e) => updateWordInBank(index, e.target.value), placeholder: `Từ ${index + 1}`, className: `flex-1 border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${errors.wordBankItems?.[index] ? 'border-red-400' : ''}` }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeWordFromBank(index), className: "text-gray-400 hover:bg-red-50 hover:text-red-500", children: _jsx(X, { className: "h-4 w-4" }) })] }), errors.wordBankItems?.[index] && (_jsx("p", { className: "mt-2 ml-11 text-sm text-red-600", children: errors.wordBankItems[index] }))] }, index))) }), wordBank.length === 0 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4", children: [_jsx("span", { className: "text-lg", children: "\u26A0\uFE0F" }), _jsx("p", { className: "text-sm text-amber-800", children: "Ch\u01B0a c\u00F3 t\u1EEB n\u00E0o. Nh\u1EA5n \"Th\u00EAm t\u1EEB\" \u0111\u1EC3 b\u1EAFt \u0111\u1EA7u." })] }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: [_jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-purple-100", children: _jsx(Tags, { className: "h-3.5 w-3.5 text-purple-600" }) }), _jsxs(CardTitle, { className: "text-lg font-semibold text-gray-800", children: ["V\u1ECB tr\u00ED c\u1EA7n \u0111i\u1EC1n ", _jsxs("span", { className: "text-sm font-normal text-gray-500", children: ["(", labels.length, " v\u1ECB tr\u00ED)"] })] })] }), _jsxs(Button, { type: "button", size: "sm", onClick: addLabel, className: "gap-2 border-purple-200 text-purple-600 hover:bg-purple-50 hover:text-purple-700", variant: "outline", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm v\u1ECB tr\u00ED"] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [errors.labels && (_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("span", { className: "text-lg", children: "\u274C" }), _jsx("p", { className: "text-sm text-red-600", children: errors.labels })] })), _jsx("div", { className: "space-y-4", children: labels
330
334
  .sort((a, b) => a.number - b.number)
331
335
  .map((label) => {
332
336
  const hasAnswer = label.correctAnswer && label.correctAnswer.trim();
@@ -337,7 +341,7 @@ function LabelThePictureCreatorContent({ initialData, onSave, onCancel, onChange
337
341
  : 'bg-purple-100 text-purple-700'}`, children: label.number }), _jsxs("div", { className: "flex-1 space-y-3", children: [_jsxs("div", { children: [_jsxs(Label, { className: "text-sm font-semibold text-gray-700", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx("div", { className: "mt-1.5", children: _jsxs(Select, { value: label.correctAnswer || '', onValueChange: (value) => updateLabel(label.id, 'correctAnswer', value), children: [_jsx(SelectTrigger, { className: `w-full border-gray-200 transition-all focus:border-indigo-300 focus:ring-indigo-200 ${errors.labelItems?.[label.id] ? 'border-red-400' : ''}`, children: _jsx(SelectValue, { placeholder: "Ch\u1ECDn t\u1EEB trong word bank" }) }), _jsx(SelectContent, { children: wordBank
338
342
  .filter((word) => word.trim() !== '')
339
343
  .map((word, index) => (_jsx(SelectItem, { value: word, children: word }, index))) })] }) }), errors.labelItems?.[label.id] && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: errors.labelItems[label.id] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("input", { type: "checkbox", id: `prefilled-${label.id}`, checked: label.isPreFilled || false, onChange: (e) => updateLabel(label.id, 'isPreFilled', e.target.checked), className: "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500" }), _jsx(Label, { htmlFor: `prefilled-${label.id}`, className: "text-sm font-normal text-gray-600 cursor-pointer", children: "\u0110\u00E1nh d\u1EA5u l\u00E0 t\u1EEB g\u1EE3i \u00FD (\u0111\u00E3 \u0111i\u1EC1n s\u1EB5n)" })] })] }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeLabel(label.id), className: "text-gray-400 hover:bg-red-50 hover:text-red-500", children: _jsx(X, { className: "h-4 w-4" }) })] }) }, label.id));
340
- }) }), labels.length === 0 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4", children: [_jsx("span", { className: "text-lg", children: "\u26A0\uFE0F" }), _jsx("p", { className: "text-sm text-amber-800", children: "Ch\u01B0a c\u00F3 v\u1ECB tr\u00ED n\u00E0o. Nh\u1EA5n \"Th\u00EAm v\u1ECB tr\u00ED\" \u0111\u1EC3 b\u1EAFt \u0111\u1EA7u." })] }))] })] }), _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) => 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" }) })] })] }));
344
+ }) }), labels.length === 0 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4", children: [_jsx("span", { className: "text-lg", children: "\u26A0\uFE0F" }), _jsx("p", { className: "text-sm text-amber-800", children: "Ch\u01B0a c\u00F3 v\u1ECB tr\u00ED n\u00E0o. Nh\u1EA5n \"Th\u00EAm v\u1ECB tr\u00ED\" \u0111\u1EC3 b\u1EAFt \u0111\u1EA7u." })] }))] })] }), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation })] }));
341
345
  }
342
346
  export function LabelThePictureCreator(props) {
343
347
  return _jsx(LabelThePictureCreatorContent, { ...props });
@@ -47,9 +47,9 @@ export function ListenAndChooseFromAnswerGroupClient({ questionData, onSubmit, i
47
47
  const hasAnswered = !!selectedAnswer;
48
48
  const selectedOption = questionData.options.find((o) => o.id === selectedAnswer);
49
49
  const isAnswerCorrect = isReviewMode && selectedAnswer === resolvedCorrectAnswer;
50
- return (_jsxs("div", { className: "space-y-4", children: [questionData.isBackup && (_jsxs("div", { className: "inline-flex self-center items-center gap-1.5 px-3 py-1.5 rounded-md bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold shadow-sm", children: [_jsx("span", { children: "\uD83D\uDD04" }), " C\u00E2u h\u1ECFi d\u1EF1 ph\u00F2ng"] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-orange-50 to-amber-50 px-5 py-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-orange-500 to-amber-600 text-white shadow-sm", children: _jsx(Volume2, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Nghe v\u00E0 ch\u1ECDn trong nh\u00F3m \u0111\u00E1p \u00E1n" })] }), isReviewMode && hasAnswered && (_jsx("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold', isAnswerCorrect
50
+ return (_jsxs("div", { className: "space-y-4", children: [questionData.isBackup && (_jsxs("div", { className: "inline-flex self-center items-center gap-1.5 px-3 py-1.5 rounded-md bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold shadow-sm", children: [_jsx("span", { children: "\uD83D\uDD04" }), " C\u00E2u h\u1ECFi d\u1EF1 ph\u00F2ng"] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gray-50 px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100 text-gray-600", children: _jsx(Volume2, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Nghe v\u00E0 ch\u1ECDn trong nh\u00F3m \u0111\u00E1p \u00E1n" })] }), isReviewMode && hasAnswered && (_jsx("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold', isAnswerCorrect
51
51
  ? 'bg-green-100 text-green-700'
52
- : 'bg-red-100 text-red-700'), children: isAnswerCorrect ? (_jsxs(_Fragment, { children: [_jsx(Check, { className: "h-3.5 w-3.5" }), " \u0110\u00FAng"] })) : (_jsxs(_Fragment, { children: [_jsx(X, { className: "h-3.5 w-3.5" }), " Sai"] })) }))] }), _jsxs("div", { className: "p-5 space-y-5", children: [questionData.audioUrl && (_jsx(QuestionAudioPlayer, { audioUrl: questionData.audioUrl })), _jsxs("div", { className: "space-y-2", children: [_jsx("span", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng" }), _jsx("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", children: questionData.options.map((option, optIndex) => {
52
+ : 'bg-red-100 text-red-700'), children: isAnswerCorrect ? (_jsxs(_Fragment, { children: [_jsx(Check, { className: "h-3.5 w-3.5" }), " \u0110\u00FAng"] })) : (_jsxs(_Fragment, { children: [_jsx(X, { className: "h-3.5 w-3.5" }), " Sai"] })) }))] }), _jsxs("div", { className: "p-3 space-y-3", children: [questionData.audioUrl && (_jsx(QuestionAudioPlayer, { audioUrl: questionData.audioUrl })), _jsxs("div", { className: "space-y-2", children: [_jsx("span", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng" }), _jsx("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", children: questionData.options.map((option, optIndex) => {
53
53
  const { isSelected, isCorrect, isWrong } = getOptionStatus(option.id);
54
54
  return (_jsxs("button", { type: "button", onClick: () => handleOptionSelect(option.id), disabled: isReviewMode, className: cn('group relative flex flex-col items-center gap-2 rounded-xl border-2 p-3 transition-all', isReviewMode
55
55
  ? isCorrect
@@ -3,19 +3,18 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useRef, useEffect } from 'react';
4
4
  import { Button } from '../../../../components/ui/button';
5
5
  import { Input } from '../../../../components/ui/input';
6
- import { Textarea } from '../../../../components/ui/textarea';
7
6
  import { Label } from '../../../../components/ui/label';
8
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
9
- import { Pencil, Eye, Play, Pause, Plus, Trash2, Music, CheckCircle2, ListChecks, ImageIcon, BookOpen } from 'lucide-react';
7
+ import { Card, CardContent, CardHeader } from '../../../../components/ui/card';
8
+ import { Play, Pause, Plus, Trash2, Music, CheckCircle2, ListChecks, ImageIcon } from 'lucide-react';
10
9
  import { toast } from 'sonner';
11
10
  import { FileUpload } from '../../../../components/ui/file-upload';
11
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
12
12
  import { ListenAndChooseFromAnswerGroupClient } from './ListenAndChooseFromAnswerGroupClient';
13
13
  import { usePresignedFileUrl, useAudioPreview, useDebouncedCallback } from '../../../../shared/lib/hooks';
14
14
  import { useReactHookForm } from '../../../../shared/lib/hooks/useReactHookForm';
15
15
  import { listenAndChooseFromAnswerGroupFormSchema } from '../../../../shared/lib/validation/listen-and-choose-from-answer-group.validation';
16
16
  import { useAnswerGroupQuestionGroup } from '../../_shared/hooks/useAnswerGroupQuestionGroup';
17
17
  import { generateObjectId } from '../../_shared/utils/id-generator';
18
- import { CreatorGuide } from '../../components/CreatorGuide';
19
18
  // ─── Sub-component: Audio preview ────────────────────────────────────────────
20
19
  function AudioPreview({ audioUrl }) {
21
20
  const { previewUrl } = usePresignedFileUrl(audioUrl);
@@ -190,34 +189,35 @@ function ListenAndChooseFromAnswerGroupCreatorContent({ initialData, onSave, onC
190
189
  explanation,
191
190
  isBackup,
192
191
  };
193
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Nghe v\u00E0 ch\u1ECDn trong nh\u00F3m \u0111\u00E1p \u00E1n (Preview)" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Ch\u1EBF \u0111\u1ED9 xem tr\u01B0\u1EDBc" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(false), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), "Quay l\u1EA1i Edit Mode"] })] }) }) }), _jsx(ListenAndChooseFromAnswerGroupClient, { questionData: questionData, onSubmit: () => { }, isReviewMode: true, userAnswer: correctAnswer || undefined, correctAnswer: correctAnswer || undefined, explanation: explanation })] }));
192
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(ListenAndChooseFromAnswerGroupClient, { questionData: questionData, onSubmit: () => { }, isReviewMode: true, userAnswer: correctAnswer || undefined, correctAnswer: correctAnswer || undefined, explanation: explanation })] }));
194
193
  }
195
194
  // ─── Creator UI ────────────────────────────────────────────────────────────
196
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(CreatorGuide, { colorScheme: "orange", steps: [
197
- { text: 'Upload các hình ảnh lựa chọn (options) cho nhóm — học sinh sẽ chọn đáp án đúng' },
198
- { text: 'Mỗi option cần có hình ảnh và nhãn mô tả' },
199
- { text: 'Upload audio câu hỏi cho từng câu' },
200
- { text: 'Chọn đáp án đúng (correct answer) từ danh sách options', highlight: true },
201
- ], tips: ['Nên có ít nhất 3-4 options để tạo sự phân biệt rõ ràng'] }), _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-orange-500 via-amber-500 to-yellow-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-orange-500 to-amber-600 shadow-md", children: _jsx(ListChecks, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold text-gray-900", children: "Nh\u00F3m \u0111\u00E1p \u00E1n" }), _jsxs("div", { className: "mt-1.5 inline-flex items-center gap-1.5 rounded-full bg-orange-100 px-3 py-1 text-sm font-medium text-orange-700", children: [_jsx("span", { children: "\uD83D\uDDBC\uFE0F" }), _jsxs("span", { children: ["Nh\u00F3m ", currentGroupNumber] }), _jsx("span", { className: "text-xs font-normal opacity-75 ml-1", children: "(D\u00F9ng chung)" })] })] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: options.filter((o) => o.imageUrl).length < 2 || !audioUrl, className: "gap-2 border-orange-200 text-orange-600 hover:bg-orange-50 hover:text-orange-700", children: [_jsx(Eye, { className: "h-4 w-4" }), "Preview"] })] }) })] }), _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" }), _jsxs(CardHeader, { className: "pb-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-amber-100", children: _jsx(ListChecks, { className: "h-3.5 w-3.5 text-amber-600" }) }), _jsxs(CardTitle, { className: "text-lg font-semibold text-gray-800", children: ["Nh\u00F3m \u0111\u00E1p \u00E1n (", options.length, " \u0111\u00E1p \u00E1n)"] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: addOption, 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 \u0111\u00E1p \u00E1n"] })] }), form.formState.errors.options?.root && (_jsx("div", { className: "px-4 pb-2", children: _jsx("p", { className: "text-sm text-red-500", children: form.formState.errors.options.root.message }) }))] }), _jsxs(CardContent, { className: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [options.map((option, index) => (_jsxs(Card, { className: "border border-gray-200", children: [_jsx(CardHeader, { className: "pb-2 pt-3 px-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("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-600 text-white text-sm font-bold shadow-sm", children: String.fromCharCode(65 + index) }), _jsxs("span", { className: "text-sm font-semibold text-gray-700", children: ["\u0110\u00E1p \u00E1n ", String.fromCharCode(65 + index)] })] }), options.length > 2 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => removeOption(option.id), className: "h-7 w-7 p-0 text-red-500 hover:text-red-700 hover:bg-red-50", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }) }), _jsxs(CardContent, { className: "space-y-3 px-4 pb-4", children: [_jsxs("div", { children: [_jsx(FileUpload, { id: `option-image-${option.id}`, label: "\u1EA2nh \u0111\u00E1p \u00E1n", accept: "image/*", value: option.imageUrl, onChange: (url) => updateOptionImageUrl(option.id, url), maxSize: 5, placeholder: "Upload \u1EA3nh cho \u0111\u00E1p \u00E1n", required: true, autoUpload: true, prefix: "questions" }), form.formState.errors.options?.[index]?.imageUrl && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: form.formState.errors.options[index].imageUrl.message })), option.imageUrl && _jsx(OptionImagePreview, { imageUrl: option.imageUrl })] }), _jsxs("div", { children: [_jsx(Label, { className: "font-medium", children: "Nh\u00E3n" }), _jsx(Input, { value: option.label || '', onChange: (e) => updateOptionLabel(option.id, e.target.value), placeholder: "Nh\u1EADp label / m\u00F4 t\u1EA3 ng\u1EAFn", className: "mt-1" })] })] })] }, option.id))), options.length === 0 && (_jsxs("div", { className: "col-span-1 md:col-span-2 flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-amber-300 p-8 bg-amber-50/50", children: [_jsx(ImageIcon, { className: "h-10 w-10 text-amber-400 mb-2" }), _jsx("p", { className: "text-sm text-gray-500 mb-3", children: "Ch\u01B0a c\u00F3 \u0111\u00E1p \u00E1n n\u00E0o" }), _jsxs(Button, { variant: "outline", size: "sm", onClick: addOption, className: "gap-2", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm \u0111\u00E1p \u00E1n \u0111\u1EA7u ti\u00EAn"] })] }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-indigo-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-indigo-400 to-purple-400" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100", children: _jsx(Music, { className: "h-3.5 w-3.5 text-indigo-600" }) }), _jsx(CardTitle, { className: "text-lg font-semibold text-gray-800", children: "C\u00E2u h\u1ECFi" })] }) }), _jsxs(CardContent, { className: "space-y-6", children: [_jsx("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: _jsxs("div", { className: "flex flex-col md:flex-row gap-6", children: [_jsxs("div", { className: "flex-1", children: [_jsx(Label, { className: "text-sm font-semibold text-gray-700 mb-2 block", children: "\u0110i\u1EC3m *" }), _jsx(Input, { type: "number", min: 1, value: points ?? '', onChange: (e) => {
202
- const val = e.target.value;
203
- setPoints(val === '' ? undefined : parseInt(val, 10));
204
- form.clearErrors('points');
205
- }, placeholder: "Nh\u1EADp \u0111i\u1EC3m s\u1ED1", className: `w-40 bg-white ${form.formState.errors.points ? 'border-red-500' : ''}` }), form.formState.errors.points && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: form.formState.errors.points.message }))] }), _jsx("div", { className: "flex-1 flex flex-col justify-center pt-2" })] }) }), _jsxs("div", { children: [_jsx(FileUpload, { id: "question-audio", label: "File \u00E2m thanh", accept: "audio/*", value: audioUrl, onChange: (url) => {
206
- setAudioUrl(url);
207
- form.clearErrors('audioUrl');
208
- }, maxSize: 10, placeholder: "Upload file audio", required: true, autoUpload: true, prefix: "questions" }), form.formState.errors.audioUrl && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: form.formState.errors.audioUrl.message })), audioUrl && _jsx(AudioPreview, { audioUrl: audioUrl })] }), _jsxs("div", { children: [_jsx(Label, { className: "text-sm font-semibold text-gray-700 mb-2 block", children: "Ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng *" }), options.filter((o) => o.imageUrl).length > 0 ? (_jsx("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", children: options.map((option, optIndex) => {
209
- if (!option.imageUrl)
210
- return null;
211
- const isSelected = correctAnswer === option.id;
212
- return (_jsxs("button", { type: "button", onClick: () => {
213
- setCorrectAnswer(option.id);
214
- form.clearErrors('correctAnswer');
215
- }, className: `relative text-left rounded-xl border-2 p-2 transition-all ${isSelected
216
- ? 'border-green-500 bg-green-50 ring-2 ring-green-200 shadow-md'
217
- : 'border-gray-200 hover:border-gray-400 hover:shadow-sm'}`, children: [_jsxs("div", { className: "flex flex-col items-center gap-2", children: [_jsx("div", { className: `flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-lg text-sm font-bold shadow-sm ${isSelected
218
- ? 'bg-green-500 text-white'
219
- : 'bg-gray-100 text-gray-600'}`, children: String.fromCharCode(65 + optIndex) }), _jsx(OptionImagePreviewInline, { imageUrl: option.imageUrl }), option.label && (_jsx("p", { className: `text-xs text-center ${isSelected ? 'text-green-700 font-medium' : 'text-gray-600'}`, children: option.label }))] }), isSelected && (_jsx("div", { className: "absolute top-1 right-1", children: _jsx(CheckCircle2, { className: "h-5 w-5 text-green-500" }) }))] }, option.id));
220
- }) })) : (_jsx("p", { className: "text-sm text-amber-600 italic", children: "Vui l\u00F2ng upload \u1EA3nh cho \u0111\u00E1p \u00E1n \u1EDF ph\u1EA7n tr\u00EAn tr\u01B0\u1EDBc" })), form.formState.errors.correctAnswer && (_jsx("div", { className: "mt-3 rounded-lg border border-red-200 bg-red-50 p-3", children: _jsxs("p", { className: "text-sm text-red-700", children: ["\u26A0\uFE0F ", form.formState.errors.correctAnswer.message] }) })), !form.formState.errors.correctAnswer && !correctAnswer && options.filter((o) => o.imageUrl).length > 0 && (_jsx("p", { className: "mt-1 text-xs text-amber-600", children: "Ch\u01B0a ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng" }))] })] })] }), _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, { 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" }) })] })] }));
195
+ return (_jsxs("div", { className: "space-y-3", children: [_jsx(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(ListChecks, { className: "h-3.5 w-3.5" }), title: "Nh\u00F3m \u0111\u00E1p \u00E1n", extraActions: _jsxs("span", { className: "inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-600", children: ["Nh\u00F3m ", currentGroupNumber] }), points: {
196
+ value: points ?? 1,
197
+ onValueChange: (value) => {
198
+ setPoints(value);
199
+ form.clearErrors('points');
200
+ },
201
+ error: form.formState.errors.points?.message,
202
+ }, preview: {
203
+ onClick: () => setIsClientMode(true),
204
+ disabled: options.filter((o) => o.imageUrl).length < 2 || !audioUrl,
205
+ } }), contentClassName: "hidden", children: null }), _jsxs(CompactFieldCard, { header: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100", children: _jsx(ListChecks, { className: "h-3.5 w-3.5 text-gray-600" }) }), _jsxs("p", { className: "text-sm font-semibold text-gray-800", children: ["Nh\u00F3m \u0111\u00E1p \u00E1n (", options.length, " \u0111\u00E1p \u00E1n)"] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: addOption, 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 \u0111\u00E1p \u00E1n"] })] }), contentClassName: "grid grid-cols-1 md:grid-cols-2 gap-4", children: [form.formState.errors.options?.root && (_jsx("p", { className: "col-span-full text-sm text-red-500", children: form.formState.errors.options.root.message })), options.map((option, index) => (_jsxs(Card, { className: "border border-gray-200", children: [_jsx(CardHeader, { className: "pb-2 pt-3 px-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("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-600 text-white text-sm font-bold shadow-sm", children: String.fromCharCode(65 + index) }), _jsxs("span", { className: "text-sm font-semibold text-gray-700", children: ["\u0110\u00E1p \u00E1n ", String.fromCharCode(65 + index)] })] }), options.length > 2 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => removeOption(option.id), className: "h-7 w-7 p-0 text-red-500 hover:text-red-700 hover:bg-red-50", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }) }), _jsxs(CardContent, { className: "space-y-3 px-4 pb-4", children: [_jsxs("div", { children: [_jsx(FileUpload, { id: `option-image-${option.id}`, label: "\u1EA2nh \u0111\u00E1p \u00E1n", accept: "image/*", value: option.imageUrl, onChange: (url) => updateOptionImageUrl(option.id, url), maxSize: 5, placeholder: "Upload \u1EA3nh cho \u0111\u00E1p \u00E1n", required: true, autoUpload: true, prefix: "questions" }), form.formState.errors.options?.[index]?.imageUrl && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: form.formState.errors.options[index].imageUrl.message })), option.imageUrl && _jsx(OptionImagePreview, { imageUrl: option.imageUrl })] }), _jsxs("div", { children: [_jsx(Label, { className: "font-medium", children: "Nh\u00E3n" }), _jsx(Input, { value: option.label || '', onChange: (e) => updateOptionLabel(option.id, e.target.value), placeholder: "Nh\u1EADp label / m\u00F4 t\u1EA3 ng\u1EAFn", className: "mt-1" })] })] })] }, option.id))), options.length === 0 && (_jsxs("div", { className: "col-span-1 md:col-span-2 flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-amber-300 p-8 bg-amber-50/50", children: [_jsx(ImageIcon, { className: "h-10 w-10 text-amber-400 mb-2" }), _jsx("p", { className: "text-sm text-gray-500 mb-3", children: "Ch\u01B0a c\u00F3 \u0111\u00E1p \u00E1n n\u00E0o" }), _jsxs(Button, { variant: "outline", size: "sm", onClick: addOption, className: "gap-2", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm \u0111\u00E1p \u00E1n \u0111\u1EA7u ti\u00EAn"] })] }))] }), _jsxs(CompactFieldCard, { header: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100", children: _jsx(Music, { className: "h-3.5 w-3.5 text-gray-600" }) }), _jsx("p", { className: "text-sm font-semibold text-gray-800", children: "C\u00E2u h\u1ECFi" })] }), children: [_jsxs("div", { children: [_jsx(FileUpload, { id: "question-audio", label: "File \u00E2m thanh", accept: "audio/*", value: audioUrl, onChange: (url) => {
206
+ setAudioUrl(url);
207
+ form.clearErrors('audioUrl');
208
+ }, maxSize: 10, placeholder: "Upload file audio", required: true, autoUpload: true, prefix: "questions" }), form.formState.errors.audioUrl && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: form.formState.errors.audioUrl.message })), audioUrl && _jsx(AudioPreview, { audioUrl: audioUrl })] }), _jsxs("div", { children: [_jsx(Label, { className: "text-sm font-semibold text-gray-700 mb-2 block", children: "Ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng *" }), options.filter((o) => o.imageUrl).length > 0 ? (_jsx("div", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", children: options.map((option, optIndex) => {
209
+ if (!option.imageUrl)
210
+ return null;
211
+ const isSelected = correctAnswer === option.id;
212
+ return (_jsxs("button", { type: "button", onClick: () => {
213
+ setCorrectAnswer(option.id);
214
+ form.clearErrors('correctAnswer');
215
+ }, className: `relative text-left rounded-xl border-2 p-2 transition-all ${isSelected
216
+ ? 'border-green-500 bg-green-50 ring-2 ring-green-200 shadow-md'
217
+ : 'border-gray-200 hover:border-gray-400 hover:shadow-sm'}`, children: [_jsxs("div", { className: "flex flex-col items-center gap-2", children: [_jsx("div", { className: `flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-lg text-sm font-bold shadow-sm ${isSelected
218
+ ? 'bg-green-500 text-white'
219
+ : 'bg-gray-100 text-gray-600'}`, children: String.fromCharCode(65 + optIndex) }), _jsx(OptionImagePreviewInline, { imageUrl: option.imageUrl }), option.label && (_jsx("p", { className: `text-xs text-center ${isSelected ? 'text-green-700 font-medium' : 'text-gray-600'}`, children: option.label }))] }), isSelected && (_jsx("div", { className: "absolute top-1 right-1", children: _jsx(CheckCircle2, { className: "h-5 w-5 text-green-500" }) }))] }, option.id));
220
+ }) })) : (_jsx("p", { className: "text-sm text-amber-600 italic", children: "Vui l\u00F2ng upload \u1EA3nh cho \u0111\u00E1p \u00E1n \u1EDF ph\u1EA7n tr\u00EAn tr\u01B0\u1EDBc" })), form.formState.errors.correctAnswer && (_jsx("div", { className: "mt-3 rounded-lg border border-red-200 bg-red-50 p-3", children: _jsxs("p", { className: "text-sm text-red-700", children: ["\u26A0\uFE0F ", form.formState.errors.correctAnswer.message] }) })), !form.formState.errors.correctAnswer && !correctAnswer && options.filter((o) => o.imageUrl).length > 0 && (_jsx("p", { className: "mt-1 text-xs text-amber-600", children: "Ch\u01B0a ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng" }))] })] }), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(explanation) })] }));
221
221
  }
222
222
  // ─── Inline image preview (for correct answer selection) ─────────────────────
223
223
  function OptionImagePreviewInline({ imageUrl }) {
@@ -55,9 +55,9 @@ export function ListenAndChooseObjectsInSceneClient({ questionData, onSubmit, is
55
55
  const hasAnswered = selectedObjectId !== '';
56
56
  const selectedNode = questionData.objectNodes.find((n) => n.id === selectedObjectId);
57
57
  const isAnswerCorrect = isReviewMode && correctAnswer && selectedObjectId === correctAnswer.objectId;
58
- return (_jsxs("div", { className: "space-y-4", children: [questionData.isBackup && (_jsxs("div", { className: "inline-flex self-center items-center gap-1.5 px-3 py-1.5 rounded-md bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold shadow-sm", children: [_jsx("span", { children: "\uD83D\uDD04" }), " C\u00E2u h\u1ECFi d\u1EF1 ph\u00F2ng"] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_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-blue-500 to-indigo-600 text-white shadow-sm", children: _jsx(CircleHelp, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Nghe v\u00E0 ch\u1ECDn \u0111\u1ED1i t\u01B0\u1EE3ng trong c\u1EA3nh" })] }), isReviewMode && hasAnswered && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold', isAnswerCorrect
58
+ return (_jsxs("div", { className: "space-y-4", children: [questionData.isBackup && (_jsxs("div", { className: "inline-flex self-center items-center gap-1.5 px-3 py-1.5 rounded-md bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold shadow-sm", children: [_jsx("span", { children: "\uD83D\uDD04" }), " C\u00E2u h\u1ECFi d\u1EF1 ph\u00F2ng"] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gray-50 px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100 text-gray-600", children: _jsx(CircleHelp, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Nghe v\u00E0 ch\u1ECDn \u0111\u1ED1i t\u01B0\u1EE3ng trong c\u1EA3nh" })] }), isReviewMode && hasAnswered && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold', isAnswerCorrect
59
59
  ? 'bg-green-100 text-green-700'
60
- : 'bg-red-100 text-red-700'), children: [isAnswerCorrect ? (_jsx(Check, { className: "h-3.5 w-3.5" })) : (_jsx(X, { className: "h-3.5 w-3.5" })), isAnswerCorrect ? 'Đúng' : 'Sai'] }))] }), _jsxs("div", { className: "space-y-5 p-5", children: [audioUrl && (_jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-blue-100 bg-gradient-to-r from-blue-50 to-indigo-50 p-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-white", children: _jsx(Volume2, { className: "h-4 w-4" }) }), _jsx("audio", { controls: true, className: "h-8 w-full", src: audioUrl, children: "Tr\u00ECnh duy\u1EC7t kh\u00F4ng h\u1ED7 tr\u1EE3 audio." })] })), _jsxs("div", { className: "overflow-hidden rounded-lg border border-gray-200 bg-gradient-to-br from-slate-50 to-gray-100", children: [_jsx("div", { className: "border-b border-gray-100 px-3 py-1.5", children: _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "h-2 w-2 rounded-full bg-red-400" }), _jsx("div", { className: "h-2 w-2 rounded-full bg-yellow-400" }), _jsx("div", { className: "h-2 w-2 rounded-full bg-green-400" }), _jsx("span", { className: "ml-2 text-xs font-medium text-gray-500", children: "Scene" })] }) }), _jsx("div", { className: "relative p-2", children: bgLoading ? (_jsx("div", { className: "flex h-64 items-center justify-center", children: _jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-blue-500" }) })) : backgroundUrl ? (_jsxs("div", { className: "relative w-full", children: [_jsx("img", { src: backgroundUrl, alt: "Scene background", className: "w-full rounded-md object-contain", draggable: false }), questionData.objectNodes.map((node) => {
60
+ : 'bg-red-100 text-red-700'), children: [isAnswerCorrect ? (_jsx(Check, { className: "h-3.5 w-3.5" })) : (_jsx(X, { className: "h-3.5 w-3.5" })), isAnswerCorrect ? 'Đúng' : 'Sai'] }))] }), _jsxs("div", { className: "space-y-3 p-3", children: [audioUrl && (_jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-blue-100 bg-gradient-to-r from-blue-50 to-indigo-50 p-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-white", children: _jsx(Volume2, { className: "h-4 w-4" }) }), _jsx("audio", { controls: true, className: "h-8 w-full", src: audioUrl, children: "Tr\u00ECnh duy\u1EC7t kh\u00F4ng h\u1ED7 tr\u1EE3 audio." })] })), _jsxs("div", { className: "overflow-hidden rounded-lg border border-gray-200 bg-gradient-to-br from-slate-50 to-gray-100", children: [_jsx("div", { className: "border-b border-gray-100 px-3 py-1.5", children: _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "h-2 w-2 rounded-full bg-red-400" }), _jsx("div", { className: "h-2 w-2 rounded-full bg-yellow-400" }), _jsx("div", { className: "h-2 w-2 rounded-full bg-green-400" }), _jsx("span", { className: "ml-2 text-xs font-medium text-gray-500", children: "Scene" })] }) }), _jsx("div", { className: "relative p-2", children: bgLoading ? (_jsx("div", { className: "flex h-64 items-center justify-center", children: _jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-blue-500" }) })) : backgroundUrl ? (_jsxs("div", { className: "relative w-full", children: [_jsx("img", { src: backgroundUrl, alt: "Scene background", className: "w-full rounded-md object-contain", draggable: false }), questionData.objectNodes.map((node) => {
61
61
  const isSelected = selectedObjectId === node.id;
62
62
  const isCorrectNode = isReviewMode && correctAnswer?.objectId === node.id;
63
63
  const isWrongNode = isReviewMode && isSelected && correctAnswer?.objectId !== node.id;
@@ -4,12 +4,10 @@ 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
7
  import { RadioGroup, RadioGroupItem } from '../../../../components/ui/radio-group';
11
- import { Pencil, Eye, Plus, Trash2, Image as ImageIcon, Volume2, Lightbulb, MousePointerClick, GripVertical, ListChecks, BookOpen, } from 'lucide-react';
8
+ import { Plus, Trash2, Image as ImageIcon, Volume2, Lightbulb, MousePointerClick, GripVertical, ListChecks, } from 'lucide-react';
12
9
  import { FileUpload } from '../../../../components/ui/file-upload';
10
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
13
11
  import { usePresignedFileUrl, useDebouncedCallback } from '../../../../shared/lib/hooks';
14
12
  import { useReactHookForm } from '../../../../shared/lib/hooks/useReactHookForm';
15
13
  import { listenAndChooseObjectsInSceneFormSchema, } from '../../../../shared/lib/validation/listen-and-choose-objects-in-scene.validation';
@@ -17,7 +15,6 @@ import { ListenAndChooseObjectsInSceneClient } from './ListenAndChooseObjectsInS
17
15
  import { useSceneQuestionGroup } from '../../_shared/hooks/useSceneQuestionGroup';
18
16
  import { generateObjectId } from '../../_shared/utils/id-generator';
19
17
  import { useExamQuestionStore } from '../../../../shared/lib/stores/examQuestionStore';
20
- import { CreatorGuide } from '../../components/CreatorGuide';
21
18
  /**
22
19
  * Scene Object Editor - An individual object node editor row
23
20
  */
@@ -347,7 +344,7 @@ function ListenAndChooseObjectsInSceneCreatorContent({ initialData, onSave, onSa
347
344
  // ============ RENDER ============
348
345
  // Preview mode
349
346
  if (isClientMode) {
350
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Nghe v\u00E0 ch\u1ECDn \u0111\u1ED1i t\u01B0\u1EE3ng (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 ch\u1EC9nh s\u1EEDa"] })] }) }) }), _jsx(ListenAndChooseObjectsInSceneClient, { questionData: {
347
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(ListenAndChooseObjectsInSceneClient, { questionData: {
351
348
  audioUrl: audioPreviewUrl || audioUrl,
352
349
  backgroundImageUrl: backgroundPreviewUrl || backgroundImageUrl,
353
350
  objectNodes,
@@ -355,31 +352,27 @@ function ListenAndChooseObjectsInSceneCreatorContent({ initialData, onSave, onSa
355
352
  }
356
353
  const formErrors = form.formState.errors;
357
354
  // Creator mode
358
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(CreatorGuide, { colorScheme: "purple", steps: [
359
- { text: 'Upload ảnh nền (Scene) — ảnh này dùng chung cho tất cả câu hỏi trong nhóm' },
360
- { text: 'Nhấn "Thêm đối tượng" để tạo các object trên ảnh nền. Mỗi đối tượng cần có tên và hình ảnh riêng' },
361
- { text: 'Kéo thả đối tượng trên ảnh nền để điều chỉnh vị trí, hoặc nhập tọa độ X, Y, Width, Height thủ công' },
362
- { text: 'Upload audio câu hỏi — học sinh sẽ nghe audio rồi chọn đúng đối tượng trên ảnh' },
363
- { text: 'Chọn đáp án đúng cho câu hỏi này từ danh sách đối tượng đã tạo', highlight: true },
364
- { text: 'Thêm giải thích (không bắt buộc) sẽ hiển thị sau khi học sinh trả lời' },
365
- ], tips: [
366
- 'Số đối tượng nên nhiều hơn số câu hỏi trong part để tạo lựa chọn nhiễu. Đặt tên rõ ràng và chọn hình ảnh dễ phân biệt giữa các đối tượng',
367
- 'Kéo thả trực tiếp trên ảnh nền sẽ nhanh hơn nhập tọa độ thủ công',
368
- ] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-purple-100/50", children: [_jsx("div", { className: "h-1.5 bg-gradient-to-r from-purple-500 via-violet-500 to-fuchsia-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-purple-500 to-violet-600 shadow-md", children: _jsx(ListChecks, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold text-gray-900", children: "Nh\u00F3m \u0111\u00E1p \u00E1n" }), _jsxs("div", { className: "mt-1.5 inline-flex items-center gap-1.5 rounded-full bg-purple-100 px-3 py-1 text-sm font-medium text-purple-700", children: [_jsx("span", { children: "\uD83D\uDDBC\uFE0F" }), _jsxs("span", { children: ["Nh\u00F3m ", currentGroupNumber] }), _jsx("span", { className: "text-xs font-normal opacity-75 ml-1", children: "(D\u00F9ng chung)" })] })] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: !backgroundImageUrl || objectNodes.length < 2, className: "gap-2 border-purple-200 text-purple-600 hover:bg-purple-50 hover:text-purple-700", children: [_jsx(Eye, { className: "h-4 w-4" }), "Preview"] })] }) })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-purple-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-purple-400 to-violet-400" }), _jsx(CardHeader, { className: "pb-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(ImageIcon, { className: "h-3.5 w-3.5 text-purple-600" }) }), _jsx(CardTitle, { className: "text-lg font-semibold text-gray-800", children: "\u1EA2nh n\u1EC1n & \u0110\u1ED1i t\u01B0\u1EE3ng trong Scene" })] }) }), _jsxs(CardContent, { className: "space-y-6", children: [_jsx("div", { className: `rounded-xl border-2 p-5 bg-gradient-to-r from-blue-50 to-indigo-50 border-blue-200`, children: _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-blue-800", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-blue-600" }), "\u1EA2nh n\u1EC1n (Scene) ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(FileUpload, { id: "scene-background", label: "", accept: "image/*", value: backgroundImageUrl, onChange: (url) => {
369
- setBackgroundImageUrl(url);
370
- form.clearErrors();
371
- }, onPresignedUrlChange: setBackgroundPreviewUrl, maxSize: 10, placeholder: "Upload h\u00ECnh \u1EA3nh n\u1EC1n c\u1EA3nh", autoUpload: true, prefix: "questions/scenes" }), formErrors.backgroundImageUrl && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: formErrors.backgroundImageUrl.message })), backgroundPreviewUrl && (_jsxs("div", { className: "mt-2 overflow-hidden rounded-lg border border-gray-200 bg-gray-50 isolate", children: [_jsx("div", { className: "border-b border-gray-100 px-3 py-1.5", children: _jsx("span", { className: "text-xs font-medium text-gray-500", children: "\u1EA2nh n\u1EC1n - K\u00E9o th\u1EA3 \u0111\u1ED1i t\u01B0\u1EE3ng \u0111\u1EC3 \u0111\u1ECBnh v\u1ECB" }) }), _jsx("div", { className: "p-2", children: _jsx(ScenePreviewWithDrag, { backgroundUrl: backgroundPreviewUrl, objectNodes: objectNodes, correctObjectId: correctObjectId, onNodeDrag: handleNodeDrag, onNodeResize: handleNodeResize }) })] }))] }) }), _jsxs("div", { className: "pt-4 border-t border-gray-100", children: [_jsxs("div", { className: "flex items-center justify-between mb-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(MousePointerClick, { className: "h-3.5 w-3.5 text-indigo-600" }) }), _jsxs("h3", { className: "text-lg font-semibold text-gray-800", children: ["\u0110\u1ED1i t\u01B0\u1EE3ng (", objectNodes.length, " items)"] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: addObjectNode, 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 \u0111\u1ED1i t\u01B0\u1EE3ng"] })] }), _jsx("div", { className: "space-y-3", children: objectNodes.length === 0 ? (_jsxs("div", { className: "flex flex-col items-center gap-3 rounded-xl border-2 border-dashed border-gray-200 bg-gray-50/50 py-10", children: [_jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-gray-100", children: _jsx(ImageIcon, { className: "h-6 w-6 text-gray-400" }) }), _jsx("p", { className: "text-sm text-gray-500", children: "Ch\u01B0a c\u00F3 \u0111\u1ED1i t\u01B0\u1EE3ng n\u00E0o. Nh\u1EA5n \"Th\u00EAm \u0111\u1ED1i t\u01B0\u1EE3ng\" \u0111\u1EC3 t\u1EA1o c\u00E1c \u0111\u1ED1i t\u01B0\u1EE3ng chung cho nh\u00F3m." })] })) : (objectNodes.map((node, index) => (_jsx(SceneObjectEditor, { node: node, index: index, isCorrect: false, onUpdate: updateObjectNode, onRemove: removeObjectNode, onSelectCorrect: () => { }, errors: {
372
- label: formErrors.objectNodes?.[index]?.label?.message,
373
- imageUrl: formErrors.objectNodes?.[index]?.imageUrl?.message,
374
- } }, node.id)))) })] })] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-green-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-green-400 to-emerald-400" }), _jsx(CardHeader, { className: "pb-4", 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-green-100", children: _jsx(Volume2, { className: "h-4 w-4 text-green-600" }) }), _jsx(CardTitle, { className: "text-lg font-semibold text-gray-800", children: "Th\u00F4ng tin c\u00E2u h\u1ECFi" })] }) }), _jsxs(CardContent, { className: "space-y-6 pt-2", children: [_jsx("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: _jsxs("div", { className: "flex flex-col md:flex-row gap-6", children: [_jsx("div", { className: "flex-1", children: _jsx(PointsInput, { value: points, onChange: (e) => setPoints(parseFloat(e.target.value) || 1) }) }), _jsx("div", { className: "flex-1 flex flex-col justify-center" })] }) }), _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("span", { className: "text-red-500", children: "*" })] }), _jsx(FileUpload, { id: "scene-audio", label: "", accept: "audio/*", value: audioUrl, onChange: (url) => {
375
- setAudioUrl(url);
376
- form.clearErrors();
377
- }, onPresignedUrlChange: setAudioPreviewUrl, maxSize: 10, placeholder: "Upload file audio c\u00E2u h\u1ECFi", autoUpload: true, prefix: "questions/audio", examId: examId || undefined, partId: partId || undefined }), formErrors.audioUrl && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: formErrors.audioUrl.message })), audioPreviewUrl && (_jsx("audio", { controls: true, className: "mt-2 w-full", src: audioPreviewUrl, children: "Tr\u00ECnh duy\u1EC7t kh\u00F4ng h\u1ED7 tr\u1EE3 audio." }))] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-green-100", children: _jsx(Lightbulb, { className: "h-3.5 w-3.5 text-green-600" }) }), _jsxs(Label, { className: "text-base font-semibold text-gray-800", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng c\u1EE7a c\u00E2u h\u1ECFi n\u00E0y ", _jsx("span", { className: "text-red-500", children: "*" })] })] }), objectNodes.length === 0 ? (_jsx("p", { className: "text-sm text-amber-600 italic", children: "Vui l\u00F2ng th\u00EAm \u0111\u1ED1i t\u01B0\u1EE3ng \u1EDF b\u00EAn tr\u00EAn tr\u01B0\u1EDBc khi ch\u1ECDn \u0111\u00E1p \u00E1n." })) : (_jsx(RadioGroup, { value: correctObjectId, onValueChange: (val) => {
378
- setCorrectObjectId(val);
379
- form.clearErrors(); // Xóa lỗi validation khi người dùng đã chọn đáp án
380
- }, className: "grid gap-3 sm:grid-cols-2 md:grid-cols-3", children: objectNodes.map((node, index) => (_jsxs("label", { className: `flex cursor-pointer items-start gap-3 rounded-lg border-2 p-3 transition-all ${correctObjectId === node.id
381
- ? 'border-green-500 bg-green-50 ring-1 ring-green-500'
382
- : 'border-gray-200 hover:border-green-300 hover:bg-green-50/50'}`, children: [_jsx(RadioGroupItem, { value: node.id, id: `correct-${node.id}`, className: "mt-1 flex-shrink-0" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("p", { className: "font-medium text-gray-900 line-clamp-1 break-all", children: [index + 1, ". ", node.label || '(Chưa đặt tên)'] }), node.imageUrl ? (_jsx("div", { className: "mt-2 h-12 w-full max-w-16 rounded overflow-hidden bg-white border border-gray-200", children: _jsx(PresignedImage, { url: node.imageUrl, alt: node.label }) })) : (_jsx("div", { className: "mt-2 text-xs text-gray-400 italic", children: "Ch\u01B0a c\u00F3 h\u00ECnh" }))] })] }, node.id))) }))] }), formErrors.correctObjectId && (_jsx("div", { className: "rounded-lg border border-red-200 bg-red-50 p-3 mt-3", children: _jsxs("p", { className: "text-sm text-red-700", children: ["\u26A0\uFE0F ", formErrors.correctObjectId.message] }) }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-amber-400 to-orange-400" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm", children: _jsx(BookOpen, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-lg font-semibold text-gray-800", children: "Gi\u1EA3i th\u00EDch" }), _jsx("span", { className: "inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-600", children: "Kh\u00F4ng b\u1EAFt bu\u1ED9c" })] }), _jsx("p", { className: "text-sm text-gray-500 mt-0.5", children: "Gi\u1EA3i th\u00EDch cho \u0111\u00E1p \u00E1n \u0111\u00FAng \u2014 hi\u1EC3n th\u1ECB sau khi h\u1ECDc sinh ho\u00E0n th\u00E0nh c\u00E2u h\u1ECFi" })] })] }) }), _jsx(CardContent, { children: _jsx(Textarea, { 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" }) })] })] }));
355
+ return (_jsxs("div", { className: "space-y-3", children: [_jsx(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(ListChecks, { className: "h-3.5 w-3.5" }), title: "Nh\u00F3m \u0111\u00E1p \u00E1n", extraActions: _jsxs("span", { className: "inline-flex items-center rounded-full bg-gray-100 px-2 py-0.5 text-xs font-medium text-gray-600", children: ["Nh\u00F3m ", currentGroupNumber] }), points: {
356
+ value: points,
357
+ onValueChange: setPoints,
358
+ }, preview: {
359
+ onClick: () => setIsClientMode(true),
360
+ disabled: !backgroundImageUrl || objectNodes.length < 2,
361
+ } }), contentClassName: "hidden", children: null }), _jsxs(CompactFieldCard, { header: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100", children: _jsx(ImageIcon, { className: "h-3.5 w-3.5 text-gray-600" }) }), _jsx("p", { className: "text-sm font-semibold text-gray-800", children: "\u1EA2nh n\u1EC1n & \u0110\u1ED1i t\u01B0\u1EE3ng trong Scene" })] }), children: [_jsx("div", { className: `rounded-xl border-2 p-5 bg-gradient-to-r from-blue-50 to-indigo-50 border-blue-200`, children: _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-blue-800", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-blue-600" }), "\u1EA2nh n\u1EC1n (Scene) ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(FileUpload, { id: "scene-background", label: "", accept: "image/*", value: backgroundImageUrl, onChange: (url) => {
362
+ setBackgroundImageUrl(url);
363
+ form.clearErrors();
364
+ }, onPresignedUrlChange: setBackgroundPreviewUrl, maxSize: 10, placeholder: "Upload h\u00ECnh \u1EA3nh n\u1EC1n c\u1EA3nh", autoUpload: true, prefix: "questions/scenes" }), formErrors.backgroundImageUrl && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: formErrors.backgroundImageUrl.message })), backgroundPreviewUrl && (_jsxs("div", { className: "mt-2 overflow-hidden rounded-lg border border-gray-200 bg-gray-50 isolate", children: [_jsx("div", { className: "border-b border-gray-100 px-3 py-1.5", children: _jsx("span", { className: "text-xs font-medium text-gray-500", children: "\u1EA2nh n\u1EC1n - K\u00E9o th\u1EA3 \u0111\u1ED1i t\u01B0\u1EE3ng \u0111\u1EC3 \u0111\u1ECBnh v\u1ECB" }) }), _jsx("div", { className: "p-2", children: _jsx(ScenePreviewWithDrag, { backgroundUrl: backgroundPreviewUrl, objectNodes: objectNodes, correctObjectId: correctObjectId, onNodeDrag: handleNodeDrag, onNodeResize: handleNodeResize }) })] }))] }) }), _jsxs("div", { className: "pt-4 border-t border-gray-100", children: [_jsxs("div", { className: "flex items-center justify-between mb-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(MousePointerClick, { className: "h-3.5 w-3.5 text-indigo-600" }) }), _jsxs("h3", { className: "text-lg font-semibold text-gray-800", children: ["\u0110\u1ED1i t\u01B0\u1EE3ng (", objectNodes.length, " items)"] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: addObjectNode, 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 \u0111\u1ED1i t\u01B0\u1EE3ng"] })] }), _jsx("div", { className: "space-y-3", children: objectNodes.length === 0 ? (_jsxs("div", { className: "flex flex-col items-center gap-3 rounded-xl border-2 border-dashed border-gray-200 bg-gray-50/50 py-10", children: [_jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-gray-100", children: _jsx(ImageIcon, { className: "h-6 w-6 text-gray-400" }) }), _jsx("p", { className: "text-sm text-gray-500", children: "Ch\u01B0a c\u00F3 \u0111\u1ED1i t\u01B0\u1EE3ng n\u00E0o. Nh\u1EA5n \"Th\u00EAm \u0111\u1ED1i t\u01B0\u1EE3ng\" \u0111\u1EC3 t\u1EA1o c\u00E1c \u0111\u1ED1i t\u01B0\u1EE3ng chung cho nh\u00F3m." })] })) : (objectNodes.map((node, index) => (_jsx(SceneObjectEditor, { node: node, index: index, isCorrect: false, onUpdate: updateObjectNode, onRemove: removeObjectNode, onSelectCorrect: () => { }, errors: {
365
+ label: formErrors.objectNodes?.[index]?.label?.message,
366
+ imageUrl: formErrors.objectNodes?.[index]?.imageUrl?.message,
367
+ } }, node.id)))) })] })] }), _jsxs(CompactFieldCard, { header: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100", children: _jsx(Volume2, { className: "h-3.5 w-3.5 text-gray-600" }) }), _jsx("p", { className: "text-sm font-semibold text-gray-800", children: "Th\u00F4ng tin c\u00E2u h\u1ECFi" })] }), children: [_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(Volume2, { className: "h-4 w-4 text-indigo-500" }), "Audio c\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(FileUpload, { id: "scene-audio", label: "", accept: "audio/*", value: audioUrl, onChange: (url) => {
368
+ setAudioUrl(url);
369
+ form.clearErrors();
370
+ }, onPresignedUrlChange: setAudioPreviewUrl, maxSize: 10, placeholder: "Upload file audio c\u00E2u h\u1ECFi", autoUpload: true, prefix: "questions/audio", examId: examId || undefined, partId: partId || undefined }), formErrors.audioUrl && (_jsx("p", { className: "mt-1 text-sm text-red-500", children: formErrors.audioUrl.message })), audioPreviewUrl && (_jsx("audio", { controls: true, className: "mt-2 w-full", src: audioPreviewUrl, children: "Tr\u00ECnh duy\u1EC7t kh\u00F4ng h\u1ED7 tr\u1EE3 audio." }))] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-green-100", children: _jsx(Lightbulb, { className: "h-3.5 w-3.5 text-green-600" }) }), _jsxs(Label, { className: "text-base font-semibold text-gray-800", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng c\u1EE7a c\u00E2u h\u1ECFi n\u00E0y ", _jsx("span", { className: "text-red-500", children: "*" })] })] }), objectNodes.length === 0 ? (_jsx("p", { className: "text-sm text-amber-600 italic", children: "Vui l\u00F2ng th\u00EAm \u0111\u1ED1i t\u01B0\u1EE3ng \u1EDF b\u00EAn tr\u00EAn tr\u01B0\u1EDBc khi ch\u1ECDn \u0111\u00E1p \u00E1n." })) : (_jsx(RadioGroup, { value: correctObjectId, onValueChange: (val) => {
371
+ setCorrectObjectId(val);
372
+ form.clearErrors(); // Xóa lỗi validation khi người dùng đã chọn đáp án
373
+ }, className: "grid gap-3 sm:grid-cols-2 md:grid-cols-3", children: objectNodes.map((node, index) => (_jsxs("label", { className: `flex cursor-pointer items-start gap-3 rounded-lg border-2 p-3 transition-all ${correctObjectId === node.id
374
+ ? 'border-green-500 bg-green-50 ring-1 ring-green-500'
375
+ : 'border-gray-200 hover:border-green-300 hover:bg-green-50/50'}`, children: [_jsx(RadioGroupItem, { value: node.id, id: `correct-${node.id}`, className: "mt-1 flex-shrink-0" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsxs("p", { className: "font-medium text-gray-900 line-clamp-1 break-all", children: [index + 1, ". ", node.label || '(Chưa đặt tên)'] }), node.imageUrl ? (_jsx("div", { className: "mt-2 h-12 w-full max-w-16 rounded overflow-hidden bg-white border border-gray-200", children: _jsx(PresignedImage, { url: node.imageUrl, alt: node.label }) })) : (_jsx("div", { className: "mt-2 text-xs text-gray-400 italic", children: "Ch\u01B0a c\u00F3 h\u00ECnh" }))] })] }, node.id))) }))] }), formErrors.correctObjectId && (_jsx("div", { className: "rounded-lg border border-red-200 bg-red-50 p-3 mt-3", children: _jsxs("p", { className: "text-sm text-red-700", children: ["\u26A0\uFE0F ", formErrors.correctObjectId.message] }) }))] }), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(explanation) })] }));
383
376
  }
384
377
  export function ListenAndChooseObjectsInSceneCreator(props) {
385
378
  return _jsx(ListenAndChooseObjectsInSceneCreatorContent, { ...props });
@@ -76,9 +76,9 @@ export function ListenAndDragObjectsIntoSceneClient({ questionData, onSubmit, is
76
76
  selectedDropZoneId === correctAnswer.dropZoneId;
77
77
  const selectedObject = questionData.objectNodes.find((n) => n.id === selectedObjectId);
78
78
  const selectedDropZone = questionData.dropZones.find((z) => z.id === selectedDropZoneId);
79
- return (_jsxs("div", { className: "space-y-4", children: [questionData.isBackup && (_jsxs("div", { className: "inline-flex self-center items-center gap-1.5 px-3 py-1.5 rounded-md bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold shadow-sm", children: [_jsx("span", { children: "\uD83D\uDD04" }), " C\u00E2u h\u1ECFi d\u1EF1 ph\u00F2ng"] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_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(CircleHelp, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Nghe v\u00E0 k\u00E9o th\u1EA3 \u0111\u1ED1i t\u01B0\u1EE3ng v\u00E0o c\u1EA3nh" })] }), isReviewMode && hasAnswered && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold', isAnswerCorrect
79
+ return (_jsxs("div", { className: "space-y-4", children: [questionData.isBackup && (_jsxs("div", { className: "inline-flex self-center items-center gap-1.5 px-3 py-1.5 rounded-md bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold shadow-sm", children: [_jsx("span", { children: "\uD83D\uDD04" }), " C\u00E2u h\u1ECFi d\u1EF1 ph\u00F2ng"] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gray-50 px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100 text-gray-600", children: _jsx(CircleHelp, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Nghe v\u00E0 k\u00E9o th\u1EA3 \u0111\u1ED1i t\u01B0\u1EE3ng v\u00E0o c\u1EA3nh" })] }), isReviewMode && hasAnswered && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold', isAnswerCorrect
80
80
  ? 'bg-green-100 text-green-700'
81
- : 'bg-red-100 text-red-700'), children: [isAnswerCorrect ? (_jsx(Check, { className: "h-3.5 w-3.5" })) : (_jsx(X, { className: "h-3.5 w-3.5" })), isAnswerCorrect ? 'Đúng' : 'Sai'] }))] }), _jsxs("div", { className: "space-y-5 p-5", children: [audioUrl && (_jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-orange-100 bg-gradient-to-r from-orange-50 to-amber-50 p-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-orange-500 text-white", children: _jsx(Volume2, { className: "h-4 w-4" }) }), _jsx("audio", { controls: true, className: "h-8 w-full", src: audioUrl, children: "Tr\u00ECnh duy\u1EC7t kh\u00F4ng h\u1ED7 tr\u1EE3 audio." })] })), _jsxs("div", { className: "flex gap-4", children: [_jsx("div", { className: "flex w-24 flex-shrink-0 flex-col gap-2", children: questionData.objectNodes.slice(0, Math.ceil(questionData.objectNodes.length / 2)).map((node) => {
81
+ : 'bg-red-100 text-red-700'), children: [isAnswerCorrect ? (_jsx(Check, { className: "h-3.5 w-3.5" })) : (_jsx(X, { className: "h-3.5 w-3.5" })), isAnswerCorrect ? 'Đúng' : 'Sai'] }))] }), _jsxs("div", { className: "space-y-3 p-3", children: [audioUrl && (_jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-orange-100 bg-gradient-to-r from-orange-50 to-amber-50 p-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-orange-500 text-white", children: _jsx(Volume2, { className: "h-4 w-4" }) }), _jsx("audio", { controls: true, className: "h-8 w-full", src: audioUrl, children: "Tr\u00ECnh duy\u1EC7t kh\u00F4ng h\u1ED7 tr\u1EE3 audio." })] })), _jsxs("div", { className: "flex gap-4", children: [_jsx("div", { className: "flex w-24 flex-shrink-0 flex-col gap-2", children: questionData.objectNodes.slice(0, Math.ceil(questionData.objectNodes.length / 2)).map((node) => {
82
82
  const isSelected = selectedObjectId === node.id;
83
83
  const isDragging = draggedObjectId === node.id;
84
84
  const isCorrectObj = isReviewMode && correctAnswer?.objectId === node.id;