@tinyweb_dev/oe-exam-sdk 1.0.5 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/dist/components/exams/ExamCreator.js +1 -16
  2. package/dist/components/exams/take/ExamTakingPageContainer.js +0 -12
  3. package/dist/components/exams/take/components/QuestionRenderer.js +2 -15
  4. package/dist/components/exams/take/components/question-renderers/index.d.ts +0 -3
  5. package/dist/components/exams/take/components/question-renderers/index.js +0 -3
  6. package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionRenderer.js +0 -4
  7. package/dist/components/exams/take/types/speaking.types.d.ts +0 -31
  8. package/dist/components/exams/take/types.d.ts +0 -65
  9. package/dist/components/exams/take/utils/answer-transformers.js +6 -60
  10. package/dist/components/exams/take/utils/question-transformers.d.ts +2 -15
  11. package/dist/components/exams/take/utils/question-transformers.js +0 -143
  12. package/dist/components/questions/_shared/components/compact/CompactCreatorHeader.d.ts +23 -0
  13. package/dist/components/questions/_shared/components/compact/CompactCreatorHeader.js +9 -0
  14. package/dist/components/questions/_shared/components/compact/CompactExampleToggle.d.ts +6 -0
  15. package/dist/components/questions/_shared/components/compact/CompactExampleToggle.js +8 -0
  16. package/dist/components/questions/_shared/components/compact/CompactExplanationToggle.d.ts +7 -0
  17. package/dist/components/questions/_shared/components/compact/CompactExplanationToggle.js +14 -0
  18. package/dist/components/questions/_shared/components/compact/CompactFieldCard.d.ts +9 -0
  19. package/dist/components/questions/_shared/components/compact/CompactFieldCard.js +7 -0
  20. package/dist/components/questions/_shared/components/compact/CompactPreviewBanner.d.ts +7 -0
  21. package/dist/components/questions/_shared/components/compact/CompactPreviewBanner.js +7 -0
  22. package/dist/components/questions/_shared/components/compact/CompactPreviewButton.d.ts +6 -0
  23. package/dist/components/questions/_shared/components/compact/CompactPreviewButton.js +7 -0
  24. package/dist/components/questions/_shared/components/compact/index.d.ts +6 -0
  25. package/dist/components/questions/_shared/components/compact/index.js +6 -0
  26. package/dist/components/questions/_shared/config/question-types.config.js +0 -24
  27. package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +0 -9
  28. package/dist/components/questions/_shared/types/index.d.ts +0 -1
  29. package/dist/components/questions/_shared/types/index.js +0 -1
  30. package/dist/components/questions/_shared/types/look-and-choose-answer.type.d.ts +0 -13
  31. package/dist/components/questions/_shared/utils/answer-builder.js +0 -11
  32. package/dist/components/questions/components/ColorRegionCreator.js +66 -63
  33. package/dist/components/questions/components/QuestionSearchDropdown.js +0 -8
  34. package/dist/components/questions/components/QuestionTypeSelector.js +0 -2
  35. package/dist/components/questions/components/index.d.ts +1 -0
  36. package/dist/components/questions/components/index.js +1 -0
  37. package/dist/components/questions/creator/question-type-registry.js +0 -8
  38. package/dist/components/questions/groups/ArticlesGroupCard.js +2 -2
  39. package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -1
  40. package/dist/components/questions/groups/DocumentGroupCard.js +1 -1
  41. package/dist/components/questions/groups/ImageLabelGroupCard.js +1 -1
  42. package/dist/components/questions/groups/MatchAnswerGroupCard.js +2 -2
  43. package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +0 -3
  44. package/dist/components/questions/question-bank/QuestionEditorRenderer.js +1 -10
  45. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.d.ts +0 -2
  46. package/dist/components/questions/question-bank/editor-adapters/choice-adapters.js +0 -41
  47. package/dist/components/questions/question-bank/editor-adapters/index.d.ts +2 -2
  48. package/dist/components/questions/question-bank/editor-adapters/index.js +2 -2
  49. package/dist/components/questions/question-bank/editor-adapters/media-adapters.d.ts +0 -1
  50. package/dist/components/questions/question-bank/editor-adapters/media-adapters.js +0 -28
  51. package/dist/components/questions/question-bank/question-bank-picker.utils.js +0 -7
  52. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +16 -16
  53. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +36 -30
  54. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupClient.js +1 -1
  55. package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupCreator.js +7 -6
  56. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +2 -2
  57. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +11 -7
  58. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -1
  59. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +11 -5
  60. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +5 -5
  61. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +34 -28
  62. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseAnswerGroupImageUpload.js +2 -2
  63. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +1 -1
  64. package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupCreator.js +44 -28
  65. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupClient.js +7 -7
  66. package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupCreator.js +8 -5
  67. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +2 -2
  68. package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +20 -16
  69. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.js +1 -1
  70. package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleCreator.js +66 -29
  71. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +5 -5
  72. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +45 -37
  73. package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupCreator.js +24 -20
  74. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +2 -2
  75. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +68 -65
  76. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixClient.js +1 -1
  77. package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixCreator.js +45 -5
  78. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +2 -2
  79. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +18 -14
  80. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +2 -2
  81. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +30 -30
  82. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +2 -2
  83. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +24 -31
  84. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +2 -2
  85. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +29 -32
  86. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -1
  87. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +27 -29
  88. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -1
  89. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +11 -15
  90. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -1
  91. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +22 -24
  92. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -1
  93. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +17 -24
  94. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -1
  95. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +31 -31
  96. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -1
  97. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +11 -15
  98. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -1
  99. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +14 -15
  100. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +2 -2
  101. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +27 -25
  102. package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesClient.js +12 -12
  103. package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesCreator.js +8 -5
  104. package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureClient.js +8 -8
  105. package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureCreator.js +25 -20
  106. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupClient.js +1 -1
  107. package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupCreator.js +13 -10
  108. package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupCreator.js +8 -5
  109. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +7 -11
  110. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -1
  111. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +15 -12
  112. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesClient.js +1 -1
  113. package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesCreator.js +5 -5
  114. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +35 -39
  115. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +2 -2
  116. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardClient.js +1 -1
  117. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +7 -8
  118. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -1
  119. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +30 -23
  120. package/dist/components/questions/types/spontaneous-qa/SpontaneousQAClient.js +1 -1
  121. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +26 -19
  122. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.js +1 -1
  123. package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.js +13 -11
  124. package/dist/components/questions/types/true-false/TrueFalseClient.js +2 -2
  125. package/dist/components/questions/types/true-false/TrueFalseCreator.js +35 -29
  126. package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupClient.js +1 -1
  127. package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupCreator.js +11 -7
  128. package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +1 -1
  129. package/dist/components/questions/types/true-false-group/TrueFalseGroupCreator.js +11 -7
  130. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +2 -2
  131. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +21 -56
  132. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -1
  133. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +30 -24
  134. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.js +3 -3
  135. package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupCreator.js +12 -14
  136. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -1
  137. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +27 -18
  138. package/dist/components/questions/types/word-ordering-group/WordOrderingGroupClient.js +7 -7
  139. package/dist/components/questions/types/word-ordering-group/WordOrderingGroupCreator.js +16 -13
  140. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +2 -2
  141. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +34 -28
  142. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +2 -2
  143. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +22 -22
  144. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +21 -18
  145. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -1
  146. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +7 -4
  147. package/dist/components/questions/viewer/QuestionViewer.js +0 -98
  148. package/dist/components/results/renderers/review-question-body-movers.js +2 -26
  149. package/dist/components/results/review-data.js +0 -4
  150. package/dist/components/ui/points-input.d.ts +1 -0
  151. package/dist/components/ui/points-input.js +12 -2
  152. package/dist/shared/constants/question-skills.js +0 -11
  153. package/dist/shared/lib/i18n/messages/en/admin.d.ts +0 -3
  154. package/dist/shared/lib/i18n/messages/en/admin.js +0 -3
  155. package/dist/shared/lib/i18n/messages/en.d.ts +0 -3
  156. package/dist/shared/lib/i18n/messages/vi/admin.d.ts +0 -3
  157. package/dist/shared/lib/i18n/messages/vi/admin.js +0 -3
  158. package/dist/shared/lib/i18n/messages/vi.d.ts +0 -3
  159. package/dist/shared/lib/utils/question-reverse-transform.js +0 -19
  160. package/dist/shared/lib/utils/question-transform-types.d.ts +1 -23
  161. package/dist/shared/lib/utils/question-transform-validation.js +0 -40
  162. package/dist/shared/lib/utils/question-transform.js +0 -9
  163. package/dist/shared/types/common.types.d.ts +1 -1
  164. package/dist/shared/types/entities/question.types.d.ts +1 -11
  165. package/dist/shared/types/questions/index.d.ts +0 -3
  166. package/dist/shared/types/questions/index.js +0 -6
  167. package/package.json +1 -1
  168. package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.d.ts +0 -2
  169. package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.js +0 -131
  170. package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.d.ts +0 -2
  171. package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.js +0 -76
  172. package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.d.ts +0 -2
  173. package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.js +0 -68
  174. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.d.ts +0 -16
  175. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.js +0 -212
  176. package/dist/components/questions/_shared/types/gn-speaking-interview.type.d.ts +0 -32
  177. package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +0 -1
  178. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.d.ts +0 -47
  179. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +0 -1
  180. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.d.ts +0 -2
  181. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +0 -262
  182. package/dist/components/questions/types/gn-speaking-interview/register.d.ts +0 -2
  183. package/dist/components/questions/types/gn-speaking-interview/register.js +0 -121
  184. package/dist/components/questions/types/gn-speaking-interview/transform.d.ts +0 -2
  185. package/dist/components/questions/types/gn-speaking-interview/transform.js +0 -48
  186. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.d.ts +0 -20
  187. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +0 -72
  188. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.d.ts +0 -15
  189. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +0 -230
  190. package/dist/components/questions/types/look-picture-choose-correct-answer/index.d.ts +0 -1
  191. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +0 -1
  192. package/dist/components/questions/types/look-picture-choose-correct-answer/register.d.ts +0 -2
  193. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +0 -66
  194. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.d.ts +0 -2
  195. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +0 -42
  196. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.d.ts +0 -6
  197. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +0 -96
  198. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.d.ts +0 -15
  199. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +0 -238
  200. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.d.ts +0 -2
  201. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +0 -2
  202. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.d.ts +0 -2
  203. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +0 -76
  204. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.d.ts +0 -2
  205. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +0 -19
  206. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.d.ts +0 -7
  207. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +0 -134
  208. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.d.ts +0 -3
  209. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +0 -617
  210. package/dist/components/questions/types/look-picture-fill-word-hint/index.d.ts +0 -2
  211. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +0 -2
  212. package/dist/components/questions/types/look-picture-fill-word-hint/register.d.ts +0 -2
  213. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +0 -82
  214. package/dist/components/questions/types/look-picture-fill-word-hint/transform.d.ts +0 -2
  215. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +0 -29
  216. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.d.ts +0 -9
  217. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +0 -12
  218. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.d.ts +0 -17
  219. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +0 -47
  220. package/dist/shared/types/questions/look-picture-choose-correct-answer.d.ts +0 -38
  221. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +0 -5
  222. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.d.ts +0 -63
  223. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +0 -9
  224. package/dist/shared/types/questions/look-picture-fill-word-hint.d.ts +0 -57
  225. package/dist/shared/types/questions/look-picture-fill-word-hint.js +0 -7
@@ -1711,9 +1711,6 @@ const admin = {
1711
1711
  'admin.exams.partEditor.skillReading': 'Đọc',
1712
1712
  'admin.exams.partEditor.skillWriting': 'Viết',
1713
1713
  'admin.exams.partEditor.qtFillMissingWordsInGrid': 'Điền chữ cái còn thiếu',
1714
- 'admin.exams.partEditor.qtLookPictureFillBlankChooseAnswer': 'Nhìn tranh, điền vào chỗ trống',
1715
- 'admin.exams.partEditor.qtLookPictureChooseCorrectAnswer': 'Nhìn tranh, chọn đáp án đúng',
1716
- 'admin.exams.partEditor.qtLookPictureFillWordHint': 'Nhìn tranh, điền từ (có gợi ý)',
1717
1714
  'admin.exams.partEditor.qtLabelThePicture': 'Gắn nhãn cho tranh',
1718
1715
  'admin.exams.partEditor.qtReadAndColorObjects': 'Đọc và tô màu',
1719
1716
  'admin.exams.partEditor.qtMultipleChoice': 'Trắc nghiệm',
@@ -1718,9 +1718,6 @@ declare const vi: {
1718
1718
  readonly 'admin.exams.partEditor.skillReading': "Đọc";
1719
1719
  readonly 'admin.exams.partEditor.skillWriting': "Viết";
1720
1720
  readonly 'admin.exams.partEditor.qtFillMissingWordsInGrid': "Điền chữ cái còn thiếu";
1721
- readonly 'admin.exams.partEditor.qtLookPictureFillBlankChooseAnswer': "Nhìn tranh, điền vào chỗ trống";
1722
- readonly 'admin.exams.partEditor.qtLookPictureChooseCorrectAnswer': "Nhìn tranh, chọn đáp án đúng";
1723
- readonly 'admin.exams.partEditor.qtLookPictureFillWordHint': "Nhìn tranh, điền từ (có gợi ý)";
1724
1721
  readonly 'admin.exams.partEditor.qtLabelThePicture': "Gắn nhãn cho tranh";
1725
1722
  readonly 'admin.exams.partEditor.qtReadAndColorObjects': "Đọc và tô màu";
1726
1723
  readonly 'admin.exams.partEditor.qtMultipleChoice': "Trắc nghiệm";
@@ -50,21 +50,6 @@ const reverseChooseTheCorrectAnswer = (apiQuestion) => {
50
50
  },
51
51
  };
52
52
  };
53
- const reversePictureChoice = (apiQuestion) => {
54
- const content = asRecord(apiQuestion.content);
55
- const answer = readAnswer(apiQuestion);
56
- return {
57
- content: '',
58
- answer: {
59
- imageUrl: asString(content.imageUrl),
60
- question: asString(content.question),
61
- options: asArray(content.options).map((option) => typeof option === 'string' ? option : asString(asRecord(option).text)),
62
- correctAnswer: answer.answer ?? answer.correctAnswer ?? -1,
63
- explanation: asString(apiQuestion.explanation),
64
- points: readPoints(apiQuestion),
65
- },
66
- };
67
- };
68
53
  const reverseFillInBlank = (apiQuestion) => {
69
54
  const content = asRecord(apiQuestion.content);
70
55
  const answer = readAnswer(apiQuestion);
@@ -796,15 +781,12 @@ const handlers = {
796
781
  CROSS_OUT_WORD_GROUP: reverseCrossOutWordGroup,
797
782
  WORD_ORDER_AND_MATCH_GROUP: reverseWordOrderAndMatchGroup,
798
783
  WORD_ORDER_AND_MATCH: reverseWordOrderAndMatchGroup,
799
- LOOK_PICTURE_CHOOSE_CORRECT_ANSWER: reversePictureChoice,
800
- LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER: reversePictureChoice,
801
784
  FILL_IN_BLANK: reverseFillInBlank,
802
785
  READ_PASSAGE_AND_ANSWER_QUESTIONS: reverseReadPassage,
803
786
  LABEL_THE_PICTURE: reverseLabelPicture,
804
787
  READ_AND_COLOR_OBJECTS: reverseGroupPayload,
805
788
  IMAGE_OBJECT_MATCHING: reverseGroupPayload,
806
789
  FILL_MISSING_WORDS_IN_GRID: reverseGrid,
807
- LOOK_PICTURE_FILL_WORD_HINT: reverseGroupPayload,
808
790
  ANSWER_THE_QUESTION: reverseGroupPayload,
809
791
  TRUE_FALSE: reverseSimpleAnswer,
810
792
  WORD_ORDERING: reverseSimpleAnswer,
@@ -832,7 +814,6 @@ const handlers = {
832
814
  SPONTANEOUS_QA_GROUP: reverseSpontaneousQaGroup,
833
815
  SPEAKING_CONVERSATION: reverseGroupPayload,
834
816
  SPEAKING_CUE_CARD: reverseSimpleAnswer,
835
- GN_SPEAKING_INTERVIEW: reverseGroupPayload,
836
817
  CROSSWORD_PUZZLE: reverseCrosswordPuzzle,
837
818
  FIND_WORDS_IN_MATRIX: reverseFindWordsInMatrix,
838
819
  SORT_WORDS_INTO_CATEGORIES: reverseSortWordsIntoCategories,
@@ -244,28 +244,6 @@ export interface FillMissingWordsInGridAnswerData {
244
244
  }>;
245
245
  caseSensitive?: boolean;
246
246
  }
247
- export interface LookAndChooseAnswerData {
248
- subQuestions: Array<{
249
- id: string;
250
- imageUrl: string;
251
- question: string;
252
- options: Array<{
253
- id: string;
254
- text: string;
255
- isCorrect: boolean;
256
- }>;
257
- }>;
258
- }
259
- export interface LookPictureFillWordHintAnswerData {
260
- subQuestions: Array<{
261
- id: string;
262
- imageUrl: string;
263
- word: string;
264
- correctAnswer: string;
265
- }>;
266
- hintLetters?: string[];
267
- caseSensitive?: boolean;
268
- }
269
247
  export interface LabelThePictureAnswerData {
270
248
  imageUrl: string;
271
249
  wordBank: string[];
@@ -374,7 +352,7 @@ export interface ListenAndDragObjectsIntoSceneAnswerData {
374
352
  }>;
375
353
  groupNumber?: number;
376
354
  }
377
- export type QuestionAnswerData = ListenImageMatchingAnswerData | ListenOrReadChooseAnswerData | ReadChooseAppropriateWordAnswerData | ListenAndChooseAnswerData | ListenAndFillWordAnswerData | TickTrueFalseOrYesNoAnswerData | LookPictureAndTickTrueFalseAnswerData | FillInTheBlankVariantsAnswerData | ListenAndColorObjectsAnswerData | ArrangeLettersIntoWordsAnswerData | WriteShortParagraphAnswerData | ListenAndWriteAnswerData | ViewImageAndFillInTheBlankAnswerData | ReadAndChooseBestAnswerData | LookPictureFillWordAnswerData | FillMissingWordsInGridAnswerData | LookAndChooseAnswerData | LookPictureFillWordHintAnswerData | LabelThePictureAnswerData | ReadAndColorObjectsAnswerData | ReadPassageAndAnswerQuestionsAnswerData | AnswerTheQuestionAnswerData | ListenAndChooseFromAnswerGroupAnswerData | ListenAndChooseObjectsInSceneAnswerData | ListenAndDragObjectsIntoSceneAnswerData;
355
+ export type QuestionAnswerData = ListenImageMatchingAnswerData | ListenOrReadChooseAnswerData | ReadChooseAppropriateWordAnswerData | ListenAndChooseAnswerData | ListenAndFillWordAnswerData | TickTrueFalseOrYesNoAnswerData | LookPictureAndTickTrueFalseAnswerData | FillInTheBlankVariantsAnswerData | ListenAndColorObjectsAnswerData | ArrangeLettersIntoWordsAnswerData | WriteShortParagraphAnswerData | ListenAndWriteAnswerData | ViewImageAndFillInTheBlankAnswerData | ReadAndChooseBestAnswerData | LookPictureFillWordAnswerData | FillMissingWordsInGridAnswerData | LabelThePictureAnswerData | ReadAndColorObjectsAnswerData | ReadPassageAndAnswerQuestionsAnswerData | AnswerTheQuestionAnswerData | ListenAndChooseFromAnswerGroupAnswerData | ListenAndChooseObjectsInSceneAnswerData | ListenAndDragObjectsIntoSceneAnswerData;
378
356
  export interface LookPictureFillWordAnswerData {
379
357
  instruction: string;
380
358
  wordLimit: number;
@@ -14,15 +14,6 @@ export function validateQuestionForTransform(question) {
14
14
  case 'FILL_MISSING_WORDS_IN_GRID':
15
15
  validateGrid(content, answer, errors);
16
16
  break;
17
- case 'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER':
18
- validatePictureFillBlank(answer, errors);
19
- break;
20
- case 'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER':
21
- validatePictureChoose(answer, errors);
22
- break;
23
- case 'LOOK_PICTURE_FILL_WORD_HINT':
24
- validateWordHint(answer, errors);
25
- break;
26
17
  case 'LABEL_THE_PICTURE':
27
18
  validateLabelPicture(answer, errors);
28
19
  break;
@@ -81,37 +72,6 @@ function validateGrid(content, answer, errors) {
81
72
  if (!isNonEmptyRecord(answer?.answers))
82
73
  errors.push('At least one missing letter is required');
83
74
  }
84
- function validatePictureFillBlank(answer, errors) {
85
- if (!hasText(answer?.imageUrl))
86
- errors.push('Vui lòng upload hình ảnh');
87
- if (!hasText(answer?.question))
88
- errors.push('Vui lòng nhập nội dung câu hỏi');
89
- validateOptions(answer?.options, errors);
90
- if (answer?.correctAnswer === undefined || answer.correctAnswer === -1)
91
- errors.push('Vui lòng chọn đáp án đúng');
92
- }
93
- function validatePictureChoose(answer, errors) {
94
- if (!hasText(answer?.imageUrl))
95
- errors.push('Vui lòng upload hình ảnh');
96
- validateOptions(answer?.options, errors);
97
- if (typeof answer?.correctAnswer !== 'number' || answer.correctAnswer < 0)
98
- errors.push('Vui lòng chọn đáp án đúng');
99
- }
100
- function validateWordHint(answer, errors) {
101
- if (!hasItems(answer?.subQuestions)) {
102
- errors.push('Vui lòng nhập dữ liệu câu hỏi');
103
- return;
104
- }
105
- answer.subQuestions.forEach((rawSubQuestion) => {
106
- const subQuestion = asRecord(rawSubQuestion);
107
- if (!hasText(subQuestion?.imageUrl))
108
- errors.push('Vui lòng upload hình ảnh');
109
- if (!hasText(subQuestion?.word))
110
- errors.push('Vui lòng nhập từ có chỗ trống');
111
- if (!hasText(subQuestion?.correctAnswer))
112
- errors.push('Vui lòng nhập đáp án đúng');
113
- });
114
- }
115
75
  function validateLabelPicture(answer, errors) {
116
76
  if (!hasText(answer?.imageUrl))
117
77
  errors.push('Vui lòng upload hình ảnh');
@@ -1,8 +1,5 @@
1
1
  import { validateQuestionForTransform } from './question-transform-validation';
2
2
  // ─── Import transforms từ từng question type ────────────────────────────────
3
- import { transformLookPictureChooseCorrectAnswer } from '../../../components/questions/types/look-picture-choose-correct-answer/transform';
4
- import { transformLookPictureFillBlankChooseAnswer } from '../../../components/questions/types/look-picture-fill-blank-choose-answer/transform';
5
- import { transformLookPictureFillWordHint } from '../../../components/questions/types/look-picture-fill-word-hint/transform';
6
3
  import { transformReadAndColorObjects } from '../../../components/questions/types/read-and-color-objects/transform';
7
4
  import { transformReadPassageAndAnswerQuestions } from '../../../components/questions/types/read-passage-and-answer-questions/transform';
8
5
  import { transformArrangeLettersIntoWords } from '../../../components/questions/types/arrange-letters-into-words/transform';
@@ -43,7 +40,6 @@ import { transformSpontaneousQA } from '../../../components/questions/types/spon
43
40
  import { transformSpontaneousQaGroup } from '../../../components/questions/types/spontaneous-qa-group/transform';
44
41
  import { transformSpeakingConversation } from '../../../components/questions/types/speaking-conversation/transform';
45
42
  import { transformSpeakingCueCard } from '../../../components/questions/types/speaking-cue-card/transform';
46
- import { transformGNSpeakingInterview } from '../../../components/questions/types/gn-speaking-interview/transform';
47
43
  import { transformWordFillStructuredForm } from '../../../components/questions/types/word-fill-structured-form/transform';
48
44
  import { transformCrosswordPuzzle } from '../../../components/questions/types/crossword-puzzle/transform';
49
45
  import { transformFindWordsInMatrix } from '../../../components/questions/types/find-words-in-matrix/transform';
@@ -54,10 +50,6 @@ import { transformMatchWordToPictureGroup } from '../../../components/questions/
54
50
  import { transformWordOrderingGroup } from '../../../components/questions/types/word-ordering-group/transform';
55
51
  // ─── Handler map ────────────────────────────────────────────────────────────
56
52
  const questionTransformHandlers = {
57
- // Look picture
58
- LOOK_PICTURE_CHOOSE_CORRECT_ANSWER: transformLookPictureChooseCorrectAnswer,
59
- LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER: transformLookPictureFillBlankChooseAnswer,
60
- LOOK_PICTURE_FILL_WORD_HINT: transformLookPictureFillWordHint,
61
53
  // Reading
62
54
  READ_AND_COLOR_OBJECTS: transformReadAndColorObjects,
63
55
  IMAGE_OBJECT_MATCHING: transformReadAndColorObjects,
@@ -101,7 +93,6 @@ const questionTransformHandlers = {
101
93
  SPONTANEOUS_QA_GROUP: transformSpontaneousQaGroup,
102
94
  SPEAKING_CONVERSATION: transformSpeakingConversation,
103
95
  SPEAKING_CUE_CARD: transformSpeakingCueCard,
104
- GN_SPEAKING_INTERVIEW: transformGNSpeakingInterview,
105
96
  WORD_FILL_STRUCTURED_FORM: transformWordFillStructuredForm,
106
97
  CROSSWORD_PUZZLE: transformCrosswordPuzzle,
107
98
  FIND_WORDS_IN_MATRIX: transformFindWordsInMatrix,
@@ -29,7 +29,7 @@ export declare enum StudentSelectionType {
29
29
  }
30
30
  export type ResultStatus = 'PENDING' | 'PARTIAL_GRADED' | 'GRADED';
31
31
  export type StudentStatus = 'NOT_STARTED' | 'IN_PROGRESS' | 'SUBMITTED';
32
- export type QuestionType = 'FILL_IN_BLANK' | 'TRUE_FALSE' | 'MATCHING' | 'ORDERING' | 'LISTENING' | 'COLORING' | 'ESSAY' | 'LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE' | 'LISTEN_AND_TICK_ANSWER' | 'LISTEN_AND_FILL_NAME_NUMBER' | 'LISTENING_FILL_BLANK' | 'LISTENING_COLOR' | 'TICK_TRUE_OR_FALSE' | 'TICK_YES_OR_NO' | 'READING_TICK_CROSS' | 'ARRANGE_LETTERS_INTO_WORDS' | 'READ_PASSAGE_AND_COMPLETE_STORY' | 'CLOZE_TEST_WITH_TICK_BOX' | 'READ_AND_CHOOSE_APPROPRIATE_WORD' | 'WRITE_SHORT_PARAGRAPH' | 'LABEL_THE_PICTURE' | 'READING_LETTER_ARRANGE' | 'READING_PASSAGE_FILL' | 'FILL_BLANK' | 'FILL_MISSING_WORDS_IN_GRID' | 'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER' | 'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER' | 'LOOK_PICTURE_FILL_WORD_HINT' | 'LABEL_THE_PICTURE' | 'READ_AND_COLOR_OBJECTS' | 'IMAGE_OBJECT_MATCHING' | 'READ_PASSAGE_AND_ANSWER_QUESTIONS' | 'CHOOSE_THE_CORRECT_ANSWER' | 'CHOOSE_THE_CORRECT_ANSWER_GROUP' | 'TRUE_FALSE_GROUP' | 'TRUE_FALSE_CORRECT_GROUP' | 'FILL_IN_BLANK_GROUP' | 'MATCHING_WITH_LINES_GROUP' | 'CROSS_OUT_WORD_GROUP' | 'SPONTANEOUS_QA_GROUP' | 'WORD_ORDER_AND_MATCH_GROUP' | 'WRITE_A_SHORT_LETTER' | 'WRITE_CORRECT_VERB_FORM' | 'CHOOSE_CORRECT_ADJECTIVE' | 'ANSWER_THE_QUESTION' | 'ANSWER_THE_QUESTION_GROUP' | 'WORD_ORDERING' | 'WORD_FILL_PARAGRAPH' | 'MATCH_BY_WRITING_ANSWER' | 'MATCH_WORD_TO_PICTURE' | 'WRITE_SENTENCES' | 'LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP' | 'LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE' | 'LISTEN_AND_DRAG_OBJECTS_INTO_SCENE' | 'LISTEN_AND_SPEAK_ANSWER' | 'LISTEN_AND_SPEAK_IMAGE_GROUP' | 'LISTEN_AND_SPEAK_QUESTION_LIST' | 'LISTEN_AND_SPEAK_COMPARE_IMAGES' | 'LISTEN_AND_SPEAK_WITH_STORY_IMAGES' | 'LISTEN_AND_SPEAK_ODD_ONE_OUT' | 'LISTEN_AND_SPEAK_INFO_EXCHANGE' | 'READ_DISPLAYED_CONTENT' | 'SPEAKING_DESCRIBE_IMAGE' | 'SPONTANEOUS_QA' | 'SPEAKING_CONVERSATION' | 'GN_SPEAKING_INTERVIEW' | 'SPEAKING_CUE_CARD' | 'WORD_FILL_STRUCTURED_FORM' | 'CROSSWORD_PUZZLE' | 'FIND_WORDS_IN_MATRIX' | 'SORT_WORDS_INTO_CATEGORIES' | 'CHOOSE_THEN_ANSWER_GROUP' | 'MATCH_COLUMNS_TO_MAKE_SENTENCES' | 'MATCH_WORD_TO_PICTURE_GROUP' | 'WORD_ORDERING_GROUP';
32
+ export type QuestionType = 'FILL_IN_BLANK' | 'TRUE_FALSE' | 'MATCHING' | 'ORDERING' | 'LISTENING' | 'COLORING' | 'ESSAY' | 'LISTEN_AND_FILL_IN_THE_BLANK_WITH_IMAGE' | 'LISTEN_AND_TICK_ANSWER' | 'LISTEN_AND_FILL_NAME_NUMBER' | 'LISTENING_FILL_BLANK' | 'LISTENING_COLOR' | 'TICK_TRUE_OR_FALSE' | 'TICK_YES_OR_NO' | 'READING_TICK_CROSS' | 'ARRANGE_LETTERS_INTO_WORDS' | 'READ_PASSAGE_AND_COMPLETE_STORY' | 'CLOZE_TEST_WITH_TICK_BOX' | 'READ_AND_CHOOSE_APPROPRIATE_WORD' | 'WRITE_SHORT_PARAGRAPH' | 'LABEL_THE_PICTURE' | 'READING_LETTER_ARRANGE' | 'READING_PASSAGE_FILL' | 'FILL_BLANK' | 'FILL_MISSING_WORDS_IN_GRID' | 'LABEL_THE_PICTURE' | 'READ_AND_COLOR_OBJECTS' | 'IMAGE_OBJECT_MATCHING' | 'READ_PASSAGE_AND_ANSWER_QUESTIONS' | 'CHOOSE_THE_CORRECT_ANSWER' | 'CHOOSE_THE_CORRECT_ANSWER_GROUP' | 'TRUE_FALSE_GROUP' | 'TRUE_FALSE_CORRECT_GROUP' | 'FILL_IN_BLANK_GROUP' | 'MATCHING_WITH_LINES_GROUP' | 'CROSS_OUT_WORD_GROUP' | 'SPONTANEOUS_QA_GROUP' | 'WORD_ORDER_AND_MATCH_GROUP' | 'WRITE_A_SHORT_LETTER' | 'WRITE_CORRECT_VERB_FORM' | 'CHOOSE_CORRECT_ADJECTIVE' | 'ANSWER_THE_QUESTION' | 'ANSWER_THE_QUESTION_GROUP' | 'WORD_ORDERING' | 'WORD_FILL_PARAGRAPH' | 'MATCH_BY_WRITING_ANSWER' | 'MATCH_WORD_TO_PICTURE' | 'WRITE_SENTENCES' | 'LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP' | 'LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE' | 'LISTEN_AND_DRAG_OBJECTS_INTO_SCENE' | 'LISTEN_AND_SPEAK_ANSWER' | 'LISTEN_AND_SPEAK_IMAGE_GROUP' | 'LISTEN_AND_SPEAK_QUESTION_LIST' | 'LISTEN_AND_SPEAK_COMPARE_IMAGES' | 'LISTEN_AND_SPEAK_WITH_STORY_IMAGES' | 'LISTEN_AND_SPEAK_ODD_ONE_OUT' | 'LISTEN_AND_SPEAK_INFO_EXCHANGE' | 'READ_DISPLAYED_CONTENT' | 'SPEAKING_DESCRIBE_IMAGE' | 'SPONTANEOUS_QA' | 'SPEAKING_CONVERSATION' | 'SPEAKING_CUE_CARD' | 'WORD_FILL_STRUCTURED_FORM' | 'CROSSWORD_PUZZLE' | 'FIND_WORDS_IN_MATRIX' | 'SORT_WORDS_INTO_CATEGORIES' | 'CHOOSE_THEN_ANSWER_GROUP' | 'MATCH_COLUMNS_TO_MAKE_SENTENCES' | 'MATCH_WORD_TO_PICTURE_GROUP' | 'WORD_ORDERING_GROUP';
33
33
  export type Level = 'Pre-A1' | 'A1' | 'A2' | 'B1' | 'B2' | 'C1' | 'C2';
34
34
  export type Difficulty = 'EASY' | 'MEDIUM' | 'HARD';
35
35
  export type Skill = 'LISTENING' | 'READING' | 'WRITING' | 'SPEAKING';
@@ -217,16 +217,6 @@ export interface LookAndChooseAnswer {
217
217
  }>;
218
218
  }>;
219
219
  }
220
- export interface LookPictureFillWordHintAnswer {
221
- subQuestions: Array<{
222
- id: string;
223
- imageUrl: string;
224
- word: string;
225
- correctAnswer: string;
226
- hintLetters?: string[];
227
- }>;
228
- caseSensitive?: boolean;
229
- }
230
220
  export interface ReadPassageAndAnswerQuestionsAnswer {
231
221
  passage: string;
232
222
  passageTitle?: string;
@@ -267,7 +257,7 @@ export interface ListenAndChooseObjectsInSceneAnswer {
267
257
  }>;
268
258
  correctObjectId: string;
269
259
  }
270
- export type QuestionAnswer = MultipleChoiceAnswer | FillInBlankAnswer | TrueFalseAnswer | MatchingAnswer | OrderingAnswer | ListeningAnswer | ColoringAnswer | EssayAnswer | ListeningImageMatchingAnswer | ListeningFillBlankAnswer | ListeningMultipleChoiceAnswer | ListeningColorAnswer | ReadingTickCrossAnswer | ReadingLetterArrangeAnswer | ReadingPassageFillAnswer | ListenAndTickAnswer | ListenAndFillWordAnswer | ViewImageAndFillInTheBlankAnswer | FillMissingWordsInGridAnswer | LookAndChooseAnswer | LookPictureFillWordHintAnswer | ReadPassageAndAnswerQuestionsAnswer | WriteCorrectVerbFormAnswer | ListenAndChooseObjectsInSceneAnswer;
260
+ export type QuestionAnswer = MultipleChoiceAnswer | FillInBlankAnswer | TrueFalseAnswer | MatchingAnswer | OrderingAnswer | ListeningAnswer | ColoringAnswer | EssayAnswer | ListeningImageMatchingAnswer | ListeningFillBlankAnswer | ListeningMultipleChoiceAnswer | ListeningColorAnswer | ReadingTickCrossAnswer | ReadingLetterArrangeAnswer | ReadingPassageFillAnswer | ListenAndTickAnswer | ListenAndFillWordAnswer | ViewImageAndFillInTheBlankAnswer | FillMissingWordsInGridAnswer | LookAndChooseAnswer | ReadPassageAndAnswerQuestionsAnswer | WriteCorrectVerbFormAnswer | ListenAndChooseObjectsInSceneAnswer;
271
261
  export interface Question extends BaseEntity {
272
262
  type: QuestionType;
273
263
  content: string;
@@ -21,10 +21,7 @@ export * from './choose-the-correct-answer-group';
21
21
  export * from './cross-out-word-group';
22
22
  export * from './write-correct-verb-form';
23
23
  export * from './choose-correct-adjective';
24
- export * from './look-picture-fill-blank-choose-answer';
25
24
  export * from './fill-missing-words-in-grid';
26
- export * from './look-picture-fill-word-hint';
27
- export * from './look-picture-choose-correct-answer';
28
25
  export * from './listen-and-speak-answer';
29
26
  export * from './read-displayed-content';
30
27
  export * from './speaking-describe-image';
@@ -27,14 +27,8 @@ export * from './cross-out-word-group';
27
27
  export * from './write-correct-verb-form';
28
28
  // CHOOSE_CORRECT_ADJECTIVE
29
29
  export * from './choose-correct-adjective';
30
- // LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER
31
- export * from './look-picture-fill-blank-choose-answer';
32
30
  // FILL_MISSING_WORDS_IN_GRID
33
31
  export * from './fill-missing-words-in-grid';
34
- // LOOK_PICTURE_FILL_WORD_HINT
35
- export * from './look-picture-fill-word-hint';
36
- // LOOK_PICTURE_CHOOSE_CORRECT_ANSWER
37
- export * from './look-picture-choose-correct-answer';
38
32
  // Add more question types here as they are created:
39
33
  // etc.
40
34
  export * from './listen-and-speak-answer';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinyweb_dev/oe-exam-sdk",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "Reusable OceanEdu question components.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,2 +0,0 @@
1
- import type { MoversFillWordHintRendererProps } from '../../types';
2
- export declare function MoversFillWordHintRenderer({ partNumber, partName, questionCount, instruction, hintLetters, subQuestions, answers, onAnswerChange, isReviewMode, }: MoversFillWordHintRendererProps): import("react").JSX.Element;
@@ -1,131 +0,0 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState } from 'react';
4
- import { ZoomIn, Check } from 'lucide-react';
5
- import { ResolvedImage } from '../../../../../components/common/ResolvedImage';
6
- import CambridgeYlePartBanner from '../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner';
7
- import CambridgeYleInstructionBanner from '../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner';
8
- // Image with zoom functionality
9
- function QuestionImage({ imageUrl, alt }) {
10
- const [isZoomed, setIsZoomed] = useState(false);
11
- // Don't render if no imageUrl
12
- if (!imageUrl) {
13
- return null;
14
- }
15
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative flex items-center justify-center", children: [_jsx(ResolvedImage, { src: imageUrl, alt: alt, className: "h-24 w-24 cursor-pointer rounded-lg object-cover shadow-md", onClick: () => setIsZoomed(true) }), _jsx("button", { type: "button", onClick: () => setIsZoomed(true), className: "absolute -right-1 -top-1 rounded-full bg-white/80 p-1 shadow-md hover:bg-white", "aria-label": "Zoom image", children: _jsx(ZoomIn, { className: "h-3 w-3 text-gray-600" }) })] }), isZoomed && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/70", onClick: () => setIsZoomed(false), children: _jsx(ResolvedImage, { src: imageUrl, alt: `${alt} (zoomed)`, className: "max-h-[90vh] max-w-[90vw] rounded-lg object-contain", showLoadingState: false }) }))] }));
16
- }
17
- // Hint Letters Component
18
- function HintLetters({ letters }) {
19
- return (_jsxs("div", { className: "rounded-lg border-2 border-amber-200 bg-amber-50 p-4", children: [_jsx("p", { className: "mb-3 text-sm font-semibold text-amber-800", children: "Available Letters:" }), _jsx("div", { className: "flex flex-wrap gap-2", children: letters.map((letter, index) => (_jsx("span", { className: "flex h-10 w-10 items-center justify-center rounded-lg bg-white text-lg font-bold text-amber-700 shadow-sm", children: letter }, index))) })] }));
20
- }
21
- // Word with blank display
22
- function WordWithBlank({ word, answer, isReviewMode, correctAnswer, }) {
23
- // Find the position of the underscore
24
- const blankIndex = word.indexOf('_');
25
- const before = word.slice(0, blankIndex);
26
- const after = word.slice(blankIndex + 1);
27
- const isCorrect = isReviewMode && answer.toLowerCase() === correctAnswer?.toLowerCase();
28
- const isWrong = isReviewMode && answer && answer.toLowerCase() !== correctAnswer?.toLowerCase();
29
- const didNotAnswer = isReviewMode && !answer;
30
- const getBlankStyle = () => {
31
- if (!isReviewMode)
32
- return 'border-blue-400 bg-blue-50 text-blue-600';
33
- if (isCorrect)
34
- return 'border-green-500 bg-green-100 text-green-600';
35
- if (isWrong)
36
- return 'border-red-500 bg-red-100 text-red-600';
37
- if (didNotAnswer)
38
- return 'border-blue-500 bg-blue-50 text-blue-600';
39
- return 'border-gray-300 bg-gray-50 text-gray-400';
40
- };
41
- // Show correct answer when user didn't answer or was wrong
42
- const displayValue = didNotAnswer ? correctAnswer : (answer || '_');
43
- return (_jsxs("div", { className: "flex items-center justify-center text-2xl font-bold tracking-wider text-gray-700", children: [_jsx("span", { children: before }), _jsx("span", { className: `mx-0.5 inline-flex h-8 w-8 items-center justify-center rounded border-2 ${getBlankStyle()}`, children: displayValue }), _jsx("span", { children: after })] }));
44
- }
45
- // Single Sub-Question Component
46
- function SubQuestionItem({ subQuestion, answer, onChange, isReviewMode, hintLetters, id, }) {
47
- const isCorrect = isReviewMode &&
48
- answer.toLowerCase() === subQuestion.correctAnswer?.toLowerCase();
49
- const isWrong = isReviewMode &&
50
- answer &&
51
- answer.toLowerCase() !== subQuestion.correctAnswer?.toLowerCase();
52
- const didNotAnswer = isReviewMode && !answer;
53
- return (_jsxs("div", { id: id, className: "rounded-lg border border-gray-200 bg-white p-4 shadow-sm", children: [_jsxs("div", { className: "mb-3 flex items-start gap-3", children: [_jsx("span", { className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-sm font-bold text-white", children: subQuestion.questionNumber }), _jsx(QuestionImage, { imageUrl: subQuestion.imageUrl, alt: `Question ${subQuestion.questionNumber}` })] }), _jsx("div", { className: "mb-3", children: _jsx(WordWithBlank, { word: subQuestion.word, answer: answer, isReviewMode: isReviewMode, correctAnswer: subQuestion.correctAnswer }) }), _jsx("div", { className: "flex flex-wrap justify-center gap-2", children: hintLetters.map((letter, index) => {
54
- const isSelected = answer.toLowerCase() === letter.toLowerCase();
55
- const isCorrectLetter = subQuestion.correctAnswer?.toLowerCase() === letter.toLowerCase();
56
- const userDidNotAnswer = !answer;
57
- const userWasWrong = answer && answer.toLowerCase() !== subQuestion.correctAnswer?.toLowerCase();
58
- let buttonStyle = 'border-gray-300 bg-white hover:border-blue-300';
59
- let textStyle = 'text-gray-700';
60
- if (isReviewMode) {
61
- if (isCorrectLetter) {
62
- // Blue if user didn't answer or was wrong, green if correct
63
- buttonStyle = (userDidNotAnswer || userWasWrong)
64
- ? 'border-blue-500 bg-blue-50'
65
- : 'border-green-500 bg-green-100';
66
- textStyle = 'text-gray-800 font-bold';
67
- }
68
- else if (isSelected && !isCorrectLetter) {
69
- buttonStyle = 'border-red-500 bg-red-100';
70
- textStyle = 'text-gray-800 font-bold';
71
- }
72
- else {
73
- textStyle = 'text-gray-600';
74
- }
75
- }
76
- else if (isSelected) {
77
- buttonStyle = 'border-blue-500 bg-blue-100';
78
- }
79
- return (_jsx("button", { type: "button", onClick: () => !isReviewMode && onChange(letter), disabled: isReviewMode, className: `flex h-10 w-10 items-center justify-center rounded-lg border-2 text-lg transition-colors ${buttonStyle} ${textStyle} ${isReviewMode ? 'cursor-default' : 'cursor-pointer'}`, children: letter }, index));
80
- }) }), isReviewMode && (_jsxs("div", { className: "mt-2 flex items-center justify-center gap-2", children: [isCorrect && (_jsxs("span", { className: "flex items-center gap-1 text-sm font-medium text-green-600", children: [_jsx(Check, { className: "h-4 w-4" }), " Correct"] })), isWrong && (_jsxs("span", { className: "text-sm font-medium text-blue-600", children: ["Correct answer: ", subQuestion.correctAnswer] })), didNotAnswer && (_jsxs("span", { className: "flex items-center gap-1 text-sm font-medium text-blue-600", children: ["Correct answer: ", subQuestion.correctAnswer] }))] }))] }));
81
- }
82
- // New format sub-question item (single image shared, inline display)
83
- function NewFormatSubQuestionItem({ subQuestion, answer, onChange, isReviewMode, hintLetters, id, }) {
84
- const isCorrect = isReviewMode &&
85
- answer.toLowerCase() === subQuestion.correctAnswer?.toLowerCase();
86
- const isWrong = isReviewMode &&
87
- answer &&
88
- answer.toLowerCase() !== subQuestion.correctAnswer?.toLowerCase();
89
- const didNotAnswer = isReviewMode && !answer;
90
- return (_jsxs("div", { id: id, className: "flex flex-col items-center gap-2 rounded-lg border border-gray-200 bg-white p-4", children: [_jsx(WordWithBlank, { word: subQuestion.word, answer: answer, isReviewMode: isReviewMode, correctAnswer: subQuestion.correctAnswer }), _jsx("div", { className: "flex flex-wrap justify-center gap-1", children: hintLetters.map((letter, index) => {
91
- const isSelected = answer.toLowerCase() === letter.toLowerCase();
92
- const isCorrectLetter = subQuestion.correctAnswer?.toLowerCase() === letter.toLowerCase();
93
- const userDidNotAnswer = !answer;
94
- const userWasWrong = answer &&
95
- answer.toLowerCase() !== subQuestion.correctAnswer?.toLowerCase();
96
- let buttonStyle = 'border-gray-300 bg-white hover:border-blue-300';
97
- let textStyle = 'text-gray-700';
98
- if (isReviewMode) {
99
- if (isCorrectLetter) {
100
- buttonStyle =
101
- userDidNotAnswer || userWasWrong
102
- ? 'border-blue-500 bg-blue-50'
103
- : 'border-green-500 bg-green-100';
104
- textStyle = 'text-gray-800 font-bold';
105
- }
106
- else if (isSelected && !isCorrectLetter) {
107
- buttonStyle = 'border-red-500 bg-red-100';
108
- textStyle = 'text-gray-800 font-bold';
109
- }
110
- else {
111
- textStyle = 'text-gray-600';
112
- }
113
- }
114
- else if (isSelected) {
115
- buttonStyle = 'border-blue-500 bg-blue-100';
116
- }
117
- return (_jsx("button", { type: "button", onClick: () => !isReviewMode && onChange(letter), disabled: isReviewMode, className: `flex h-8 w-8 items-center justify-center rounded-lg border-2 text-base transition-colors ${buttonStyle} ${textStyle} ${isReviewMode ? 'cursor-default' : 'cursor-pointer'}`, children: letter }, index));
118
- }) }), isReviewMode && (_jsxs("div", { className: "flex items-center justify-center gap-2", children: [isCorrect && (_jsxs("span", { className: "flex items-center gap-1 text-xs font-medium text-green-600", children: [_jsx(Check, { className: "h-3 w-3" }), " Correct"] })), (isWrong || didNotAnswer) && (_jsxs("span", { className: "text-xs font-medium text-blue-600", children: ["Correct answer: ", subQuestion.correctAnswer] }))] }))] }));
119
- }
120
- export function MoversFillWordHintRenderer({ partNumber, partName, questionCount, instruction, hintLetters, subQuestions, answers, onAnswerChange, isReviewMode = false, }) {
121
- // Detect if this is new format (single shared image, multiple blanks with blankIndex)
122
- const isNewFormat = subQuestions.length > 0 &&
123
- subQuestions[0].blankIndex !== undefined &&
124
- subQuestions.every((sq) => sq.imageUrl === subQuestions[0].imageUrl);
125
- if (isNewFormat) {
126
- // NEW FORMAT: Single card with shared image and multiple blank inputs
127
- return (_jsxs("div", { className: "flex h-full flex-col gap-4 overflow-y-auto p-6", children: [_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx(CambridgeYlePartBanner, { partNumber: partNumber, questionCount: questionCount, partName: partName, isReviewMode: isReviewMode }), _jsx(CambridgeYleInstructionBanner, { instruction: instruction, isReviewMode: isReviewMode }), _jsx(HintLetters, { letters: hintLetters })] }), _jsxs("div", { className: "rounded-lg border border-gray-200 bg-white p-6 shadow-sm", children: [_jsx("div", { className: "mb-6 flex justify-center", children: _jsx(QuestionImage, { imageUrl: subQuestions[0].imageUrl, alt: "Question image" }) }), _jsx("div", { className: "flex flex-wrap justify-center gap-4", children: subQuestions.map((subQuestion) => (_jsx(NewFormatSubQuestionItem, { id: `question-${subQuestion.id}`, subQuestion: subQuestion, answer: answers[subQuestion.id] || '', onChange: (value) => onAnswerChange(subQuestion.id, value), isReviewMode: isReviewMode, hintLetters: hintLetters }, subQuestion.id))) })] })] }));
128
- }
129
- // OLD FORMAT: Grid of individual cards (each with its own image)
130
- return (_jsxs("div", { className: "flex h-full flex-col gap-4 overflow-y-auto p-6", children: [_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx(CambridgeYlePartBanner, { partNumber: partNumber, questionCount: questionCount, partName: partName, isReviewMode: isReviewMode }), _jsx(CambridgeYleInstructionBanner, { instruction: instruction, isReviewMode: isReviewMode }), _jsx(HintLetters, { letters: hintLetters })] }), _jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3", children: subQuestions.map((subQuestion) => (_jsx(SubQuestionItem, { id: `question-${subQuestion.id}`, subQuestion: subQuestion, answer: answers[subQuestion.id] || '', onChange: (value) => onAnswerChange(subQuestion.id, value), isReviewMode: isReviewMode, hintLetters: hintLetters }, subQuestion.id))) })] }));
131
- }
@@ -1,2 +0,0 @@
1
- import type { MoversPictureChooseRendererProps } from '../../types';
2
- export declare function MoversPictureChooseRenderer({ partNumber, partName, questionCount, instruction, subQuestions, answers, onAnswerChange, isReviewMode, }: MoversPictureChooseRendererProps): import("react").JSX.Element;
@@ -1,76 +0,0 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState } from 'react';
4
- import { ZoomIn, Check, X } from 'lucide-react';
5
- import { ResolvedImage } from '../../../../../components/common/ResolvedImage';
6
- import CambridgeYlePartBanner from '../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner';
7
- import CambridgeYleInstructionBanner from '../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner';
8
- // Image with zoom functionality
9
- function QuestionImage({ imageUrl, alt }) {
10
- const [isZoomed, setIsZoomed] = useState(false);
11
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative flex items-center justify-center", children: [_jsx(ResolvedImage, { src: imageUrl, alt: alt, className: "max-h-[180px] cursor-pointer rounded-lg object-contain shadow-md", onClick: () => setIsZoomed(true) }), _jsx("button", { type: "button", onClick: () => setIsZoomed(true), className: "absolute right-2 top-2 rounded-full bg-white/80 p-1.5 shadow-md hover:bg-white", "aria-label": "Zoom image", children: _jsx(ZoomIn, { className: "h-4 w-4 text-gray-600" }) })] }), isZoomed && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/70", onClick: () => setIsZoomed(false), children: _jsx(ResolvedImage, { src: imageUrl, alt: `${alt} (zoomed)`, className: "max-h-[90vh] max-w-[90vw] rounded-lg object-contain", showLoadingState: false }) }))] }));
12
- }
13
- // Single Sub-Question Component
14
- function SubQuestionItem({ subQuestion, selectedOptionId, onSelect, isReviewMode, id, }) {
15
- // Review mode states
16
- const isUserCorrect = isReviewMode && selectedOptionId === subQuestion.correctOptionId;
17
- const getOptionStyle = (optionId) => {
18
- if (!isReviewMode) {
19
- return selectedOptionId === optionId
20
- ? 'border-blue-500 bg-[#f4f9ff]'
21
- : 'border-blue-200 bg-white hover:border-blue-300 hover:bg-blue-50/30';
22
- }
23
- // Review mode
24
- const isSelected = selectedOptionId === optionId;
25
- const isCorrect = subQuestion.correctOptionId === optionId;
26
- if (isCorrect) {
27
- // ✅ Green if user answered correctly
28
- // ⚪ Blue if user didn't answer or was wrong (show correct answer)
29
- return isUserCorrect
30
- ? 'border-green-500 bg-green-50'
31
- : 'border-blue-500 bg-blue-50';
32
- }
33
- if (isSelected && !isCorrect) {
34
- // ❌ Red for wrong answer
35
- return 'border-red-500 bg-red-50';
36
- }
37
- return 'border-blue-200 bg-white';
38
- };
39
- const getRadioStyle = (optionId) => {
40
- if (!isReviewMode) {
41
- return selectedOptionId === optionId
42
- ? 'border-blue-500 bg-blue-500'
43
- : 'border-gray-300 bg-white';
44
- }
45
- // Review mode
46
- const isSelected = selectedOptionId === optionId;
47
- const isCorrect = subQuestion.correctOptionId === optionId;
48
- if (isCorrect) {
49
- return isUserCorrect
50
- ? 'border-green-500 bg-green-500'
51
- : 'border-blue-500 bg-blue-500';
52
- }
53
- if (isSelected && !isCorrect) {
54
- return 'border-red-500 bg-red-500';
55
- }
56
- return 'border-gray-300 bg-white';
57
- };
58
- // Get icon for review mode
59
- const getReviewIcon = (optionId) => {
60
- if (!isReviewMode)
61
- return null;
62
- const isSelected = selectedOptionId === optionId;
63
- const isCorrect = subQuestion.correctOptionId === optionId;
64
- if (isCorrect) {
65
- return _jsx(Check, { className: "h-4 w-4 text-white" });
66
- }
67
- if (isSelected && !isCorrect) {
68
- return _jsx(X, { className: "h-4 w-4 text-white" });
69
- }
70
- return null;
71
- };
72
- return (_jsxs("div", { id: id, className: "rounded-lg border border-gray-200 bg-white p-4 shadow-sm", children: [_jsxs("div", { className: "mb-3 flex items-start gap-3", children: [_jsx("span", { className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-sm font-bold text-white", children: subQuestion.questionNumber }), _jsx(QuestionImage, { imageUrl: subQuestion.imageUrl, alt: `Question ${subQuestion.questionNumber}` })] }), subQuestion.questionText && (_jsx("p", { className: "mb-3 text-[17px] font-bold text-[#001590]", children: subQuestion.questionText })), _jsx("div", { className: "flex flex-col gap-2", children: subQuestion.options.map((option) => (_jsxs("button", { type: "button", onClick: () => !isReviewMode && onSelect(option.id), disabled: isReviewMode, className: `flex items-center gap-3 rounded-lg border-2 px-3 py-2 text-left transition-colors ${getOptionStyle(option.id)} ${isReviewMode ? 'cursor-default' : 'cursor-pointer'}`, children: [_jsxs("span", { className: "text-sm font-medium text-gray-600", children: [option.label, "."] }), _jsx("span", { className: "flex-1 text-sm text-gray-700", children: option.text }), _jsx("div", { className: `flex h-5 w-5 items-center justify-center rounded-full border-2 ${getRadioStyle(option.id)}`, children: isReviewMode ? (getReviewIcon(option.id)) : (selectedOptionId === option.id && (_jsx("div", { className: "h-2 w-2 rounded-full bg-white" }))) })] }, option.id))) })] }));
73
- }
74
- export function MoversPictureChooseRenderer({ partNumber, partName, questionCount, instruction, subQuestions, answers, onAnswerChange, isReviewMode = false, }) {
75
- return (_jsxs("div", { className: "flex h-full flex-col gap-4 overflow-y-auto p-6", children: [_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx(CambridgeYlePartBanner, { partNumber: partNumber, questionCount: questionCount, partName: partName, isReviewMode: isReviewMode }), _jsx(CambridgeYleInstructionBanner, { instruction: instruction, isReviewMode: isReviewMode })] }), _jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: subQuestions.map((subQuestion) => (_jsx(SubQuestionItem, { id: `question-${subQuestion.id}`, subQuestion: subQuestion, selectedOptionId: answers[subQuestion.id], onSelect: (optionId) => onAnswerChange(subQuestion.id, optionId), isReviewMode: isReviewMode }, subQuestion.id))) })] }));
76
- }
@@ -1,2 +0,0 @@
1
- import type { MoversPictureFillBlankChooseRendererProps } from '../../types';
2
- export declare function MoversPictureFillBlankChooseRenderer({ partNumber, partName, questionCount, instruction, subQuestions, answers, onAnswerChange, isReviewMode, }: MoversPictureFillBlankChooseRendererProps): import("react").JSX.Element;
@@ -1,68 +0,0 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState } from 'react';
4
- import { ZoomIn } from 'lucide-react';
5
- import { ResolvedImage } from '../../../../../components/common/ResolvedImage';
6
- import CambridgeYlePartBanner from '../../../../../components/themes/cambridge-yle/CambridgeYlePartBanner';
7
- import CambridgeYleInstructionBanner from '../../../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner';
8
- // Image with zoom functionality
9
- function QuestionImage({ imageUrl, alt }) {
10
- const [isZoomed, setIsZoomed] = useState(false);
11
- return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "relative flex items-center justify-center", children: [_jsx(ResolvedImage, { src: imageUrl, alt: alt, className: "max-h-[150px] cursor-pointer rounded-lg object-contain shadow-md", onClick: () => setIsZoomed(true) }), _jsx("button", { type: "button", onClick: () => setIsZoomed(true), className: "absolute right-1 top-1 rounded-full bg-white/80 p-1 shadow-md hover:bg-white", "aria-label": "Zoom image", children: _jsx(ZoomIn, { className: "h-3 w-3 text-gray-600" }) })] }), isZoomed && (_jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/70", onClick: () => setIsZoomed(false), children: _jsx(ResolvedImage, { src: imageUrl, alt: `${alt} (zoomed)`, className: "max-h-[90vh] max-w-[90vw] rounded-lg object-contain", showLoadingState: false }) }))] }));
12
- }
13
- // Render question text with blank highlight
14
- function QuestionWithBlank({ question, selectedAnswer, isReviewMode, isCorrect, }) {
15
- // Split by {0} placeholder
16
- const parts = question.split('{0}');
17
- const getBlankStyle = () => {
18
- if (!isReviewMode || !selectedAnswer) {
19
- return 'border-blue-400 text-blue-600';
20
- }
21
- return isCorrect
22
- ? 'border-green-500 text-green-600'
23
- : 'border-red-500 text-red-600';
24
- };
25
- return (_jsxs("p", { className: "text-sm font-medium text-gray-700", children: [parts[0], _jsx("span", { className: `mx-1 inline-block min-w-[60px] border-b-2 border-dashed px-2 text-center font-semibold ${getBlankStyle()}`, children: selectedAnswer || '______' }), parts[1]] }));
26
- }
27
- // Single Sub-Question Component
28
- function SubQuestionItem({ subQuestion, selectedOptionId, onSelect, isReviewMode, id, }) {
29
- const selectedOption = subQuestion.options.find((o) => o.id === selectedOptionId);
30
- // Review mode states
31
- const isUserCorrect = isReviewMode && selectedOptionId === subQuestion.correctOptionId;
32
- const getOptionStyle = (optionId) => {
33
- if (!isReviewMode) {
34
- return selectedOptionId === optionId
35
- ? 'border-blue-500 bg-blue-50'
36
- : 'border-gray-300 bg-white hover:border-blue-300';
37
- }
38
- // Review mode
39
- const isSelected = selectedOptionId === optionId;
40
- const isCorrect = subQuestion.correctOptionId === optionId;
41
- if (isCorrect) {
42
- // ✅ Green if user answered correctly
43
- // ⚪ Blue if user didn't answer or was wrong (show correct answer)
44
- return isUserCorrect
45
- ? 'border-green-500 bg-green-50'
46
- : 'border-blue-500 bg-blue-50';
47
- }
48
- if (isSelected && !isCorrect) {
49
- // ❌ Red for wrong answer
50
- return 'border-red-500 bg-red-50';
51
- }
52
- return 'border-gray-300 bg-white';
53
- };
54
- const getTextStyle = (optionId) => {
55
- if (!isReviewMode)
56
- return 'text-gray-700';
57
- const isSelected = selectedOptionId === optionId;
58
- const isCorrect = subQuestion.correctOptionId === optionId;
59
- if (isCorrect || isSelected) {
60
- return 'text-gray-800 font-bold';
61
- }
62
- return 'text-gray-600';
63
- };
64
- return (_jsxs("div", { id: id, className: "rounded-lg border border-gray-200 bg-white p-4 shadow-sm", children: [_jsxs("div", { className: "mb-3 flex items-start gap-3", children: [_jsx("span", { className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-sm font-bold text-white", children: subQuestion.questionNumber }), _jsx(QuestionImage, { imageUrl: subQuestion.imageUrl, alt: `Question ${subQuestion.questionNumber}` })] }), _jsx("div", { className: "mb-3", children: _jsx(QuestionWithBlank, { question: subQuestion.question, selectedAnswer: selectedOption?.text, isReviewMode: isReviewMode, isCorrect: selectedOptionId === subQuestion.correctOptionId }) }), _jsx("div", { className: "flex flex-wrap gap-2", children: subQuestion.options.map((option) => (_jsxs("button", { type: "button", onClick: () => !isReviewMode && onSelect(option.id), disabled: isReviewMode, className: `rounded-lg border-2 px-3 py-1.5 text-sm transition-colors ${getOptionStyle(option.id)} ${getTextStyle(option.id)} ${isReviewMode ? 'cursor-default' : 'cursor-pointer'}`, children: [option.label, ". ", option.text] }, option.id))) })] }));
65
- }
66
- export function MoversPictureFillBlankChooseRenderer({ partNumber, partName, questionCount, instruction, subQuestions, answers, onAnswerChange, isReviewMode = false, }) {
67
- return (_jsxs("div", { className: "flex h-full flex-col gap-4 overflow-y-auto p-6", children: [_jsxs("div", { className: "flex flex-col gap-4", children: [_jsx(CambridgeYlePartBanner, { partNumber: partNumber, questionCount: questionCount, partName: partName, isReviewMode: isReviewMode }), _jsx(CambridgeYleInstructionBanner, { instruction: instruction, isReviewMode: isReviewMode })] }), _jsx("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: subQuestions.map((subQuestion) => (_jsx(SubQuestionItem, { id: `question-${subQuestion.id}`, subQuestion: subQuestion, selectedOptionId: answers[subQuestion.id], onSelect: (optionId) => onAnswerChange(subQuestion.id, optionId), isReviewMode: isReviewMode }, subQuestion.id))) })] }));
68
- }
@@ -1,16 +0,0 @@
1
- import type { QuestionPart } from '../../../utils/question-transformers';
2
- interface SpeakingGNInterviewRendererProps {
3
- part: QuestionPart;
4
- answers: Record<string, unknown>;
5
- onAnswerChange: (questionId: string, value: unknown) => void;
6
- isReviewMode?: boolean;
7
- onExit: () => void;
8
- onPartComplete?: () => void;
9
- onPartBack?: () => void;
10
- speakingTheme?: string;
11
- teacherVideoUrl?: string;
12
- onTeacherIntroPlayed?: () => void;
13
- skipTeacherVideo?: boolean;
14
- }
15
- export declare function SpeakingGNInterviewRenderer(props: SpeakingGNInterviewRendererProps): import("react").JSX.Element;
16
- export {};