@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
@@ -1,14 +1,11 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useRef, useEffect } from 'react';
4
- import { Button } from '../../../../components/ui/button';
5
4
  import { Label } from '../../../../components/ui/label';
6
5
  import { Input } from '../../../../components/ui/input';
7
- import { Textarea } from '../../../../components/ui/textarea';
8
- import { PointsInput } from '../../../../components/ui/points-input';
9
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
10
6
  import { Switch } from '../../../../components/ui/switch';
11
- import { Pencil, Eye, Lightbulb, PenTool, MessageSquare, ToggleLeft, CheckCircle2, BookOpen } from 'lucide-react';
7
+ import { Eye, PenTool, MessageSquare, ToggleLeft, CheckCircle2 } from 'lucide-react';
8
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
12
9
  import { WriteCorrectVerbFormClient } from './WriteCorrectVerbFormClient';
13
10
  import { useDebouncedCallback } from '../../../../shared/lib/hooks';
14
11
  function WriteCorrectVerbFormCreatorContent({ initialData, onSave, onCancel, onChange, externalErrors, onUnsavedChangesChange, validationRef, }) {
@@ -171,25 +168,28 @@ function WriteCorrectVerbFormCreatorContent({ initialData, onSave, onCancel, onC
171
168
  caseSensitive,
172
169
  instruction,
173
170
  };
174
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(CardTitle, { children: "Write the Correct Form of the Verb (Preview)" }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(false), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), "Quay l\u1EA1i Edit Mode"] })] }) }) }), _jsx(WriteCorrectVerbFormClient, { questionData: { ...questionData, explanation }, isReviewMode: true, userAnswer: correctAnswer })] }));
171
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(WriteCorrectVerbFormClient, { questionData: { ...questionData, explanation }, isReviewMode: true, userAnswer: correctAnswer })] }));
175
172
  }
176
173
  // ============ CREATOR UI ============
177
- 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-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(PenTool, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold text-gray-900", children: "Vi\u1EBFt d\u1EA1ng \u0111\u00FAng c\u1EE7a \u0111\u1ED9ng t\u1EEB" }), _jsx("p", { className: "text-sm text-gray-500", children: "Chia \u0111\u1ED9ng t\u1EEB trong ngo\u1EB7c \u0111\u00FAng th\u00EC" })] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: !isValid(), className: "gap-2 border-amber-200 text-amber-600 hover:bg-amber-50 hover:text-amber-700", children: [_jsx(Eye, { className: "h-4 w-4" }), "Xem tr\u01B0\u1EDBc"] })] }) }), _jsxs(CardContent, { className: "space-y-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-amber-200 bg-gradient-to-r from-amber-50 to-yellow-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-amber-100", children: _jsx(Lightbulb, { className: "h-4 w-4 text-amber-600" }) }), _jsxs("div", { className: "text-sm text-amber-900", children: [_jsx("p", { className: "font-semibold text-amber-800", children: "H\u01B0\u1EDBng d\u1EABn t\u1EA1o c\u00E2u h\u1ECFi" }), _jsxs("ul", { className: "mt-2 space-y-1.5 text-amber-700", children: [_jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-amber-400" }), "S\u1EED d\u1EE5ng ", _jsx("code", { className: "mx-1 rounded bg-amber-100 px-1.5 py-0.5 font-mono text-amber-800", children: '{0}' }), " \u0111\u1EC3 \u0111\u00E1nh d\u1EA5u ch\u1ED7 tr\u1ED1ng trong c\u00E2u"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-amber-400" }), "Nh\u1EADp \u0111\u1ED9ng t\u1EEB g\u1EE3i \u00FD (d\u1EA1ng nguy\u00EAn m\u1EABu) v\u00E0o \u00F4 \"G\u1EE3i \u00FD \u0111\u1ED9ng t\u1EEB\""] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-amber-400" }), "H\u1ECDc sinh s\u1EBD vi\u1EBFt d\u1EA1ng \u0111\u00FAng c\u1EE7a \u0111\u1ED9ng t\u1EEB \u0111\u1EC3 ho\u00E0n th\u00E0nh c\u00E2u"] }), _jsxs("li", { className: "flex items-center gap-2", children: [_jsx("span", { className: "h-1.5 w-1.5 rounded-full bg-amber-400" }), "V\u00ED d\u1EE5: ", _jsxs("code", { className: "ml-1 rounded bg-amber-100 px-1.5 py-0.5 font-mono text-amber-800 select-all cursor-pointer", title: "Click \u0111\u1EC3 copy", children: ["John ", '{0}', " his homework at night."] })] })] })] })] }) }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-4 w-4 text-amber-500" }), "C\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { value: sentence, onChange: (e) => {
178
- setSentence(e.target.value);
179
- if (validationErrors.sentence) {
180
- setValidationErrors(prev => ({ ...prev, sentence: undefined }));
181
- }
182
- }, placeholder: "V\u00ED d\u1EE5: John {0} his homework at night.", className: `border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 ${validationErrors.sentence ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.sentence && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.sentence }))] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: `rounded-xl border-2 p-4 transition-all ${verbHint ? 'border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50' : 'border-gray-200 bg-white'}`, children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-700", children: [_jsx("span", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-amber-100 text-xs font-bold text-amber-600", children: "V" }), "G\u1EE3i \u00FD \u0111\u1ED9ng t\u1EEB ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { value: verbHint, onChange: (e) => {
183
- setVerbHint(e.target.value);
184
- if (validationErrors.verbHint) {
185
- setValidationErrors(prev => ({ ...prev, verbHint: undefined }));
186
- }
187
- }, placeholder: "V\u00ED d\u1EE5: do", className: `mt-2 border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 ${validationErrors.verbHint ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.verbHint && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: validationErrors.verbHint }))] }), _jsxs("div", { className: `rounded-xl border-2 p-4 transition-all ${correctAnswer ? 'border-green-200 bg-gradient-to-r from-green-50 to-emerald-50' : 'border-gray-200 bg-white'}`, children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-700", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), "\u0110\u00E1p \u00E1n \u0111\u00FAng ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { value: correctAnswer, onChange: (e) => {
188
- setCorrectAnswer(e.target.value);
189
- if (validationErrors.correctAnswer) {
190
- setValidationErrors(prev => ({ ...prev, correctAnswer: undefined }));
191
- }
192
- }, placeholder: "V\u00ED d\u1EE5: does", className: `mt-2 border-gray-200 bg-white transition-all focus:border-green-300 focus:ring-green-200 ${validationErrors.correctAnswer ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.correctAnswer && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: validationErrors.correctAnswer }))] })] }), _jsxs("div", { className: "flex items-center justify-between rounded-xl border-2 border-gray-200 bg-white p-4 transition-all hover:border-gray-300", 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-gray-100", children: _jsx(ToggleLeft, { className: "h-4 w-4 text-gray-600" }) }), _jsxs("div", { className: "space-y-0.5", children: [_jsx(Label, { htmlFor: "caseSensitive", className: "font-semibold text-gray-800", children: "Ph\u00E2n bi\u1EC7t ch\u1EEF hoa/th\u01B0\u1EDDng" }), _jsx("p", { className: "text-sm text-gray-500", children: "B\u1EADt n\u1EBFu \u0111\u00E1p \u00E1n c\u1EA7n kh\u1EDBp ch\u00EDnh x\u00E1c (v\u00ED d\u1EE5: \"Does\" \u2260 \"does\")" })] })] }), _jsx(Switch, { id: "caseSensitive", checked: caseSensitive, onCheckedChange: setCaseSensitive })] }), sentence && verbHint && (_jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: [_jsxs("div", { className: "mb-2 flex items-center gap-2 text-xs font-semibold text-gray-600", children: [_jsx(Eye, { className: "h-3.5 w-3.5" }), "Xem tr\u01B0\u1EDBc c\u00E2u h\u1ECFi"] }), _jsxs("p", { className: "text-sm text-gray-700", children: [sentence.replace('{0}', '_______'), " ", _jsxs("span", { className: "font-semibold text-amber-600", children: ["(", verbHint, ")"] })] }), correctAnswer && (_jsxs("div", { className: "mt-2 flex items-center gap-2 rounded-lg bg-green-50 px-3 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-500" }), _jsxs("span", { className: "text-sm text-green-700", children: ["\u0110\u00E1p \u00E1n: ", _jsx("strong", { children: sentence.replace('{0}', correctAnswer) })] })] }))] })), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100", children: _jsx("span", { className: "text-lg", children: "\u274C" }) }), _jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("ul", { className: "mt-1 list-inside list-disc text-red-700", children: externalErrors.map((error, index) => (_jsx("li", { children: error }, index))) })] })] }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50", children: [_jsx("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" }) })] })] }));
174
+ return (_jsx("div", { className: "space-y-3", children: _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(PenTool, { className: "h-3.5 w-3.5" }), title: "Vi\u1EBFt d\u1EA1ng \u0111\u00FAng c\u1EE7a \u0111\u1ED9ng t\u1EEB", points: { value: points, onValueChange: setPoints }, preview: {
175
+ onClick: () => setIsClientMode(true),
176
+ disabled: !isValid(),
177
+ } }), 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(MessageSquare, { className: "h-3.5 w-3.5 text-amber-500" }), "C\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { value: sentence, onChange: (e) => {
178
+ setSentence(e.target.value);
179
+ if (validationErrors.sentence) {
180
+ setValidationErrors(prev => ({ ...prev, sentence: undefined }));
181
+ }
182
+ }, placeholder: "V\u00ED d\u1EE5: John {0} his homework at night.", className: `h-8 border-gray-200 bg-white ${validationErrors.sentence ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.sentence && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.sentence }))] }), _jsxs("div", { className: "grid grid-cols-2 gap-2.5", children: [_jsxs("div", { className: `rounded-md border p-2.5 ${verbHint ? 'border-amber-200 bg-amber-50/70' : 'border-gray-200 bg-white'}`, children: [_jsxs(Label, { className: "flex items-center gap-1.5 text-xs font-semibold text-gray-700", children: [_jsx("span", { className: "flex h-5 w-5 items-center justify-center rounded-md bg-amber-100 text-[10px] font-bold text-amber-600", children: "V" }), "G\u1EE3i \u00FD \u0111\u1ED9ng t\u1EEB ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { value: verbHint, onChange: (e) => {
183
+ setVerbHint(e.target.value);
184
+ if (validationErrors.verbHint) {
185
+ setValidationErrors(prev => ({ ...prev, verbHint: undefined }));
186
+ }
187
+ }, placeholder: "V\u00ED d\u1EE5: do", className: `mt-1.5 h-8 border-gray-200 bg-white ${validationErrors.verbHint ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.verbHint && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: validationErrors.verbHint }))] }), _jsxs("div", { className: `rounded-md border p-2.5 ${correctAnswer ? 'border-green-200 bg-green-50/70' : 'border-gray-200 bg-white'}`, children: [_jsxs(Label, { className: "flex items-center gap-1.5 text-xs font-semibold text-gray-700", children: [_jsx(CheckCircle2, { className: "h-3.5 w-3.5 text-green-600" }), "\u0110\u00E1p \u00E1n \u0111\u00FAng ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { value: correctAnswer, onChange: (e) => {
188
+ setCorrectAnswer(e.target.value);
189
+ if (validationErrors.correctAnswer) {
190
+ setValidationErrors(prev => ({ ...prev, correctAnswer: undefined }));
191
+ }
192
+ }, placeholder: "V\u00ED d\u1EE5: does", className: `mt-1.5 h-8 border-gray-200 bg-white ${validationErrors.correctAnswer ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.correctAnswer && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: validationErrors.correctAnswer }))] })] }), _jsxs("div", { className: "flex items-center justify-between rounded-md border border-gray-200 bg-white px-2.5 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100", children: _jsx(ToggleLeft, { className: "h-3.5 w-3.5 text-gray-600" }) }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "caseSensitive", className: "text-sm font-semibold text-gray-800", children: "Ph\u00E2n bi\u1EC7t ch\u1EEF hoa/th\u01B0\u1EDDng" }), _jsx("p", { className: "text-xs text-gray-500", children: "B\u1EADt n\u1EBFu \u0111\u00E1p \u00E1n c\u1EA7n kh\u1EDBp ch\u00EDnh x\u00E1c (v\u00ED d\u1EE5: \"Does\" \u2260 \"does\")" })] })] }), _jsx(Switch, { id: "caseSensitive", checked: caseSensitive, onCheckedChange: setCaseSensitive })] }), sentence && verbHint && (_jsxs("div", { className: "rounded-md border border-gray-200 bg-gray-50 p-2.5", children: [_jsxs("div", { className: "mb-1 flex items-center gap-1.5 text-xs font-semibold text-gray-600", children: [_jsx(Eye, { className: "h-3.5 w-3.5" }), "Xem tr\u01B0\u1EDBc c\u00E2u h\u1ECFi"] }), _jsxs("p", { className: "text-sm text-gray-700", children: [sentence.replace('{0}', '_______'), " ", _jsxs("span", { className: "font-semibold text-amber-600", children: ["(", verbHint, ")"] })] }), correctAnswer && (_jsxs("div", { className: "mt-1.5 flex items-center gap-1.5 rounded-md bg-green-50 px-2.5 py-1.5", children: [_jsx(CheckCircle2, { className: "h-3.5 w-3.5 text-green-500" }), _jsxs("span", { className: "text-xs text-green-700", children: ["\u0110\u00E1p \u00E1n: ", _jsx("strong", { children: sentence.replace('{0}', correctAnswer) })] })] }))] })), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-red-200 bg-red-50 p-2 text-xs text-red-700", children: [_jsx("span", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("span", { children: externalErrors.join(' · ') })] })), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] }) }));
193
193
  }
194
194
  export function WriteCorrectVerbFormCreator(props) {
195
195
  return _jsx(WriteCorrectVerbFormCreatorContent, { ...props });
@@ -1,13 +1,11 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useState, useRef, useEffect } from 'react';
4
- import { Button } from '../../../../components/ui/button';
5
4
  import { Input } from '../../../../components/ui/input';
6
5
  import { Label } from '../../../../components/ui/label';
7
6
  import { Textarea } from '../../../../components/ui/textarea';
8
- import { PointsInput } from '../../../../components/ui/points-input';
9
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
10
- import { Pencil, Eye, PenLine, BookOpen, Hash } from 'lucide-react';
7
+ import { PenLine, BookOpen, Hash } from 'lucide-react';
8
+ import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
11
9
  import { WriteSentencesClient } from './WriteSentencesClient';
12
10
  import { useDebouncedCallback } from '../../../../shared/lib/hooks';
13
11
  import { BasicQuestionGroupCard } from '../../groups/BasicQuestionGroupCard';
@@ -152,22 +150,27 @@ function WriteSentencesCreatorContent({ initialData, onChange, externalErrors, v
152
150
  instruction,
153
151
  sentenceCount,
154
152
  };
155
- return (_jsxs("div", { className: "space-y-6", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Write Sentences (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(WriteSentencesClient, { questionData: questionData, onSubmit: () => { }, isReviewMode: true, explanation: explanation })] }));
153
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(WriteSentencesClient, { questionData: questionData, onSubmit: () => { }, isReviewMode: true, explanation: explanation })] }));
156
154
  }
157
155
  // Creator UI
158
- return (_jsxs("div", { className: "space-y-6", children: [isGrouped && (_jsx(BasicQuestionGroupCard, { groupNumber: currentGroupNumber, title: groupTitle, subtitle: groupSubtitle, content: groupContent, imageUrl: imageUrl, onTitleChange: setGroupTitle, onSubtitleChange: setGroupSubtitle, onContentChange: setGroupContent, onImageChange: setImageUrl, uploadIdPrefix: `write-sentences-${partId ?? 'group'}` })), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50", children: [_jsx("div", { className: "h-1.5 bg-gradient-to-r from-teal-400 to-emerald-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-teal-500 to-emerald-600 shadow-md", children: _jsx(PenLine, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold text-gray-900", children: "Vi\u1EBFt c\u00E2u" }), _jsx("p", { className: "text-sm text-gray-500", children: "H\u1ECDc sinh t\u1EF1 vi\u1EBFt m\u1ED9t c\u00E2u ho\u00E0n ch\u1EC9nh" })] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), className: "gap-2 border-emerald-200 text-emerald-600 hover:bg-emerald-50 hover:text-emerald-700", children: [_jsx(Eye, { className: "h-4 w-4" }), "Xem tr\u01B0\u1EDBc"] })] }) }), _jsxs(CardContent, { className: "space-y-6", children: [_jsxs("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2", 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) }) }), _jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: [_jsxs(Label, { htmlFor: "sentenceCount", className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(Hash, { className: "h-4 w-4 text-teal-500" }), "S\u1ED1 c\u00E2u c\u1EA7n vi\u1EBFt ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "sentenceCount", type: "number", min: 1, max: 10, value: sentenceCount ?? '', onChange: (e) => {
159
- const raw = e.target.value;
160
- if (raw === '') {
161
- setSentenceCount(undefined);
162
- }
163
- else {
164
- const val = parseInt(raw, 10);
165
- setSentenceCount(isNaN(val) ? undefined : Math.max(1, val));
166
- }
167
- if (validationErrors.sentenceCount) {
168
- setValidationErrors((prev) => ({ ...prev, sentenceCount: undefined }));
169
- }
170
- }, placeholder: "Nh\u1EADp s\u1ED1 c\u00E2u...", className: `mt-1.5 border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200 ${validationErrors.sentenceCount ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.sentenceCount && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: validationErrors.sentenceCount }))] })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "instruction", className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(BookOpen, { className: "h-4 w-4 text-teal-500" }), "H\u01B0\u1EDBng d\u1EABn ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(T\u00F9y ch\u1ECDn)" })] }), _jsx(Textarea, { id: "instruction", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: "V\u00ED d\u1EE5: Now write two sentences about the picture.", rows: 2, className: "border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200" })] }), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100", children: _jsx("span", { className: "text-lg", children: "\u274C" }) }), _jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("ul", { className: "mt-1 list-inside list-disc text-red-700", children: externalErrors.map((error, index) => (_jsx("li", { children: error }, index))) })] })] }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50", children: [_jsx("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" }) })] })] }));
156
+ return (_jsxs("div", { className: "space-y-3", children: [isGrouped && (_jsx(BasicQuestionGroupCard, { groupNumber: currentGroupNumber, title: groupTitle, subtitle: groupSubtitle, content: groupContent, imageUrl: imageUrl, onTitleChange: setGroupTitle, onSubtitleChange: setGroupSubtitle, onContentChange: setGroupContent, onImageChange: setImageUrl, uploadIdPrefix: `write-sentences-${partId ?? 'group'}` })), _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(PenLine, { className: "h-3.5 w-3.5" }), title: "Vi\u1EBFt c\u00E2u", points: {
157
+ value: points,
158
+ onValueChange: setPoints,
159
+ }, preview: {
160
+ onClick: () => setIsClientMode(true),
161
+ } }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { htmlFor: "sentenceCount", className: "flex items-center gap-1.5 text-sm font-semibold text-gray-800", children: [_jsx(Hash, { className: "h-3.5 w-3.5 text-teal-500" }), "S\u1ED1 c\u00E2u c\u1EA7n vi\u1EBFt ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "sentenceCount", type: "number", min: 1, max: 10, value: sentenceCount ?? '', onChange: (e) => {
162
+ const raw = e.target.value;
163
+ if (raw === '') {
164
+ setSentenceCount(undefined);
165
+ }
166
+ else {
167
+ const val = parseInt(raw, 10);
168
+ setSentenceCount(isNaN(val) ? undefined : Math.max(1, val));
169
+ }
170
+ if (validationErrors.sentenceCount) {
171
+ setValidationErrors((prev) => ({ ...prev, sentenceCount: undefined }));
172
+ }
173
+ }, placeholder: "Nh\u1EADp s\u1ED1 c\u00E2u...", className: `mt-1.5 border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200 ${validationErrors.sentenceCount ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.sentenceCount && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: validationErrors.sentenceCount }))] }), _jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { htmlFor: "instruction", className: "flex items-center gap-1.5 text-sm font-semibold text-gray-800", children: [_jsx(BookOpen, { className: "h-3.5 w-3.5 text-teal-500" }), "H\u01B0\u1EDBng d\u1EABn ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(T\u00F9y ch\u1ECDn)" })] }), _jsx(Textarea, { id: "instruction", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: "V\u00ED d\u1EE5: Now write two sentences about the picture.", rows: 2, className: "border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200" })] }), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-red-200 bg-red-50 p-2 text-xs text-red-700", children: [_jsx("span", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("span", { children: externalErrors.join(' · ') })] }))] }), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation })] }));
171
174
  }
172
175
  export function WriteSentencesCreator(props) {
173
176
  return _jsx(WriteSentencesCreatorContent, { ...props });
@@ -62,7 +62,7 @@ export function WriteShortParagraphClient({ questionData, onSubmit, isReviewMode
62
62
  return 'Text';
63
63
  }
64
64
  };
65
- return (_jsxs("div", { className: "space-y-6", children: [instruction && (_jsx(Card, { children: _jsx(CardHeader, { children: _jsx(CardTitle, { className: "text-lg", children: instruction }) }) })), contexts.length > 0 && (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: "Contexts" }) }), _jsx(CardContent, { children: _jsx("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3", children: contexts.map((ctx, index) => (_jsx(ContextDisplay, { context: ctx, index: index }, ctx.id))) }) })] })), _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(CardTitle, { children: "Vi\u1EBFt b\u00E0i" }), _jsx("div", { className: "flex items-center gap-2", children: _jsxs("span", { className: `text-sm font-semibold ${isValidWordCount ? 'text-green-600' : 'text-red-600'}`, children: [wordCount, " / ", minWords, maxWords ? ` - ${maxWords}` : '+', " t\u1EEB"] }) })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: "writing-answer", children: "N\u1ED9i dung b\u00E0i vi\u1EBFt" }), _jsx(Textarea, { id: "writing-answer", value: answer, onChange: (e) => handleAnswerChange(e.target.value), placeholder: "Nh\u1EADp n\u1ED9i dung b\u00E0i vi\u1EBFt c\u1EE7a b\u1EA1n...", className: "mt-2 min-h-[200px]", rows: 10, disabled: isReviewMode })] }), !isReviewMode && (_jsx("div", { className: "flex items-start gap-2 rounded-lg border p-3", children: isValidWordCount ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "mt-0.5 h-5 w-5 rounded-full bg-green-500" }), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-sm font-medium text-green-700", children: "\u0110\u00E3 \u0111\u1EE7 s\u1ED1 t\u1EEB y\u00EAu c\u1EA7u" }), _jsxs("p", { className: "text-xs text-green-600", children: ["B\u1EA1n \u0111\u00E3 vi\u1EBFt ", wordCount, " t\u1EEB. Y\u00EAu c\u1EA7u: ", minWords, maxWords ? ` - ${maxWords}` : '+', " t\u1EEB."] })] })] })) : (_jsxs(_Fragment, { children: [_jsx(AlertCircle, { className: "h-5 w-5 text-amber-500" }), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-sm font-medium text-amber-700", children: "Ch\u01B0a \u0111\u1EE7 s\u1ED1 t\u1EEB y\u00EAu c\u1EA7u" }), _jsxs("p", { className: "text-xs text-amber-600", children: ["B\u1EA1n \u0111\u00E3 vi\u1EBFt ", wordCount, " t\u1EEB. C\u1EA7n \u00EDt nh\u1EA5t ", minWords, " t\u1EEB.", maxWords && wordCount > maxWords && _jsxs("span", { children: [" T\u1ED1i \u0111a ", maxWords, " t\u1EEB."] })] })] })] })) }))] })] })] }));
65
+ return (_jsxs("div", { className: "space-y-3", children: [instruction && (_jsx(Card, { className: "border-0 shadow-sm", children: _jsx(CardHeader, { className: "px-3 py-2", children: _jsx(CardTitle, { className: "text-sm", children: instruction }) }) })), contexts.length > 0 && (_jsxs(Card, { className: "border-0 shadow-sm", children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsx(CardTitle, { className: "text-sm", children: "Contexts" }) }), _jsx(CardContent, { className: "p-3 space-y-3", children: _jsx("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3", children: contexts.map((ctx, index) => (_jsx(ContextDisplay, { context: ctx, index: index }, ctx.id))) }) })] })), _jsxs(Card, { className: "border-0 shadow-sm", children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsx(CardTitle, { className: "text-sm", children: "Vi\u1EBFt b\u00E0i" }), _jsx("div", { className: "flex items-center gap-2", children: _jsxs("span", { className: `text-sm font-semibold ${isValidWordCount ? 'text-green-600' : 'text-red-600'}`, children: [wordCount, " / ", minWords, maxWords ? ` - ${maxWords}` : '+', " t\u1EEB"] }) })] }) }), _jsxs(CardContent, { className: "space-y-3 p-3", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: "writing-answer", children: "N\u1ED9i dung b\u00E0i vi\u1EBFt" }), _jsx(Textarea, { id: "writing-answer", value: answer, onChange: (e) => handleAnswerChange(e.target.value), placeholder: "Nh\u1EADp n\u1ED9i dung b\u00E0i vi\u1EBFt c\u1EE7a b\u1EA1n...", className: "mt-2 min-h-[200px]", rows: 10, disabled: isReviewMode })] }), !isReviewMode && (_jsx("div", { className: "flex items-start gap-2 rounded-lg border p-3", children: isValidWordCount ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "mt-0.5 h-5 w-5 rounded-full bg-green-500" }), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-sm font-medium text-green-700", children: "\u0110\u00E3 \u0111\u1EE7 s\u1ED1 t\u1EEB y\u00EAu c\u1EA7u" }), _jsxs("p", { className: "text-xs text-green-600", children: ["B\u1EA1n \u0111\u00E3 vi\u1EBFt ", wordCount, " t\u1EEB. Y\u00EAu c\u1EA7u: ", minWords, maxWords ? ` - ${maxWords}` : '+', " t\u1EEB."] })] })] })) : (_jsxs(_Fragment, { children: [_jsx(AlertCircle, { className: "h-5 w-5 text-amber-500" }), _jsxs("div", { className: "flex-1", children: [_jsx("p", { className: "text-sm font-medium text-amber-700", children: "Ch\u01B0a \u0111\u1EE7 s\u1ED1 t\u1EEB y\u00EAu c\u1EA7u" }), _jsxs("p", { className: "text-xs text-amber-600", children: ["B\u1EA1n \u0111\u00E3 vi\u1EBFt ", wordCount, " t\u1EEB. C\u1EA7n \u00EDt nh\u1EA5t ", minWords, " t\u1EEB.", maxWords && wordCount > maxWords && _jsxs("span", { children: [" T\u1ED1i \u0111a ", maxWords, " t\u1EEB."] })] })] })] })) }))] })] })] }));
66
66
  }
67
67
  function ContextDisplay({ context, index }) {
68
68
  const { previewUrl } = usePresignedFileUrl(context.type !== 'text' ? context.url || '' : '');
@@ -5,8 +5,8 @@ import { Button } from '../../../../components/ui/button';
5
5
  import { Label } from '../../../../components/ui/label';
6
6
  import { Input } from '../../../../components/ui/input';
7
7
  import { Textarea } from '../../../../components/ui/textarea';
8
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
9
- import { Pencil, Eye, X, Image, Music, FileText } from 'lucide-react';
8
+ import { X, Image, Music, FileText } from 'lucide-react';
9
+ import { CompactCreatorHeader, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
10
10
  import { FileUpload } from '../../../../components/ui/file-upload';
11
11
  import { WriteShortParagraphClient } from './WriteShortParagraphClient';
12
12
  import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
@@ -113,9 +113,12 @@ onChange, }) {
113
113
  maxWords,
114
114
  instruction,
115
115
  };
116
- 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, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold tracking-tight text-gray-900", children: "Write Short Paragraph (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(WriteShortParagraphClient, { questionData: questionData, onSubmit: () => { } })] }));
116
+ return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(WriteShortParagraphClient, { questionData: questionData, onSubmit: () => { } })] }));
117
117
  }
118
- 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, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold tracking-tight text-gray-900", children: "Write Short Paragraph" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "T\u1EA1o b\u00E0i t\u1EADp vi\u1EBFt \u0111o\u1EA1n v\u0103n v\u1EDBi nhi\u1EC1u content (\u1EA3nh, audio, text)" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: contexts.length === 0 || minWords < 1, children: [_jsx(Eye, { className: "mr-2 h-4 w-4" }), "Client Mode"] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "instruction", className: "text-sm font-semibold text-gray-700", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Textarea, { id: "instruction", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: "V\u00ED d\u1EE5: Look at the three pictures. Write about this story.", className: "mt-1", rows: 2 })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "minWords", className: "text-sm font-semibold text-gray-700", children: "S\u1ED1 t\u1EEB t\u1ED1i thi\u1EC3u *" }), _jsx(Input, { id: "minWords", type: "number", min: "1", value: minWords, onChange: (e) => setMinWords(parseInt(e.target.value) || 1), placeholder: "20", className: "mt-1" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "maxWords", className: "text-sm font-semibold text-gray-700", children: "S\u1ED1 t\u1EEB t\u1ED1i \u0111a (t\u00F9y ch\u1ECDn)" }), _jsx(Input, { id: "maxWords", type: "number", min: minWords, value: maxWords || '', onChange: (e) => setMaxWords(e.target.value ? parseInt(e.target.value) : undefined), placeholder: "Kh\u00F4ng gi\u1EDBi h\u1EA1n", className: "mt-1" })] })] })] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-xl shadow-blue-100/40 ring-1 ring-gray-100", children: [_jsx(CardHeader, { className: "bg-gradient-to-br from-indigo-50/50 via-white to-blue-50/30 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(FileText, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-lg font-bold text-gray-900", children: "Contexts" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Th\u00EAm c\u00E1c context (\u1EA3nh, audio, ho\u1EB7c text) \u0111\u1EC3 h\u1ECDc sinh xem khi vi\u1EBFt" })] })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => addContext('image'), className: "flex items-center gap-2 border-indigo-200 bg-indigo-50 text-indigo-700 hover:bg-indigo-100 hover:text-indigo-800", children: [_jsx(Image, { className: "h-4 w-4" }), "Th\u00EAm \u1EA3nh"] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => addContext('audio'), className: "flex items-center gap-2 border-purple-200 bg-purple-50 text-purple-700 hover:bg-purple-100 hover:text-purple-800", children: [_jsx(Music, { className: "h-4 w-4" }), "Th\u00EAm audio"] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => addContext('text'), className: "flex items-center gap-2 border-blue-200 bg-blue-50 text-blue-700 hover:bg-blue-100 hover:text-blue-800", children: [_jsx(FileText, { className: "h-4 w-4" }), "Th\u00EAm text"] })] })] }) }), _jsx(CardContent, { className: "space-y-4 pt-6", children: contexts.length > 0 ? (contexts.map((ctx, index) => (_jsx(ContextItem, { context: ctx, index: index, onUpdate: updateContext, onRemove: removeContext }, ctx.id)))) : (_jsxs("div", { className: "flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-gray-300 bg-gray-50 p-12", children: [_jsx("div", { className: "mb-4 rounded-full bg-gray-200 p-4", children: _jsx(Image, { className: "h-8 w-8 text-gray-400" }) }), _jsx("p", { className: "mb-2 text-sm font-medium text-gray-700", children: "Ch\u01B0a c\u00F3 context n\u00E0o" }), _jsx("p", { className: "mb-4 text-xs text-gray-500", children: "Nh\u1EA5n c\u00E1c n\u00FAt b\u00EAn tr\u00EAn \u0111\u1EC3 th\u00EAm context" })] })) })] })] }));
118
+ return (_jsxs("div", { className: "space-y-3", children: [_jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(FileText, { className: "h-3.5 w-3.5" }), title: "Write Short Paragraph", preview: {
119
+ onClick: () => setIsClientMode(true),
120
+ disabled: contexts.length === 0 || minWords < 1,
121
+ } }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "instruction", className: "text-sm font-semibold text-gray-700", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Textarea, { id: "instruction", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: "V\u00ED d\u1EE5: Look at the three pictures. Write about this story.", className: "mt-1", rows: 2 })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "minWords", className: "text-sm font-semibold text-gray-700", children: "S\u1ED1 t\u1EEB t\u1ED1i thi\u1EC3u *" }), _jsx(Input, { id: "minWords", type: "number", min: "1", value: minWords, onChange: (e) => setMinWords(parseInt(e.target.value) || 1), placeholder: "20", className: "mt-1" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "maxWords", className: "text-sm font-semibold text-gray-700", children: "S\u1ED1 t\u1EEB t\u1ED1i \u0111a (t\u00F9y ch\u1ECDn)" }), _jsx(Input, { id: "maxWords", type: "number", min: minWords, value: maxWords || '', onChange: (e) => setMaxWords(e.target.value ? parseInt(e.target.value) : undefined), placeholder: "Kh\u00F4ng gi\u1EDBi h\u1EA1n", className: "mt-1" })] })] })] }), _jsx(CompactFieldCard, { header: _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100", children: _jsx(FileText, { className: "h-3.5 w-3.5 text-gray-600" }) }), _jsx("span", { className: "text-sm font-bold text-gray-900", children: "Contexts" })] }), _jsxs("div", { className: "flex gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => addContext('image'), className: "flex items-center gap-2 border-indigo-200 bg-indigo-50 text-indigo-700 hover:bg-indigo-100 hover:text-indigo-800", children: [_jsx(Image, { className: "h-4 w-4" }), "Th\u00EAm \u1EA3nh"] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => addContext('audio'), className: "flex items-center gap-2 border-purple-200 bg-purple-50 text-purple-700 hover:bg-purple-100 hover:text-purple-800", children: [_jsx(Music, { className: "h-4 w-4" }), "Th\u00EAm audio"] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => addContext('text'), className: "flex items-center gap-2 border-blue-200 bg-blue-50 text-blue-700 hover:bg-blue-100 hover:text-blue-800", children: [_jsx(FileText, { className: "h-4 w-4" }), "Th\u00EAm text"] })] })] }), children: contexts.length > 0 ? (contexts.map((ctx, index) => (_jsx(ContextItem, { context: ctx, index: index, onUpdate: updateContext, onRemove: removeContext }, ctx.id)))) : (_jsxs("div", { className: "flex flex-col items-center justify-center rounded-lg border-2 border-dashed border-gray-300 bg-gray-50 p-12", children: [_jsx("div", { className: "mb-4 rounded-full bg-gray-200 p-4", children: _jsx(Image, { className: "h-8 w-8 text-gray-400" }) }), _jsx("p", { className: "mb-2 text-sm font-medium text-gray-700", children: "Ch\u01B0a c\u00F3 context n\u00E0o" }), _jsx("p", { className: "mb-4 text-xs text-gray-500", children: "Nh\u1EA5n c\u00E1c n\u00FAt b\u00EAn tr\u00EAn \u0111\u1EC3 th\u00EAm context" })] })) })] }));
119
122
  }
120
123
  function ContextItem({ context, index, onUpdate, onRemove }) {
121
124
  const { previewUrl } = usePresignedFileUrl(context.type !== 'text' ? context.url || '' : '');
@@ -11,12 +11,9 @@ import { ReadAndColorObjectsClient } from '../types/read-and-color-objects/ReadA
11
11
  import { ArrangeLettersIntoWordsClient } from '../types/arrange-letters-into-words/ArrangeLettersIntoWordsClient';
12
12
  import { WriteShortParagraphClient } from '../types/write-short-paragraph/WriteShortParagraphClient';
13
13
  import { LabelThePictureClient } from '../types/label-the-picture/LabelThePictureClient';
14
- import { LookPictureFillWordHintClient } from '../types/look-picture-fill-word-hint/LookPictureFillWordHintClient';
15
14
  import { ReadPassageAndAnswerQuestionsClient } from '../types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient';
16
15
  import { ChooseTheCorrectAnswerClient } from '../types/choose-the-correct-answer/ChooseTheCorrectAnswerClient';
17
16
  import { WriteAShortLetterClient } from '../types/write-a-short-letter/WriteAShortLetterClient';
18
- import { LookPictureChooseCorrectAnswerClient } from '../types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient';
19
- import { LookPictureFillBlankChooseAnswerClient } from '../types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient';
20
17
  import { FillMissingWordsInGridClient } from '../types/fill-missing-words-in-grid/FillMissingWordsInGridClient';
21
18
  import { CrosswordPuzzleClient } from '../types/crossword-puzzle/CrosswordPuzzleClient';
22
19
  import { FindWordsInMatrixClient } from '../types/find-words-in-matrix/FindWordsInMatrixClient';
@@ -393,50 +390,6 @@ export function QuestionViewer({ question, onSubmitAnswer, isReviewMode = false,
393
390
  onSubmitAnswer?.(userConnections);
394
391
  } }));
395
392
  };
396
- const renderLookPictureFillWordHint = () => {
397
- const answer = question.answer;
398
- const content = question.content;
399
- // Check if question has group info (from API)
400
- const questionGroup = question.questionGroup;
401
- let subQuestions = [];
402
- if (answer?.subQuestions && answer.subQuestions.length > 0) {
403
- subQuestions = answer.subQuestions;
404
- }
405
- else if (content?.imageUrl && content?.word) {
406
- // Single question format - transform to subQuestions
407
- subQuestions = [{
408
- id: question.id,
409
- imageUrl: content.imageUrl,
410
- word: content.word,
411
- correctAnswer: answer?.correctAnswer || '',
412
- groupNumber: questionGroup?.groupNumber || 1,
413
- }];
414
- }
415
- // Get hintLetters from multiple sources (priority order):
416
- // 1. questionGroup.payload.hintLetters (from API group payload)
417
- // 2. answer.hintLetters
418
- // 3. content.hintLetters
419
- const hintLetters = questionGroup?.payload?.hintLetters ||
420
- answer?.hintLetters ||
421
- content?.hintLetters ||
422
- [];
423
- // Build hintLettersByGroup if we have a groupNumber
424
- const groupNumber = questionGroup?.groupNumber || 1;
425
- const hintLettersByGroup = {};
426
- if (hintLetters.length > 0) {
427
- hintLettersByGroup[groupNumber] = hintLetters;
428
- }
429
- const questionData = {
430
- subQuestions,
431
- hintLetters,
432
- hintLettersByGroup,
433
- groups: [{ number: groupNumber, questionCount: subQuestions.length }],
434
- };
435
- return (_jsx(LookPictureFillWordHintClient, { questionData: questionData, userAnswer: userAnswer, isReviewMode: isReviewMode, explanation: question.explanation, onSubmit: (answers) => {
436
- setSelectedAnswer(answers);
437
- onSubmitAnswer?.(answers);
438
- } }));
439
- };
440
393
  const renderReadPassageAndAnswerQuestions = () => {
441
394
  const answer = question.answer;
442
395
  const questionData = {
@@ -450,50 +403,6 @@ export function QuestionViewer({ question, onSubmitAnswer, isReviewMode = false,
450
403
  onSubmitAnswer?.(answers);
451
404
  } }));
452
405
  };
453
- const renderLookPictureChooseCorrectAnswer = () => {
454
- const content = question.content;
455
- const answer = question.answer;
456
- // Merge correct answers into subQuestions
457
- const subQuestions = (content?.subQuestions || answer?.subQuestions || []).map((sq) => {
458
- const correctAnswerId = answer?.answers?.[sq.id] || sq.correctAnswer;
459
- return {
460
- ...sq,
461
- options: (sq.options || []).map((opt) => ({
462
- ...opt,
463
- isCorrect: opt.id === correctAnswerId,
464
- })),
465
- };
466
- });
467
- const questionData = {
468
- subQuestions,
469
- };
470
- return (_jsx(LookPictureChooseCorrectAnswerClient, { questionData: questionData, userAnswer: userAnswer, isReviewMode: isReviewMode, onSubmit: (answers) => {
471
- setSelectedAnswer(answers);
472
- onSubmitAnswer?.(answers);
473
- } }));
474
- };
475
- const renderLookPictureFillBlankChooseAnswer = () => {
476
- const content = question.content;
477
- const answer = question.answer;
478
- // Merge correct answers into subQuestions
479
- const subQuestions = (content?.subQuestions || answer?.subQuestions || []).map((sq) => {
480
- const correctAnswerId = answer?.answers?.[sq.id] || sq.correctAnswer;
481
- return {
482
- ...sq,
483
- options: (sq.options || []).map((opt) => ({
484
- ...opt,
485
- isCorrect: opt.id === correctAnswerId,
486
- })),
487
- };
488
- });
489
- const questionData = {
490
- subQuestions,
491
- };
492
- return (_jsx(LookPictureFillBlankChooseAnswerClient, { questionData: questionData, userAnswer: userAnswer, isReviewMode: isReviewMode, onSubmit: (answers) => {
493
- setSelectedAnswer(answers);
494
- onSubmitAnswer?.(answers);
495
- } }));
496
- };
497
406
  const renderFillMissingWordsInGrid = () => {
498
407
  const content = question.content;
499
408
  const answer = question.answer;
@@ -629,19 +538,12 @@ export function QuestionViewer({ question, onSubmitAnswer, isReviewMode = false,
629
538
  case 'WRITE_A_SHORT_LETTER':
630
539
  return renderWriteAShortLetter();
631
540
  // Marketing exam questions grade 1-2
632
- case 'LOOK_PICTURE_FILL_WORD_HINT':
633
- return renderLookPictureFillWordHint();
634
541
  case 'LABEL_THE_PICTURE':
635
542
  return renderLabelThePicture();
636
543
  case 'READ_AND_COLOR_OBJECTS':
637
544
  return renderReadAndColorObjects();
638
545
  case 'READ_PASSAGE_AND_ANSWER_QUESTIONS':
639
546
  return renderReadPassageAndAnswerQuestions();
640
- // Additional marketing exam questions
641
- case 'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER':
642
- return renderLookPictureChooseCorrectAnswer();
643
- case 'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER':
644
- return renderLookPictureFillBlankChooseAnswer();
645
547
  case 'FILL_MISSING_WORDS_IN_GRID':
646
548
  return renderFillMissingWordsInGrid();
647
549
  case 'WORD_FILL_PARAGRAPH':
@@ -1,82 +1,7 @@
1
1
  'use client';
2
- import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
- function isRecord(value) {
4
- return typeof value === 'object' && value !== null && !Array.isArray(value);
5
- }
6
- function asRecord(value) {
7
- return isRecord(value) ? value : {};
8
- }
9
- function asString(value, fallback = '') {
10
- return typeof value === 'string' ? value : fallback;
11
- }
12
- function mapItem(item, index) {
13
- const record = asRecord(item);
14
- const content = asRecord(record.content);
15
- const correctAnswer = asRecord(record.correctAnswer);
16
- const expected = Array.isArray(correctAnswer.expectedAnswers)
17
- ? correctAnswer.expectedAnswers.map((value) => String(value ?? ''))
18
- : [];
19
- return {
20
- questionText: asString(content.questionText) ||
21
- asString(content.question) ||
22
- asString(record.questionText),
23
- audioUrl: asString(content.audioUrl) || undefined,
24
- expectedAnswers: expected,
25
- isExample: record.isExample === true,
26
- points: typeof record.points === 'number' ? record.points : 1,
27
- questionNumber: typeof record.questionNumber === 'number' ? record.questionNumber : index + 1,
28
- };
29
- }
30
- function isNestedPart(item) {
31
- const record = asRecord(item);
32
- return Array.isArray(record.questions);
33
- }
34
- function resolveGroup(question) {
35
- const content = asRecord(question.content);
36
- const list = asRecord(question.spontaneousQAList ?? content.spontaneousQAList ?? content);
37
- const itemsRaw = Array.isArray(list.items)
38
- ? list.items
39
- : Array.isArray(content.items)
40
- ? content.items
41
- : [];
42
- const instruction = asString(asRecord(list.meta).instruction) ||
43
- asString(asRecord(content.meta).instruction) ||
44
- asString(question.explanation) ||
45
- 'Talk about the topic.';
46
- if (itemsRaw.some(isNestedPart)) {
47
- return {
48
- instruction,
49
- parts: itemsRaw.map((item, index) => {
50
- const record = asRecord(item);
51
- const questions = Array.isArray(record.questions)
52
- ? record.questions.map(mapItem)
53
- : [];
54
- return {
55
- partNumber: typeof record.partNumber === 'number' ? record.partNumber : index + 1,
56
- partTitle: asString(record.partTitle) || `PART ${index + 1}`,
57
- topic: asString(record.topic),
58
- questions,
59
- };
60
- }),
61
- };
62
- }
63
- return {
64
- instruction,
65
- parts: [
66
- {
67
- partNumber: 1,
68
- partTitle: '',
69
- topic: '',
70
- questions: itemsRaw.map(mapItem),
71
- },
72
- ],
73
- };
74
- }
75
- function QaPromptCard({ item, index, isReviewMode = false, }) {
76
- return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gray-50 px-4 py-2.5", children: [_jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", item.questionNumber || index + 1] }), _jsxs("span", { className: "text-xs text-gray-500", children: [item.points || 0, " \u0111i\u1EC3m"] })] }), _jsxs("div", { className: "space-y-2 p-4", children: [_jsx("p", { className: "text-sm font-semibold text-gray-800", children: item.questionText || 'Câu hỏi chưa được nhập' }), item.audioUrl ? (_jsxs("p", { className: "text-xs text-gray-500", children: ["Audio: ", item.audioUrl] })) : null, isReviewMode && item.expectedAnswers.length > 0 ? (_jsxs("div", { className: "rounded-lg border border-emerald-100 bg-emerald-50 px-3 py-2 text-sm text-emerald-800", children: [_jsx("p", { className: "mb-1 font-medium", children: "G\u1EE3i \u00FD \u0111\u00E1p \u00E1n" }), _jsx("ul", { className: "list-disc space-y-1 pl-4", children: item.expectedAnswers.map((answer) => (_jsx("li", { children: answer }, answer))) })] })) : null] })] }));
77
- }
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { SpontaneousQaGroupClient } from '../types/spontaneous-qa-group/SpontaneousQaGroupClient';
4
+ import { mapQuestionToSpontaneousQaGroupData } from '../types/spontaneous-qa-group/map-spontaneous-qa-group-data';
78
5
  export function SpontaneousQaGroupViewer({ question, isReviewMode = false, }) {
79
- const { instruction, parts } = resolveGroup(question);
80
- const showPartChrome = parts.length > 1 || parts.some((part) => part.partTitle || part.topic);
81
- return (_jsxs("div", { className: "space-y-5", children: [instruction ? (_jsx("div", { className: "rounded-xl border border-indigo-100 bg-indigo-50 px-4 py-3 text-sm font-medium text-indigo-800", children: instruction })) : null, parts.map((part) => (_jsxs("section", { className: "space-y-3", children: [showPartChrome ? (_jsxs("div", { children: [part.partTitle ? (_jsx("h3", { className: "text-base font-bold tracking-wide text-sky-600", children: part.partTitle })) : null, part.topic ? (_jsx("p", { className: "text-sm font-semibold text-slate-600", children: part.topic })) : null] })) : null, _jsx("div", { className: "space-y-3", children: part.questions.map((item, index) => (_jsx(QaPromptCard, { item: item, index: index, isReviewMode: isReviewMode }, `${part.partNumber}-${item.questionNumber}-${index}`))) })] }, `part-${part.partNumber}`)))] }));
6
+ return (_jsx(SpontaneousQaGroupClient, { questionData: mapQuestionToSpontaneousQaGroupData(question), isReviewMode: isReviewMode }));
82
7
  }
@@ -4,8 +4,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
4
4
  * Movers fallback review cases (isReviewMode=true).
5
5
  * Source: oe-exam-fe ReviewQuestionRenderer.tsx
6
6
  */
7
- import { MoversAnswerTheQuestionRenderer, MoversFillWordHintRenderer, MoversGridFillRenderer, MoversLabelThePictureRenderer, MoversMatchByWritingAnswerRenderer, MoversMatchWordToPictureRenderer, MoversPictureChooseRenderer, MoversPictureFillBlankChooseRenderer, MoversReadingPassageRenderer, MoversTrueFalseRenderer, MoversTrueFalseCorrectGroupRenderer, MoversFillInBlankGroupRenderer, MoversMatchingWithLinesGroupRenderer, MoversCrossOutWordGroupRenderer, MoversWordFillParagraphRenderer, MoversWordFillStructuredFormRenderer, MoversWordOrderingRenderer, MoversWordOrderingGroupRenderer, MoversWriteSentencesRenderer, MoversWritingRenderer, MoversCrosswordPuzzleRenderer, ChooseThenAnswerGroupRenderer, SortWordsIntoCategoriesRenderer, } from '../../../components/exams/take/components/question-renderers';
8
- import { transformAnswerTheQuestion, transformFillWordHint, transformGridFill, transformLabelPicture, transformMatchByWritingAnswer, transformMatchWordToPicture, transformPictureChoose, transformPictureFillBlankChoose, transformReadingPassage, transformTrueFalse, transformTrueFalseCorrectGroup, transformFillInBlankGroup, transformMatchingWithLinesGroup, transformWordFillParagraph, transformWordFillStructuredForm, transformWordOrdering, transformWordOrderingGroupTake, transformWriteSentences, transformWriteShortLetter, transformCrosswordPuzzle, transformChooseThenAnswerGroup, transformSortWordsIntoCategories, } from '../../../components/exams/take/utils/question-transformers';
7
+ import { MoversAnswerTheQuestionRenderer, MoversGridFillRenderer, MoversLabelThePictureRenderer, MoversMatchByWritingAnswerRenderer, MoversMatchWordToPictureRenderer, MoversReadingPassageRenderer, MoversTrueFalseRenderer, MoversTrueFalseCorrectGroupRenderer, MoversFillInBlankGroupRenderer, MoversMatchingWithLinesGroupRenderer, MoversCrossOutWordGroupRenderer, MoversWordFillParagraphRenderer, MoversWordFillStructuredFormRenderer, MoversWordOrderingRenderer, MoversWordOrderingGroupRenderer, MoversWriteSentencesRenderer, MoversWritingRenderer, MoversCrosswordPuzzleRenderer, ChooseThenAnswerGroupRenderer, SortWordsIntoCategoriesRenderer, } from '../../../components/exams/take/components/question-renderers';
8
+ import { transformAnswerTheQuestion, transformGridFill, transformLabelPicture, transformMatchByWritingAnswer, transformMatchWordToPicture, transformReadingPassage, transformTrueFalse, transformTrueFalseCorrectGroup, transformFillInBlankGroup, transformMatchingWithLinesGroup, transformWordFillParagraph, transformWordFillStructuredForm, transformWordOrdering, transformWordOrderingGroupTake, transformWriteSentences, transformWriteShortLetter, transformCrosswordPuzzle, transformChooseThenAnswerGroup, transformSortWordsIntoCategories, } from '../../../components/exams/take/utils/question-transformers';
9
9
  import { normalizeCrossOutWordGroupAnswer, transformCrossOutWordGroup, } from '../../../components/exams/take/utils/cross-out-word-group.transformer';
10
10
  function asApiQuestions(questions) {
11
11
  return questions;
@@ -28,22 +28,6 @@ export function renderMoversReviewBody(ctx) {
28
28
  const gradingInfo = essayGradingMap[firstQuestion.id];
29
29
  return (_jsx(MoversWritingRenderer, { partNumber: partNo, partName: part.name, prompt: data.prompt, requirements: data.requirements, minWords: data.minWords, maxWords: data.maxWords, answer: writingAnswer, onAnswerChange: handleAnswerChange, isReviewMode: true, guideImages: data.guideImages, passage: data.passage, teacherScore: gradingInfo?.score, maxScore: gradingInfo?.maxScore, teacherFeedback: gradingInfo?.teacherFeedback, writingAssessment: gradingInfo?.writingAssessment }));
30
30
  }
31
- case 'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER': {
32
- const data = transformPictureChoose(apiQuestions);
33
- const subQuestionsWithCorrect = data.subQuestions.map((sq) => ({
34
- ...sq,
35
- correctOptionId: correctOptionIdMap[sq.id],
36
- }));
37
- return (_jsx(MoversPictureChooseRenderer, { partNumber: partNo, partName: part.name, instruction: part.instructions || data.instruction, questionCount: questionCount, subQuestions: subQuestionsWithCorrect, answers: answers, onAnswerChange: handleAnswerChange, isReviewMode: true }));
38
- }
39
- case 'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER': {
40
- const data = transformPictureFillBlankChoose(apiQuestions);
41
- const subQuestionsWithCorrect = data.subQuestions.map((sq) => ({
42
- ...sq,
43
- correctOptionId: correctOptionIdMap[sq.id],
44
- }));
45
- return (_jsx(MoversPictureFillBlankChooseRenderer, { partNumber: partNo, partName: part.name, instruction: part.instructions || data.instruction, questionCount: questionCount, subQuestions: subQuestionsWithCorrect, answers: answers, onAnswerChange: handleAnswerChange, isReviewMode: true }));
46
- }
47
31
  case 'READ_PASSAGE_AND_ANSWER_QUESTIONS': {
48
32
  const data = transformReadingPassage(apiQuestions);
49
33
  return (_jsx(MoversReadingPassageRenderer, { partNumber: partNo, partName: part.name, instruction: part.instructions || data.instruction, questionCount: data.questions.length, passageTitle: data.passageTitle, passage: data.passage, passageImageUrl: data.passageImageUrl, questions: data.questions, answers: answers, onAnswerChange: handleAnswerChange, isReviewMode: true, correctOptionIdMap: correctOptionIdMap }));
@@ -58,14 +42,6 @@ export function renderMoversReviewBody(ctx) {
58
42
  }));
59
43
  return (_jsx(MoversLabelThePictureRenderer, { partNumber: partNo, partName: part.name, instruction: part.instructions || data.instruction, questionCount: data.labels.length, imageUrl: data.imageUrl, wordBank: data.wordBank, labels: labelsWithCorrect, answers: answers, onAnswerChange: handleAnswerChange, isReviewMode: true }));
60
44
  }
61
- case 'LOOK_PICTURE_FILL_WORD_HINT': {
62
- const data = transformFillWordHint(apiQuestions);
63
- const subQuestionsWithCorrect = data.subQuestions.map((sq) => ({
64
- ...sq,
65
- correctAnswer: correctAnswerTextMap[sq.id],
66
- }));
67
- return (_jsx(MoversFillWordHintRenderer, { partNumber: partNo, partName: part.name, instruction: part.instructions || data.instruction, questionCount: questionCount, hintLetters: data.hintLetters, subQuestions: subQuestionsWithCorrect, answers: answers, onAnswerChange: handleAnswerChange, isReviewMode: true }));
68
- }
69
45
  case 'FILL_MISSING_WORDS_IN_GRID': {
70
46
  const data = transformGridFill(apiQuestions);
71
47
  const questionId = questions[0]?.id;
@@ -1,7 +1,6 @@
1
1
  import { normalizeCrossOutWordGroupAnswer } from '../../components/exams/take/utils/cross-out-word-group.transformer';
2
2
  const FLATTENED_ANSWER_TYPES = new Set([
3
3
  'READ_PASSAGE_AND_ANSWER_QUESTIONS',
4
- 'LOOK_PICTURE_FILL_WORD_HINT',
5
4
  'LABEL_THE_PICTURE',
6
5
  'READ_AND_COLOR_OBJECTS',
7
6
  'IMAGE_OBJECT_MATCHING',
@@ -286,9 +285,6 @@ function transformStudentAnswer(questionId, questionType, value) {
286
285
  }
287
286
  if (questionType === 'LABEL_THE_PICTURE')
288
287
  return record.answers ?? value;
289
- if (questionType === 'LOOK_PICTURE_FILL_WORD_HINT' && typeof record.answer === 'string' && record.answer.includes(',')) {
290
- return Object.fromEntries(record.answer.split(',').map((item, index) => [`${questionId}-blank-${index}`, item.trim()]));
291
- }
292
288
  if (questionType === 'WORD_FILL_PARAGRAPH') {
293
289
  if (Array.isArray(record.answers))
294
290
  return record.answers;
@@ -4,6 +4,7 @@ interface PointsInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, '
4
4
  error?: string;
5
5
  required?: boolean;
6
6
  allowZero?: boolean;
7
+ onValueChange?: (value: number) => void;
7
8
  }
8
9
  /**
9
10
  * A specialized input component for "Điểm số" (points/score) fields.
@@ -10,7 +10,7 @@ import { cn } from '../../shared/lib/utils';
10
10
  * - Allows clearing the input to enter new values
11
11
  * - Strips non-digit characters automatically
12
12
  */
13
- export const PointsInput = forwardRef(({ label = 'Điểm số', error, required = true, allowZero = false, className, id, ...props }, ref) => {
13
+ export const PointsInput = forwardRef(({ label = 'Điểm số', error, required = true, allowZero = false, className, id, onValueChange, onChange, ...props }, ref) => {
14
14
  const inputId = id || 'points';
15
15
  const handleInput = (e) => {
16
16
  const input = e.target;
@@ -28,6 +28,16 @@ export const PointsInput = forwardRef(({ label = 'Điểm số', error, required
28
28
  input.value = intValue < minVal ? minVal.toString() : intValue.toString();
29
29
  }
30
30
  };
31
- return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Label, { htmlFor: inputId, className: "whitespace-nowrap text-sm font-medium", children: [label, " ", required && _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { ref: ref, id: inputId, type: "number", step: "1", min: allowZero ? "0" : "1", onInput: handleInput, className: cn('w-20', error && 'border-red-500 focus:ring-red-500', className), ...props }), error && _jsx("p", { className: "text-sm text-red-600", children: error })] }));
31
+ return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Label, { htmlFor: inputId, className: "whitespace-nowrap text-sm font-medium", children: [label, " ", required && _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { ref: ref, id: inputId, type: "number", step: "1", min: allowZero ? "0" : "1", onInput: handleInput, onChange: (e) => {
32
+ onChange?.(e);
33
+ if (onValueChange) {
34
+ const raw = e.target.value;
35
+ if (raw === '')
36
+ return;
37
+ const parsed = parseInt(raw, 10);
38
+ if (!Number.isNaN(parsed))
39
+ onValueChange(parsed);
40
+ }
41
+ }, className: cn('w-20', error && 'border-red-500 focus:ring-red-500', className), ...props }), error && _jsx("p", { className: "text-sm text-red-600", children: error })] }));
32
42
  });
33
43
  PointsInput.displayName = 'PointsInput';