@tinyweb_dev/oe-exam-sdk 0.1.1 → 0.1.3

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 (514) hide show
  1. package/dist/api/exam-create.js +1 -179
  2. package/dist/api/exam-create.types.js +1 -1
  3. package/dist/api/exam-questions.d.ts +1 -1
  4. package/dist/api/exam-questions.js +1 -98
  5. package/dist/api/exam-questions.types.d.ts +45 -1
  6. package/dist/api/exam-questions.types.js +1 -1
  7. package/dist/api/exam-taking.js +1 -73
  8. package/dist/api/exam-taking.types.js +1 -1
  9. package/dist/api/index.js +1 -6
  10. package/dist/api/result-review.js +1 -50
  11. package/dist/api/result-review.types.js +1 -1
  12. package/dist/components/CueCardEditor.js +1 -58
  13. package/dist/components/common/ResolvedImage.js +1 -15
  14. package/dist/components/exams/CompletionDialog.d.ts +8 -0
  15. package/dist/components/exams/CompletionDialog.js +1 -0
  16. package/dist/components/exams/CreateExamInfoForm.d.ts +4 -1
  17. package/dist/components/exams/CreateExamInfoForm.js +1 -35
  18. package/dist/components/exams/CreateExamPageContainer.d.ts +3 -0
  19. package/dist/components/exams/CreateExamPageContainer.js +1 -103
  20. package/dist/components/exams/ExamCreator.d.ts +9 -3
  21. package/dist/components/exams/ExamCreator.js +1 -89
  22. package/dist/components/exams/ExamPartTabs.d.ts +1 -1
  23. package/dist/components/exams/ExamPartTabs.js +1 -40
  24. package/dist/components/exams/ExamPreviewDialog.d.ts +20 -0
  25. package/dist/components/exams/ExamPreviewDialog.js +1 -0
  26. package/dist/components/exams/ExamQuestionGrid.d.ts +1 -1
  27. package/dist/components/exams/ExamQuestionGrid.js +1 -18
  28. package/dist/components/exams/ExamQuestionsPageContainer.d.ts +3 -0
  29. package/dist/components/exams/ExamQuestionsPageContainer.js +1 -90
  30. package/dist/components/exams/ExamViewDialog.d.ts +24 -0
  31. package/dist/components/exams/ExamViewDialog.js +1 -0
  32. package/dist/components/exams/PartAudioUploader.js +1 -9
  33. package/dist/components/exams/TemplateSelector.js +1 -66
  34. package/dist/components/exams/UnsavedChangesDialog.d.ts +8 -0
  35. package/dist/components/exams/UnsavedChangesDialog.js +1 -0
  36. package/dist/components/exams/api.d.ts +1 -1
  37. package/dist/components/exams/api.js +1 -3
  38. package/dist/components/exams/create-exam.texts.d.ts +58 -0
  39. package/dist/components/exams/create-exam.texts.js +1 -0
  40. package/dist/components/exams/create-exam.validation.js +1 -18
  41. package/dist/components/exams/exam-question-status.d.ts +9 -0
  42. package/dist/components/exams/exam-question-status.js +1 -0
  43. package/dist/components/exams/exam-template.utils.js +1 -18
  44. package/dist/components/exams/index.d.ts +11 -1
  45. package/dist/components/exams/index.js +1 -10
  46. package/dist/components/exams/take/ExamTakingPageContainer.js +1 -153
  47. package/dist/components/exams/take/GenericQuestionRenderer.js +1 -14
  48. package/dist/components/exams/take/exam-taking.utils.js +1 -88
  49. package/dist/components/exams/take/hooks/useSpeakingRecorder.js +1 -24
  50. package/dist/components/exams/take/index.js +1 -2
  51. package/dist/components/exams/take/types.js +1 -1
  52. package/dist/components/index.js +1 -42
  53. package/dist/components/questions/_shared/config/question-types.config.js +1 -333
  54. package/dist/components/questions/_shared/constants/color-palette.js +1 -21
  55. package/dist/components/questions/_shared/hooks/useAnswerGroupQuestionGroup.js +1 -105
  56. package/dist/components/questions/_shared/hooks/useBasicQuestionGroup.js +1 -222
  57. package/dist/components/questions/_shared/hooks/useCompareImagesGroup.js +1 -140
  58. package/dist/components/questions/_shared/hooks/useImageQuestionGroup.js +1 -123
  59. package/dist/components/questions/_shared/hooks/useInfoExchangeGroup.js +1 -50
  60. package/dist/components/questions/_shared/hooks/useQuestionFormState.js +1 -54
  61. package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +1 -123
  62. package/dist/components/questions/_shared/hooks/useQuestionListGroup.js +1 -121
  63. package/dist/components/questions/_shared/hooks/useQuestionOnChange.js +1 -70
  64. package/dist/components/questions/_shared/hooks/useSceneQuestionGroup.js +1 -194
  65. package/dist/components/questions/_shared/index.js +1 -7
  66. package/dist/components/questions/_shared/types/answer-the-question.type.js +1 -1
  67. package/dist/components/questions/_shared/types/arrange-letters-into-words.type.js +1 -1
  68. package/dist/components/questions/_shared/types/choose-correct-adjective.type.js +1 -3
  69. package/dist/components/questions/_shared/types/color-region.config.js +1 -1
  70. package/dist/components/questions/_shared/types/fill-in-blank.type.js +1 -1
  71. package/dist/components/questions/_shared/types/fill-missing-words-in-grid.type.js +1 -1
  72. package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +1 -1
  73. package/dist/components/questions/_shared/types/index.js +1 -12
  74. package/dist/components/questions/_shared/types/label-the-picture.type.js +1 -1
  75. package/dist/components/questions/_shared/types/listen-and-choose-from-answer-group.type.js +1 -1
  76. package/dist/components/questions/_shared/types/listen-and-choose-objects-in-scene.type.js +1 -1
  77. package/dist/components/questions/_shared/types/listen-and-drag-objects-into-scene.type.js +1 -1
  78. package/dist/components/questions/_shared/types/listen-and-speak-answer.type.js +1 -1
  79. package/dist/components/questions/_shared/types/listen-and-speak-compare-images.type.js +1 -1
  80. package/dist/components/questions/_shared/types/listen-and-speak-image-group.type.js +1 -1
  81. package/dist/components/questions/_shared/types/listen-and-speak-info-exchange.type.js +1 -1
  82. package/dist/components/questions/_shared/types/listen-and-speak-odd-one-out.type.js +1 -3
  83. package/dist/components/questions/_shared/types/listen-and-speak-question-list.type.js +1 -1
  84. package/dist/components/questions/_shared/types/listen-and-speak-with-story-images.type.js +1 -3
  85. package/dist/components/questions/_shared/types/listen-and-tick-answer.type.js +1 -1
  86. package/dist/components/questions/_shared/types/listening-drawline.type.js +1 -1
  87. package/dist/components/questions/_shared/types/look-and-choose-answer.type.js +1 -1
  88. package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +1 -1
  89. package/dist/components/questions/_shared/types/match-by-writing-answer.type.js +1 -1
  90. package/dist/components/questions/_shared/types/multiple-choice.type.js +1 -1
  91. package/dist/components/questions/_shared/types/question-group.type.js +1 -11
  92. package/dist/components/questions/_shared/types/read-and-color-objects.type.js +1 -1
  93. package/dist/components/questions/_shared/types/read-displayed-content.type.js +1 -1
  94. package/dist/components/questions/_shared/types/read-passage-and-answer-questions.type.js +1 -1
  95. package/dist/components/questions/_shared/types/speaking-describe-image.type.js +1 -1
  96. package/dist/components/questions/_shared/types/spontaneous-qa.type.js +1 -1
  97. package/dist/components/questions/_shared/types/word-fill-paragraph-with-options.type.js +1 -1
  98. package/dist/components/questions/_shared/types/word-fill-paragraph.type.js +1 -1
  99. package/dist/components/questions/_shared/types/word-fill-structured-form.type.js +1 -1
  100. package/dist/components/questions/_shared/types/word-ordering.type.js +1 -1
  101. package/dist/components/questions/_shared/types/write-a-short-letter.type.js +1 -1
  102. package/dist/components/questions/_shared/types/write-correct-verb-form.type.js +1 -3
  103. package/dist/components/questions/_shared/types/write-sentences.type.js +1 -1
  104. package/dist/components/questions/_shared/types/write-short-paragraph.type.js +1 -1
  105. package/dist/components/questions/_shared/utils/answer-builder.js +1 -79
  106. package/dist/components/questions/_shared/utils/createColorRegionRegistration.js +1 -39
  107. package/dist/components/questions/_shared/utils/id-generator.js +1 -3
  108. package/dist/components/questions/_shared/utils/question-validation.js +1 -21
  109. package/dist/components/questions/components/ColorNode.js +1 -85
  110. package/dist/components/questions/components/ColorRegionClient.js +1 -545
  111. package/dist/components/questions/components/ColorRegionCreator.js +1 -420
  112. package/dist/components/questions/components/ColorRegionNode.js +1 -59
  113. package/dist/components/questions/components/CreatorGuide.js +1 -103
  114. package/dist/components/questions/components/NameNode.js +1 -26
  115. package/dist/components/questions/components/QuestionBankOption.js +1 -46
  116. package/dist/components/questions/components/QuestionBasicInfoForm.js +1 -9
  117. package/dist/components/questions/components/QuestionCommonFields.js +1 -33
  118. package/dist/components/questions/components/QuestionSearchDropdown.js +1 -258
  119. package/dist/components/questions/components/QuestionTypeSelector.js +1 -32
  120. package/dist/components/questions/components/QuestionTypeSpecificForms.js +1 -6
  121. package/dist/components/questions/components/RegionNode.js +1 -94
  122. package/dist/components/questions/components/index.js +1 -14
  123. package/dist/components/questions/creator/QuestionCreator.js +1 -90
  124. package/dist/components/questions/creator/QuestionGroupCreator.js +1 -119
  125. package/dist/components/questions/creator/dedicated-component-router.js +1 -79
  126. package/dist/components/questions/creator/index.js +1 -3
  127. package/dist/components/questions/creator/question-type-registry.js +1 -80
  128. package/dist/components/questions/groups/ArticlesGroupCard.js +1 -121
  129. package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -50
  130. package/dist/components/questions/groups/DocumentGroupCard.js +1 -114
  131. package/dist/components/questions/groups/MatchAnswerGroupCard.js +1 -25
  132. package/dist/components/questions/index.js +1 -10
  133. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +1 -37
  134. package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +1 -351
  135. package/dist/components/questions/types/answer-the-question/register.js +1 -67
  136. package/dist/components/questions/types/answer-the-question/transform.js +1 -36
  137. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +1 -141
  138. package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +1 -118
  139. package/dist/components/questions/types/arrange-letters-into-words/index.js +1 -2
  140. package/dist/components/questions/types/arrange-letters-into-words/register.js +1 -4
  141. package/dist/components/questions/types/arrange-letters-into-words/transform.js +1 -24
  142. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -64
  143. package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +1 -169
  144. package/dist/components/questions/types/choose-correct-adjective/index.js +1 -2
  145. package/dist/components/questions/types/choose-correct-adjective/register.js +1 -55
  146. package/dist/components/questions/types/choose-correct-adjective/transform.js +1 -16
  147. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +1 -131
  148. package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +1 -581
  149. package/dist/components/questions/types/choose-the-correct-answer/index.js +1 -2
  150. package/dist/components/questions/types/choose-the-correct-answer/register.js +1 -101
  151. package/dist/components/questions/types/choose-the-correct-answer/transform.js +1 -43
  152. package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +1 -275
  153. package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +1 -448
  154. package/dist/components/questions/types/fill-in-blank/index.js +1 -2
  155. package/dist/components/questions/types/fill-in-blank/register.js +1 -138
  156. package/dist/components/questions/types/fill-in-blank/transform.js +1 -59
  157. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +1 -117
  158. package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +1 -671
  159. package/dist/components/questions/types/fill-missing-words-in-grid/index.js +1 -2
  160. package/dist/components/questions/types/fill-missing-words-in-grid/register.js +1 -37
  161. package/dist/components/questions/types/fill-missing-words-in-grid/transform.js +1 -15
  162. package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +1 -262
  163. package/dist/components/questions/types/gn-speaking-interview/register.js +1 -121
  164. package/dist/components/questions/types/gn-speaking-interview/transform.js +1 -48
  165. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingClient.js +1 -16
  166. package/dist/components/questions/types/image-object-matching/ImageObjectMatchingCreator.js +1 -27
  167. package/dist/components/questions/types/image-object-matching/index.js +1 -2
  168. package/dist/components/questions/types/image-object-matching/register.js +1 -3
  169. package/dist/components/questions/types/image-object-matching/transform.js +1 -2
  170. package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +1 -102
  171. package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +1 -344
  172. package/dist/components/questions/types/label-the-picture/index.js +1 -2
  173. package/dist/components/questions/types/label-the-picture/register.js +1 -54
  174. package/dist/components/questions/types/label-the-picture/transform.js +1 -28
  175. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +1 -78
  176. package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +1 -232
  177. package/dist/components/questions/types/listen-and-choose-from-answer-group/index.js +1 -2
  178. package/dist/components/questions/types/listen-and-choose-from-answer-group/register.js +1 -82
  179. package/dist/components/questions/types/listen-and-choose-from-answer-group/transform.js +1 -27
  180. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +1 -70
  181. package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +1 -386
  182. package/dist/components/questions/types/listen-and-choose-objects-in-scene/index.js +1 -2
  183. package/dist/components/questions/types/listen-and-choose-objects-in-scene/register.js +1 -100
  184. package/dist/components/questions/types/listen-and-choose-objects-in-scene/transform.js +1 -33
  185. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +1 -106
  186. package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +1 -457
  187. package/dist/components/questions/types/listen-and-drag-objects-into-scene/index.js +1 -2
  188. package/dist/components/questions/types/listen-and-drag-objects-into-scene/register.js +1 -110
  189. package/dist/components/questions/types/listen-and-drag-objects-into-scene/transform.js +1 -35
  190. package/dist/components/questions/types/listen-and-speak-answer/ArrowIndicator.js +1 -8
  191. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -22
  192. package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +1 -291
  193. package/dist/components/questions/types/listen-and-speak-answer/index.js +1 -2
  194. package/dist/components/questions/types/listen-and-speak-answer/register.js +1 -84
  195. package/dist/components/questions/types/listen-and-speak-answer/transform.js +1 -27
  196. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -32
  197. package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +1 -295
  198. package/dist/components/questions/types/listen-and-speak-compare-images/register.js +1 -79
  199. package/dist/components/questions/types/listen-and-speak-compare-images/transform.js +1 -27
  200. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -34
  201. package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +1 -285
  202. package/dist/components/questions/types/listen-and-speak-image-group/register.js +1 -81
  203. package/dist/components/questions/types/listen-and-speak-image-group/transform.js +1 -26
  204. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -38
  205. package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +1 -385
  206. package/dist/components/questions/types/listen-and-speak-info-exchange/register.js +1 -87
  207. package/dist/components/questions/types/listen-and-speak-info-exchange/transform.js +1 -32
  208. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -26
  209. package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +1 -247
  210. package/dist/components/questions/types/listen-and-speak-odd-one-out/register.js +1 -59
  211. package/dist/components/questions/types/listen-and-speak-odd-one-out/transform.js +1 -18
  212. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -26
  213. package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +1 -256
  214. package/dist/components/questions/types/listen-and-speak-question-list/register.js +1 -81
  215. package/dist/components/questions/types/listen-and-speak-question-list/transform.js +1 -26
  216. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -26
  217. package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +1 -230
  218. package/dist/components/questions/types/listen-and-speak-with-story-images/register.js +1 -56
  219. package/dist/components/questions/types/listen-and-speak-with-story-images/transform.js +1 -16
  220. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +1 -72
  221. package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +1 -230
  222. package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +1 -1
  223. package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +1 -66
  224. package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +1 -42
  225. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +1 -96
  226. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +1 -238
  227. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +1 -2
  228. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +1 -76
  229. package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +1 -19
  230. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +1 -134
  231. package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +1 -617
  232. package/dist/components/questions/types/look-picture-fill-word-hint/index.js +1 -2
  233. package/dist/components/questions/types/look-picture-fill-word-hint/register.js +1 -82
  234. package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +1 -29
  235. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +1 -38
  236. package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +1 -359
  237. package/dist/components/questions/types/match-by-writing-answer/index.js +1 -3
  238. package/dist/components/questions/types/match-by-writing-answer/register.js +1 -58
  239. package/dist/components/questions/types/match-by-writing-answer/transform.js +1 -61
  240. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsClient.js +1 -16
  241. package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsCreator.js +1 -23
  242. package/dist/components/questions/types/read-and-color-objects/index.js +1 -2
  243. package/dist/components/questions/types/read-and-color-objects/register.js +1 -3
  244. package/dist/components/questions/types/read-and-color-objects/transform.js +1 -36
  245. package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +1 -125
  246. package/dist/components/questions/types/read-displayed-content/register.js +1 -64
  247. package/dist/components/questions/types/read-displayed-content/transform.js +1 -12
  248. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -64
  249. package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +1 -511
  250. package/dist/components/questions/types/read-passage-and-answer-questions/index.js +1 -2
  251. package/dist/components/questions/types/read-passage-and-answer-questions/register.js +1 -58
  252. package/dist/components/questions/types/read-passage-and-answer-questions/transform.js +1 -28
  253. package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +1 -373
  254. package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +1 -41
  255. package/dist/components/questions/types/speaking-conversation/register.js +1 -110
  256. package/dist/components/questions/types/speaking-conversation/transform.js +1 -34
  257. package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +1 -74
  258. package/dist/components/questions/types/speaking-cue-card/register.js +1 -52
  259. package/dist/components/questions/types/speaking-cue-card/transform.js +1 -12
  260. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -10
  261. package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +1 -161
  262. package/dist/components/questions/types/speaking-describe-image/index.js +1 -2
  263. package/dist/components/questions/types/speaking-describe-image/register.js +1 -69
  264. package/dist/components/questions/types/speaking-describe-image/transform.js +1 -16
  265. package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +1 -234
  266. package/dist/components/questions/types/spontaneous-qa/register.js +1 -93
  267. package/dist/components/questions/types/spontaneous-qa/transform.js +1 -34
  268. package/dist/components/questions/types/true-false/TrueFalseClient.js +1 -75
  269. package/dist/components/questions/types/true-false/TrueFalseCreator.js +1 -244
  270. package/dist/components/questions/types/true-false/index.js +1 -2
  271. package/dist/components/questions/types/true-false/register.js +1 -77
  272. package/dist/components/questions/types/true-false/transform.js +1 -32
  273. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +1 -233
  274. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +1 -631
  275. package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphWrapper.js +1 -28
  276. package/dist/components/questions/types/word-fill-paragraph/index.js +1 -2
  277. package/dist/components/questions/types/word-fill-paragraph/register.js +1 -136
  278. package/dist/components/questions/types/word-fill-paragraph/transform.js +1 -56
  279. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -138
  280. package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +1 -261
  281. package/dist/components/questions/types/word-fill-structured-form/index.js +1 -2
  282. package/dist/components/questions/types/word-fill-structured-form/register.js +1 -67
  283. package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -40
  284. package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -84
  285. package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +1 -192
  286. package/dist/components/questions/types/word-ordering/index.js +1 -2
  287. package/dist/components/questions/types/word-ordering/register.js +1 -46
  288. package/dist/components/questions/types/word-ordering/transform.js +1 -24
  289. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +1 -98
  290. package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +1 -320
  291. package/dist/components/questions/types/write-a-short-letter/index.js +1 -2
  292. package/dist/components/questions/types/write-a-short-letter/register.js +1 -68
  293. package/dist/components/questions/types/write-a-short-letter/transform.js +1 -25
  294. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +1 -61
  295. package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +1 -196
  296. package/dist/components/questions/types/write-correct-verb-form/index.js +1 -2
  297. package/dist/components/questions/types/write-correct-verb-form/register.js +1 -62
  298. package/dist/components/questions/types/write-correct-verb-form/transform.js +1 -17
  299. package/dist/components/questions/types/write-sentences/WriteSentencesClient.js +1 -22
  300. package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +1 -174
  301. package/dist/components/questions/types/write-sentences/index.js +1 -2
  302. package/dist/components/questions/types/write-sentences/register.js +1 -58
  303. package/dist/components/questions/types/write-sentences/transform.js +1 -28
  304. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -96
  305. package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +1 -161
  306. package/dist/components/questions/types/write-short-paragraph/index.js +1 -2
  307. package/dist/components/questions/types/write-short-paragraph/register.js +1 -4
  308. package/dist/components/questions/types/write-short-paragraph/transform.js +1 -21
  309. package/dist/components/questions/viewer/QuestionGroupViewer.js +1 -36
  310. package/dist/components/questions/viewer/QuestionViewer.js +1 -446
  311. package/dist/components/questions/viewer/index.js +1 -2
  312. package/dist/components/results/ResultReviewPageContainer.js +1 -98
  313. package/dist/components/results/ReviewQuestionRenderer.js +1 -72
  314. package/dist/components/results/index.js +1 -2
  315. package/dist/components/results/result-review.utils.js +1 -6
  316. package/dist/components/results/review-data.js +1 -150
  317. package/dist/components/results/review-renderer.utils.js +1 -97
  318. package/dist/components/results/review-types.js +1 -1
  319. package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +1 -138
  320. package/dist/components/themes/cambridge-yle/CambridgeYleBottomNav.js +1 -12
  321. package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.js +1 -18
  322. package/dist/components/themes/cambridge-yle/CambridgeYleExampleText.js +1 -11
  323. package/dist/components/themes/cambridge-yle/CambridgeYleHeader.js +1 -11
  324. package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.js +1 -9
  325. package/dist/components/themes/cambridge-yle/CambridgeYleNavButton.js +1 -13
  326. package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.js +1 -8
  327. package/dist/components/themes/cambridge-yle/CambridgeYleProgressBar.js +1 -6
  328. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCard.js +1 -5
  329. package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCardFullWidth.js +1 -9
  330. package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.js +1 -41
  331. package/dist/components/themes/cambridge-yle/CambridgeYleTimer.js +1 -25
  332. package/dist/components/themes/cambridge-yle/index.js +1 -13
  333. package/dist/components/themes/english-certification/EcAnswerTheQuestion.js +1 -24
  334. package/dist/components/themes/english-certification/EcMatchByWritingAnswer.js +1 -88
  335. package/dist/components/themes/english-certification/EcWordFillParagraph.js +1 -55
  336. package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -156
  337. package/dist/components/themes/english-certification/EcWritingTask.js +1 -34
  338. package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +1 -114
  339. package/dist/components/themes/english-certification/EnglishCertificationBrand.js +1 -11
  340. package/dist/components/themes/english-certification/EnglishCertificationExamLayout.js +1 -21
  341. package/dist/components/themes/english-certification/EnglishCertificationFlagButton.js +1 -15
  342. package/dist/components/themes/english-certification/EnglishCertificationFooterBar.js +1 -19
  343. package/dist/components/themes/english-certification/EnglishCertificationGroupedQuestion.js +1 -148
  344. package/dist/components/themes/english-certification/EnglishCertificationHeader.js +1 -31
  345. package/dist/components/themes/english-certification/EnglishCertificationNavigationPanel.js +1 -71
  346. package/dist/components/themes/english-certification/EnglishCertificationOptionSelector.js +1 -33
  347. package/dist/components/themes/english-certification/EnglishCertificationPassagePanel.js +1 -16
  348. package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.js +1 -22
  349. package/dist/components/themes/english-certification/EnglishCertificationQuestionItem.js +1 -37
  350. package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +1 -72
  351. package/dist/components/themes/english-certification/EnglishCertificationReadingSection.js +1 -90
  352. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestion.js +1 -18
  353. package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js +1 -29
  354. package/dist/components/themes/english-certification/english-certification-theme.js +1 -58
  355. package/dist/components/themes/english-certification/grouped-question-utils.js +1 -218
  356. package/dist/components/themes/english-certification/index.js +1 -21
  357. package/dist/components/themes/index.js +1 -6
  358. package/dist/components/themes/summer-sky/SummerSkyAudioPlayer.js +1 -170
  359. package/dist/components/themes/summer-sky/SummerSkyLayout.js +1 -221
  360. package/dist/components/themes/summer-sky/SummerSkySubmitModal.js +1 -18
  361. package/dist/components/themes/summer-sky/SummerSkyWaitingRoom.js +1 -194
  362. package/dist/components/themes/types.js +1 -1
  363. package/dist/components/ui/alert-dialog.js +1 -26
  364. package/dist/components/ui/alert.js +1 -22
  365. package/dist/components/ui/avatar.js +1 -12
  366. package/dist/components/ui/badge.js +1 -38
  367. package/dist/components/ui/button.js +1 -35
  368. package/dist/components/ui/calendar.js +1 -76
  369. package/dist/components/ui/card.js +1 -16
  370. package/dist/components/ui/carousel.js +1 -91
  371. package/dist/components/ui/checkbox.js +1 -9
  372. package/dist/components/ui/collapsible.js +1 -13
  373. package/dist/components/ui/command.js +1 -34
  374. package/dist/components/ui/confirm-dialog.js +1 -49
  375. package/dist/components/ui/dialog.js +1 -23
  376. package/dist/components/ui/drawer.js +1 -35
  377. package/dist/components/ui/dropdown-menu.js +1 -33
  378. package/dist/components/ui/file-upload.js +1 -155
  379. package/dist/components/ui/form.js +1 -62
  380. package/dist/components/ui/hover-card.js +1 -10
  381. package/dist/components/ui/image-preview.js +1 -29
  382. package/dist/components/ui/input.js +1 -8
  383. package/dist/components/ui/label.js +1 -9
  384. package/dist/components/ui/language-switcher.js +1 -15
  385. package/dist/components/ui/multi-select.js +1 -90
  386. package/dist/components/ui/points-input.js +1 -33
  387. package/dist/components/ui/popover.js +1 -26
  388. package/dist/components/ui/progress.js +1 -16
  389. package/dist/components/ui/radio-group.js +1 -12
  390. package/dist/components/ui/scroll-area.js +1 -11
  391. package/dist/components/ui/select.js +1 -38
  392. package/dist/components/ui/separator.js +1 -8
  393. package/dist/components/ui/sheet.js +1 -40
  394. package/dist/components/ui/single-select.js +1 -76
  395. package/dist/components/ui/skeleton.js +1 -6
  396. package/dist/components/ui/switch.js +1 -8
  397. package/dist/components/ui/table.js +1 -20
  398. package/dist/components/ui/tabs.js +1 -17
  399. package/dist/components/ui/textarea.js +1 -8
  400. package/dist/components/ui/tooltip.js +1 -11
  401. package/dist/index.d.ts +3 -3
  402. package/dist/index.js +1 -19
  403. package/dist/mcp/config.js +1 -32
  404. package/dist/mcp/http-client.js +1 -48
  405. package/dist/mcp/index.js +1 -29
  406. package/dist/mcp/tools/_helpers.js +1 -25
  407. package/dist/mcp/tools/exams.js +1 -439
  408. package/dist/mcp/tools/index.js +1 -6
  409. package/dist/mcp/tools/results.js +1 -19
  410. package/dist/shared/constants/ApiConstant.js +1 -276
  411. package/dist/shared/constants/QueryKeyConstant.js +1 -112
  412. package/dist/shared/constants/exam-level.enum.js +1 -41
  413. package/dist/shared/constants/landings/placement-test.constants.js +1 -431
  414. package/dist/shared/constants/question-skills.js +1 -267
  415. package/dist/shared/constants/routes.js +1 -81
  416. package/dist/shared/index.d.ts +3 -0
  417. package/dist/shared/index.js +1 -7
  418. package/dist/shared/lib/hooks/index.js +1 -10
  419. package/dist/shared/lib/hooks/useAlertDialog.js +1 -57
  420. package/dist/shared/lib/hooks/useAudioPlayCounter.js +1 -30
  421. package/dist/shared/lib/hooks/useAudioPreview.js +1 -81
  422. package/dist/shared/lib/hooks/useAudioUpload.js +1 -24
  423. package/dist/shared/lib/hooks/useConfirmDialog.js +1 -89
  424. package/dist/shared/lib/hooks/useDebouncedCallback.js +1 -32
  425. package/dist/shared/lib/hooks/useDelayedAutoPlay.js +1 -12
  426. package/dist/shared/lib/hooks/useFileUpload.js +1 -24
  427. package/dist/shared/lib/hooks/useImageUrl.js +1 -5
  428. package/dist/shared/lib/hooks/usePresignedFileUrl.js +1 -10
  429. package/dist/shared/lib/hooks/useReactHookForm.js +1 -31
  430. package/dist/shared/lib/hooks/useToast.js +1 -62
  431. package/dist/shared/lib/i18n/index.js +1 -54
  432. package/dist/shared/lib/i18n/messages/en/admin.js +1 -2104
  433. package/dist/shared/lib/i18n/messages/en/auth.js +1 -86
  434. package/dist/shared/lib/i18n/messages/en/common.js +1 -140
  435. package/dist/shared/lib/i18n/messages/en/file-upload.js +1 -18
  436. package/dist/shared/lib/i18n/messages/en/student.js +1 -133
  437. package/dist/shared/lib/i18n/messages/en/terms.js +1 -37
  438. package/dist/shared/lib/i18n/messages/en.js +1 -15
  439. package/dist/shared/lib/i18n/messages/vi/admin.js +1 -2104
  440. package/dist/shared/lib/i18n/messages/vi/auth.js +1 -86
  441. package/dist/shared/lib/i18n/messages/vi/common.js +1 -140
  442. package/dist/shared/lib/i18n/messages/vi/file-upload.js +1 -18
  443. package/dist/shared/lib/i18n/messages/vi/student.js +1 -133
  444. package/dist/shared/lib/i18n/messages/vi/terms.js +1 -37
  445. package/dist/shared/lib/i18n/messages/vi.js +1 -15
  446. package/dist/shared/lib/i18n/types.js +1 -1
  447. package/dist/shared/lib/stores/examQuestionStore.js +1 -212
  448. package/dist/shared/lib/stores/localeStore.js +1 -10
  449. package/dist/shared/lib/utils/public-page.js +1 -44
  450. package/dist/shared/lib/utils/question-reverse-transform.d.ts +6 -0
  451. package/dist/shared/lib/utils/question-reverse-transform.js +1 -0
  452. package/dist/shared/lib/utils/question-transform-types.js +1 -1
  453. package/dist/shared/lib/utils/question-transform-validation.d.ts +6 -0
  454. package/dist/shared/lib/utils/question-transform-validation.js +1 -0
  455. package/dist/shared/lib/utils/question-transform.d.ts +6 -2
  456. package/dist/shared/lib/utils/question-transform.js +1 -174
  457. package/dist/shared/lib/utils.js +1 -168
  458. package/dist/shared/lib/validation/choose-correct-answer.validation.js +1 -111
  459. package/dist/shared/lib/validation/label-the-picture.validation.js +1 -19
  460. package/dist/shared/lib/validation/listen-and-choose-from-answer-group.validation.js +1 -46
  461. package/dist/shared/lib/validation/listen-and-choose-objects-in-scene.validation.js +1 -21
  462. package/dist/shared/lib/validation/listen-and-drag-objects-into-scene.validation.js +1 -34
  463. package/dist/shared/lib/validation/listen-and-speak-answer.validation.js +1 -40
  464. package/dist/shared/lib/validation/listen-and-speak-compare-images.validation.js +1 -45
  465. package/dist/shared/lib/validation/listen-and-speak-image-group.validation.js +1 -19
  466. package/dist/shared/lib/validation/listen-and-speak-info-exchange.validation.js +1 -39
  467. package/dist/shared/lib/validation/listen-and-speak-odd-one-out.validation.js +1 -32
  468. package/dist/shared/lib/validation/listen-and-speak-question-list.validation.js +1 -19
  469. package/dist/shared/lib/validation/listen-and-speak-with-story-images.validation.js +1 -28
  470. package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +1 -12
  471. package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +1 -47
  472. package/dist/shared/lib/validation/read-and-color-objects.validation.js +1 -29
  473. package/dist/shared/lib/validation/read-displayed-content.validation.js +1 -28
  474. package/dist/shared/lib/validation/speaking-conversation.validation.js +1 -75
  475. package/dist/shared/lib/validation/speaking-describe-image.validation.js +1 -31
  476. package/dist/shared/lib/validation/spontaneous-qa.validation.js +1 -48
  477. package/dist/shared/lib/validation/word-fill-paragraph-default.validation.js +1 -38
  478. package/dist/shared/lib/validation/word-fill-paragraph-with-options.validation.js +1 -39
  479. package/dist/shared/lib/validation/word-fill-paragraph.validation.js +1 -43
  480. package/dist/shared/lib/validation/word-ordering.validation.js +1 -33
  481. package/dist/shared/types/branch.types.js +1 -6
  482. package/dist/shared/types/common.types.js +1 -50
  483. package/dist/shared/types/entities/exam-schedule.types.js +1 -5
  484. package/dist/shared/types/entities/exam.types.js +1 -1
  485. package/dist/shared/types/entities/question.types.js +1 -4
  486. package/dist/shared/types/entities/result.types.js +1 -4
  487. package/dist/shared/types/entities/user.types.js +1 -4
  488. package/dist/shared/types/exam-taking.types.js +1 -2
  489. package/dist/shared/types/history.types.js +1 -34
  490. package/dist/shared/types/index.js +1 -1
  491. package/dist/shared/types/questions/choose-correct-adjective.js +1 -4
  492. package/dist/shared/types/questions/choose-the-correct-answer.js +1 -9
  493. package/dist/shared/types/questions/fill-missing-words-in-grid.js +1 -11
  494. package/dist/shared/types/questions/index.js +1 -40
  495. package/dist/shared/types/questions/listen-and-speak-answer.js +1 -1
  496. package/dist/shared/types/questions/listen-and-speak-compare-images.js +1 -1
  497. package/dist/shared/types/questions/listen-and-speak-image-group.js +1 -1
  498. package/dist/shared/types/questions/listen-and-speak-info-exchange.js +1 -3
  499. package/dist/shared/types/questions/listen-and-speak-odd-one-out.js +1 -3
  500. package/dist/shared/types/questions/listen-and-speak-question-list.js +1 -1
  501. package/dist/shared/types/questions/listen-and-speak-with-story-images.js +1 -3
  502. package/dist/shared/types/questions/look-picture-choose-correct-answer.js +1 -5
  503. package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +1 -9
  504. package/dist/shared/types/questions/look-picture-fill-word-hint.js +1 -7
  505. package/dist/shared/types/questions/read-displayed-content.js +1 -1
  506. package/dist/shared/types/questions/speaking-conversation.js +1 -7
  507. package/dist/shared/types/questions/speaking-describe-image.js +1 -1
  508. package/dist/shared/types/questions/spontaneous-qa.js +1 -1
  509. package/dist/shared/types/questions/write-correct-verb-form.js +1 -12
  510. package/dist/shared/types/role.types.js +1 -1
  511. package/dist/shared/types/structure-management.js +1 -28
  512. package/dist/shared/types/student.types.js +1 -54
  513. package/dist/shared/types/teacher-dashboard-rooms.js +1 -2
  514. package/package.json +12 -3
@@ -1,72 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { Check, X } from 'lucide-react';
4
- import { Badge } from '../../components/ui/badge';
5
- import { ResolvedImage } from '../../components/common/ResolvedImage';
6
- import { asRecord, normalizeRecord, normalizeSelected, readQuestionText, readString, transformAdjectiveQuestions, transformChoiceQuestions, transformColoring, transformFillQuestions } from './review-renderer.utils';
7
- export function ReviewQuestionRenderer({ part, reviewData }) {
8
- if (['CHOOSE_THE_CORRECT_ANSWER', 'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER', 'TRUE_FALSE', 'CHOOSE_CORRECT_ADJECTIVE'].includes(part.questionType)) {
9
- return _jsx(ChoiceReview, { part: part, reviewData: reviewData });
10
- }
11
- if (['FILL_IN_BLANK', 'WRITE_CORRECT_VERB_FORM', 'ANSWER_THE_QUESTION', 'WRITE_SENTENCES'].includes(part.questionType)) {
12
- return _jsx(FillReview, { part: part, reviewData: reviewData });
13
- }
14
- if (part.questionType === 'READ_AND_COLOR_OBJECTS' || part.questionType === 'IMAGE_OBJECT_MATCHING') {
15
- return _jsx(ColoringReview, { part: part, reviewData: reviewData });
16
- }
17
- return _jsx(GenericReview, { part: part, reviewData: reviewData });
18
- }
19
- function ChoiceReview({ part, reviewData }) {
20
- const questions = part.questionType === 'CHOOSE_CORRECT_ADJECTIVE' ? transformAdjectiveQuestions(part.questions) : transformChoiceQuestions(part.questions);
21
- return _jsx("div", { className: "space-y-4", children: questions.map((question) => {
22
- const selected = normalizeSelected(reviewData.studentAnswers[question.id]);
23
- const correct = normalizeSelected(reviewData.correctOptionIdMap[question.id]);
24
- return _jsxs(QuestionShell, { question: part.questions.find((item) => item.id === question.id), reviewData: reviewData, children: [question.imageUrl && _jsx(ResolvedImage, { src: question.imageUrl, alt: "Question", className: "mb-3 max-h-64 rounded object-contain" }), _jsx("p", { className: "mb-3 font-medium", children: 'questionText' in question ? question.questionText : `${question.sentenceBefore} ____ ${question.sentenceAfter}` }), _jsx("div", { className: "grid gap-2 md:grid-cols-2", children: question.options.map((option) => _jsxs("div", { className: `rounded-lg border p-3 ${correct.includes(option.id) ? 'border-green-400 bg-green-50' : selected.includes(option.id) ? 'border-red-300 bg-red-50' : 'border-slate-200'}`, children: [_jsxs("span", { className: "font-bold", children: [option.label, "."] }), " ", option.imageUrl ? _jsx(ResolvedImage, { src: option.imageUrl, alt: option.label, className: "mt-2 max-h-24 rounded object-contain" }) : option.text] }, option.id)) })] }, question.id);
25
- }) });
26
- }
27
- function FillReview({ part, reviewData }) {
28
- const questions = transformFillQuestions(part.questions);
29
- return _jsx("div", { className: "space-y-4", children: questions.map((question) => _jsxs(QuestionShell, { question: part.questions.find((item) => item.id === question.id), reviewData: reviewData, children: [_jsxs("p", { className: "text-base", children: [_jsx("span", { children: question.textBefore }), " ", _jsx(AnswerPill, { value: reviewData.studentAnswers[question.id], correct: reviewData.correctAnswerTextMap[question.id] }), " ", _jsx("span", { children: question.textAfter })] }), question.imageUrl && _jsx(ResolvedImage, { src: question.imageUrl, alt: "Question", className: "mt-3 max-h-64 rounded object-contain" })] }, question.id)) });
30
- }
31
- function ColoringReview({ part, reviewData }) {
32
- const data = transformColoring(part.questions);
33
- return _jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "rounded-xl border bg-white p-4", children: [_jsx("p", { className: "font-semibold", children: data.instruction || part.instructions }), data.imageUrl && _jsx(ResolvedImage, { src: data.imageUrl, alt: "Coloring", className: "mt-3 max-h-96 rounded object-contain" }), _jsx("div", { className: "mt-3 flex flex-wrap gap-2", children: data.hintItems.map((item) => _jsx("span", { className: "rounded-full border px-3 py-1 text-sm", style: { backgroundColor: item.color || undefined }, children: item.label || item.id }, item.id)) })] }), part.questions.map((question) => _jsx(QuestionShell, { question: question, reviewData: reviewData, children: _jsx("pre", { className: "overflow-auto rounded bg-slate-50 p-3 text-xs", children: JSON.stringify({ studentAnswer: reviewData.rawStudentAnswerMap[question.id], correctAnswer: reviewData.rawCorrectAnswerMap[question.id] }, null, 2) }) }, question.id))] });
34
- }
35
- function GenericReview({ part, reviewData }) {
36
- return _jsx("div", { className: "space-y-4", children: part.questions.map((question) => _jsxs(QuestionShell, { question: question, reviewData: reviewData, children: [_jsx("p", { className: "mb-3 whitespace-pre-wrap text-sm text-slate-700", children: readQuestionText(question) }), _jsx("pre", { className: "overflow-auto rounded bg-slate-50 p-3 text-xs", children: JSON.stringify({ content: question.content, studentAnswer: reviewData.rawStudentAnswerMap[question.id], correctAnswer: reviewData.rawCorrectAnswerMap[question.id] }, null, 2) })] }, question.id)) });
37
- }
38
- function QuestionShell({ question, reviewData, children }) {
39
- if (!question)
40
- return null;
41
- const score = reviewData.questionScoreMap[question.id];
42
- const isCorrect = reviewData.isCorrectMap[question.id];
43
- return _jsxs("div", { className: "rounded-xl border bg-white p-4", children: [_jsxs("div", { className: "mb-3 flex flex-wrap items-center justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("strong", { children: ["C\u00E2u ", question.question_number] }), _jsx(ResultBadge, { isCorrect: isCorrect })] }), score && _jsxs(Badge, { variant: "outline", children: [formatScore(score.score), " / ", formatScore(score.maxScore)] })] }), children, _jsx(Explanation, { question: question })] });
44
- }
45
- function ResultBadge({ isCorrect }) {
46
- if (isCorrect === true)
47
- return _jsxs(Badge, { className: "bg-green-600", children: [_jsx(Check, { className: "mr-1 h-3 w-3" }), "\u0110\u00FAng"] });
48
- if (isCorrect === false)
49
- return _jsxs(Badge, { variant: "destructive", children: [_jsx(X, { className: "mr-1 h-3 w-3" }), "Sai"] });
50
- return _jsx(Badge, { variant: "outline", children: "Ch\u01B0a ch\u1EA5m" });
51
- }
52
- function AnswerPill({ value, correct }) {
53
- return _jsxs("span", { className: "inline-flex rounded-md border border-blue-200 bg-blue-50 px-2 py-1 font-semibold text-blue-700", children: [stringifyAnswer(value), correct ? _jsxs("span", { className: "ml-2 text-green-700", children: ["(", correct, ")"] }) : null] });
54
- }
55
- function Explanation({ question }) {
56
- const explanation = readString(question, 'explanation') || readString(asRecord(question.content), 'explanation');
57
- return explanation ? _jsxs("div", { className: "mt-3 rounded-lg bg-amber-50 p-3 text-sm text-amber-900", children: [_jsx("strong", { children: "Gi\u1EA3i th\u00EDch:" }), " ", explanation] }) : null;
58
- }
59
- function stringifyAnswer(value) {
60
- if (value == null || value === '')
61
- return '—';
62
- if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean')
63
- return String(value);
64
- const record = normalizeRecord(value);
65
- const text = Object.values(record).join(', ');
66
- return text || JSON.stringify(value);
67
- }
68
- function formatScore(score) {
69
- if (score == null || !Number.isFinite(score))
70
- return '0';
71
- return score < 10 ? score.toFixed(1) : String(Math.round(score));
72
- }
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Check as n,X as t}from"lucide-react";import{Badge as i}from"../../components/ui/badge";import{ResolvedImage as a}from"../../components/common/ResolvedImage";import{asRecord as s,normalizeRecord as o,normalizeSelected as c,readQuestionText as l,readString as d,transformAdjectiveQuestions as u,transformChoiceQuestions as m,transformColoring as p,transformFillQuestions as h}from"./review-renderer.utils";export function ReviewQuestionRenderer({part:r,reviewData:n}){return["CHOOSE_THE_CORRECT_ANSWER","LOOK_PICTURE_CHOOSE_CORRECT_ANSWER","TRUE_FALSE","CHOOSE_CORRECT_ADJECTIVE"].includes(r.questionType)?e(b,{part:r,reviewData:n}):["FILL_IN_BLANK","WRITE_CORRECT_VERB_FORM","ANSWER_THE_QUESTION","WRITE_SENTENCES"].includes(r.questionType)?e(f,{part:r,reviewData:n}):"READ_AND_COLOR_OBJECTS"===r.questionType||"IMAGE_OBJECT_MATCHING"===r.questionType?e(N,{part:r,reviewData:n}):e(g,{part:r,reviewData:n})}function b({part:n,reviewData:t}){const i="CHOOSE_CORRECT_ADJECTIVE"===n.questionType?u(n.questions):m(n.questions);return e("div",{className:"space-y-4",children:i.map(i=>{const s=c(t.studentAnswers[i.id]),o=c(t.correctOptionIdMap[i.id]);return r(w,{question:n.questions.find(e=>e.id===i.id),reviewData:t,children:[i.imageUrl&&e(a,{src:i.imageUrl,alt:"Question",className:"mb-3 max-h-64 rounded object-contain"}),e("p",{className:"mb-3 font-medium",children:"questionText"in i?i.questionText:`${i.sentenceBefore} ____ ${i.sentenceAfter}`}),e("div",{className:"grid gap-2 md:grid-cols-2",children:i.options.map(n=>r("div",{className:"rounded-lg border p-3 "+(o.includes(n.id)?"border-green-400 bg-green-50":s.includes(n.id)?"border-red-300 bg-red-50":"border-slate-200"),children:[r("span",{className:"font-bold",children:[n.label,"."]})," ",n.imageUrl?e(a,{src:n.imageUrl,alt:n.label,className:"mt-2 max-h-24 rounded object-contain"}):n.text]},n.id))})]},i.id)})})}function f({part:n,reviewData:t}){const i=h(n.questions);return e("div",{className:"space-y-4",children:i.map(i=>r(w,{question:n.questions.find(e=>e.id===i.id),reviewData:t,children:[r("p",{className:"text-base",children:[e("span",{children:i.textBefore})," ",e(v,{value:t.studentAnswers[i.id],correct:t.correctAnswerTextMap[i.id]})," ",e("span",{children:i.textAfter})]}),i.imageUrl&&e(a,{src:i.imageUrl,alt:"Question",className:"mt-3 max-h-64 rounded object-contain"})]},i.id))})}function N({part:n,reviewData:t}){const i=p(n.questions);return r("div",{className:"space-y-4",children:[r("div",{className:"rounded-xl border bg-white p-4",children:[e("p",{className:"font-semibold",children:i.instruction||n.instructions}),i.imageUrl&&e(a,{src:i.imageUrl,alt:"Coloring",className:"mt-3 max-h-96 rounded object-contain"}),e("div",{className:"mt-3 flex flex-wrap gap-2",children:i.hintItems.map(r=>e("span",{className:"rounded-full border px-3 py-1 text-sm",style:{backgroundColor:r.color||void 0},children:r.label||r.id},r.id))})]}),n.questions.map(r=>e(w,{question:r,reviewData:t,children:e("pre",{className:"overflow-auto rounded bg-slate-50 p-3 text-xs",children:JSON.stringify({studentAnswer:t.rawStudentAnswerMap[r.id],correctAnswer:t.rawCorrectAnswerMap[r.id]},null,2)})},r.id))]})}function g({part:n,reviewData:t}){return e("div",{className:"space-y-4",children:n.questions.map(n=>r(w,{question:n,reviewData:t,children:[e("p",{className:"mb-3 whitespace-pre-wrap text-sm text-slate-700",children:l(n)}),e("pre",{className:"overflow-auto rounded bg-slate-50 p-3 text-xs",children:JSON.stringify({content:n.content,studentAnswer:t.rawStudentAnswerMap[n.id],correctAnswer:t.rawCorrectAnswerMap[n.id]},null,2)})]},n.id))})}function w({question:n,reviewData:t,children:a}){if(!n)return null;const s=t.questionScoreMap[n.id],o=t.isCorrectMap[n.id];return r("div",{className:"rounded-xl border bg-white p-4",children:[r("div",{className:"mb-3 flex flex-wrap items-center justify-between gap-2",children:[r("div",{className:"flex items-center gap-2",children:[r("strong",{children:["Câu ",n.question_number]}),e(x,{isCorrect:o})]}),s&&r(i,{variant:"outline",children:[_(s.score)," / ",_(s.maxScore)]})]}),a,e(E,{question:n})]})}function x({isCorrect:a}){return!0===a?r(i,{className:"bg-green-600",children:[e(n,{className:"mr-1 h-3 w-3"}),"Đúng"]}):!1===a?r(i,{variant:"destructive",children:[e(t,{className:"mr-1 h-3 w-3"}),"Sai"]}):e(i,{variant:"outline",children:"Chưa chấm"})}function v({value:e,correct:n}){return r("span",{className:"inline-flex rounded-md border border-blue-200 bg-blue-50 px-2 py-1 font-semibold text-blue-700",children:[C(e),n?r("span",{className:"ml-2 text-green-700",children:["(",n,")"]}):null]})}function E({question:n}){const t=d(n,"explanation")||d(s(n.content),"explanation");return t?r("div",{className:"mt-3 rounded-lg bg-amber-50 p-3 text-sm text-amber-900",children:[e("strong",{children:"Giải thích:"})," ",t]}):null}function C(e){if(null==e||""===e)return"—";if("string"==typeof e||"number"==typeof e||"boolean"==typeof e)return String(e);const r=o(e);return Object.values(r).join(", ")||JSON.stringify(e)}function _(e){return null!=e&&Number.isFinite(e)?e<10?e.toFixed(1):String(Math.round(e)):"0"}
@@ -1,2 +1 @@
1
- export { ResultReviewPageContainer } from './ResultReviewPageContainer';
2
- export { ReviewQuestionRenderer } from './ReviewQuestionRenderer';
1
+ export{ResultReviewPageContainer}from"./ResultReviewPageContainer";export{ReviewQuestionRenderer}from"./ReviewQuestionRenderer";
@@ -1,6 +1 @@
1
- export function unwrap(value) {
2
- return hasData(value) ? value.data : value;
3
- }
4
- function hasData(value) {
5
- return typeof value === 'object' && value !== null && 'data' in value;
6
- }
1
+ export function unwrap(n){return function(n){return"object"==typeof n&&null!==n&&"data"in n}(n)?n.data:n}
@@ -1,150 +1 @@
1
- export function transformResultAnswersForReview(answers, templateParts = []) {
2
- const groups = new Map();
3
- for (const answer of answers) {
4
- const key = [answer.partId ?? 'unknown', answer.questionGroup?.id ?? 'none', answer.questionType].join(':');
5
- groups.set(key, [...(groups.get(key) ?? []), answer]);
6
- }
7
- const result = {
8
- parts: [],
9
- studentAnswers: {},
10
- isCorrectMap: {},
11
- correctAnswerTextMap: {},
12
- correctOptionIdMap: {},
13
- essayGradingMap: {},
14
- partScoresMap: {},
15
- rawCorrectAnswerMap: {},
16
- rawStudentAnswerMap: {},
17
- questionScoreMap: {},
18
- };
19
- const partScores = [];
20
- for (const groupAnswers of groups.values()) {
21
- groupAnswers.sort((a, b) => a.questionNumber - b.questionNumber);
22
- const first = groupAnswers[0];
23
- if (!first)
24
- continue;
25
- const template = templateParts.find((part) => String(part.id) === String(first.partId));
26
- const partNo = first.partNo ?? parsePartNo(template?.name) ?? 0;
27
- result.parts.push({
28
- partId: first.partId,
29
- partNo,
30
- questionType: first.questionType,
31
- name: template?.name,
32
- instructions: template?.instructions,
33
- audioUrl: template?.audioUrl,
34
- questions: groupAnswers.map(toQuestion),
35
- });
36
- const scored = groupAnswers.filter((answer) => !answer.isExample);
37
- partScores.push({
38
- score: scored.reduce((sum, answer) => sum + (answer.score ?? 0), 0),
39
- maxScore: scored.reduce((sum, answer) => sum + answer.maxScore, 0),
40
- correctCount: scored.filter((answer) => answer.isCorrect === true).length,
41
- totalCount: scored.length,
42
- });
43
- for (const answer of groupAnswers)
44
- addAnswerMaps(result, answer);
45
- }
46
- const order = result.parts.map((_, index) => index).sort((a, b) => {
47
- const byPart = result.parts[a].partNo - result.parts[b].partNo;
48
- return byPart || ((result.parts[a].questions[0]?.question_number ?? 0) - (result.parts[b].questions[0]?.question_number ?? 0));
49
- });
50
- result.parts = order.map((index) => result.parts[index]);
51
- order.forEach((original, sorted) => { result.partScoresMap[sorted] = partScores[original]; });
52
- return result;
53
- }
54
- function toQuestion(answer) {
55
- return {
56
- id: answer.questionId,
57
- content: answer.questionContent,
58
- correct_answer: answer.correctAnswer,
59
- total_points: String(answer.maxScore),
60
- question_type: answer.questionType,
61
- is_example: answer.isExample ?? false,
62
- explanation: answer.explanation ?? '',
63
- difficult_level: 'MEDIUM',
64
- question_skill_type: '',
65
- exam_id: '',
66
- part_id: answer.partId ?? '',
67
- part_no: answer.partNo ?? 0,
68
- question_number: answer.questionNumber,
69
- questionGroup: answer.questionGroup ?? null,
70
- };
71
- }
72
- function addAnswerMaps(result, answer) {
73
- const student = transformStudentAnswer(answer.questionId, answer.questionType, answer.studentAnswer);
74
- if (student !== undefined)
75
- result.studentAnswers[answer.questionId] = student;
76
- result.isCorrectMap[answer.questionId] = answer.isCorrect;
77
- result.rawCorrectAnswerMap[answer.questionId] = answer.correctAnswer;
78
- result.rawStudentAnswerMap[answer.questionId] = answer.studentAnswer;
79
- result.questionScoreMap[answer.questionId] = { score: answer.score, maxScore: answer.maxScore };
80
- const correctText = readAnswerText(answer.correctAnswer);
81
- if (correctText)
82
- result.correctAnswerTextMap[answer.questionId] = correctText;
83
- const correctOption = readOptionId(answer.questionId, answer.correctAnswer);
84
- if (correctOption)
85
- result.correctOptionIdMap[answer.questionId] = correctOption;
86
- if (answer.questionType === 'CHOOSE_CORRECT_ADJECTIVE') {
87
- const selected = readNumberProp(answer.studentAnswer, 'answer');
88
- const correct = readNumberProp(answer.correctAnswer, 'answer');
89
- if (selected !== undefined)
90
- result.studentAnswers[answer.questionId] = `${answer.questionId}-opt-${selected}`;
91
- if (correct !== undefined)
92
- result.correctOptionIdMap[answer.questionId] = `${answer.questionId}-opt-${correct}`;
93
- }
94
- if (answer.questionType === 'WRITE_A_SHORT_LETTER' || answer.questionType === 'WRITE_SHORT_PARAGRAPH') {
95
- result.essayGradingMap[answer.questionId] = {
96
- score: answer.teacherScore ?? answer.score,
97
- maxScore: answer.maxScore,
98
- teacherFeedback: answer.teacherFeedback ?? null,
99
- };
100
- }
101
- }
102
- function transformStudentAnswer(questionId, questionType, value) {
103
- const record = asRecord(value);
104
- if (value == null)
105
- return undefined;
106
- if (questionType === 'CHOOSE_THE_CORRECT_ANSWER')
107
- return readOptionId(questionId, value);
108
- if (['FILL_IN_BLANK', 'WRITE_CORRECT_VERB_FORM', 'ANSWER_THE_QUESTION', 'TRUE_FALSE', 'WRITE_SENTENCES'].includes(questionType)) {
109
- return typeof value === 'string' ? value : record.answer ?? value;
110
- }
111
- if (questionType === 'READ_AND_COLOR_OBJECTS' || questionType === 'IMAGE_OBJECT_MATCHING')
112
- return record.answers ?? value;
113
- if (questionType.startsWith('LISTEN_AND_'))
114
- return value;
115
- return record.answer ?? value;
116
- }
117
- function readOptionId(questionId, value) {
118
- const answer = asRecord(value).answer;
119
- if (typeof answer === 'number')
120
- return `${questionId}-opt-${answer}`;
121
- if (typeof answer === 'string')
122
- return answer;
123
- if (Array.isArray(answer))
124
- return answer.map((item) => typeof item === 'number' ? `${questionId}-opt-${item}` : String(item)).join(',');
125
- return '';
126
- }
127
- function readAnswerText(value) {
128
- const record = asRecord(value);
129
- if (typeof record.answer === 'string')
130
- return record.answer;
131
- if (typeof record.correctAnswer === 'string')
132
- return record.correctAnswer;
133
- const answers = record.answers;
134
- if (Array.isArray(answers))
135
- return String(answers[0] ?? '');
136
- if (answers && typeof answers === 'object')
137
- return String(Object.values(answers)[0] ?? '');
138
- return '';
139
- }
140
- function asRecord(value) {
141
- return typeof value === 'object' && value !== null ? value : {};
142
- }
143
- function readNumberProp(value, key) {
144
- const item = asRecord(value)[key];
145
- return typeof item === 'number' ? item : undefined;
146
- }
147
- function parsePartNo(value) {
148
- const match = value?.match(/\d+/);
149
- return match ? Number(match[0]) : undefined;
150
- }
1
+ export function transformResultAnswersForReview(r,n=[]){const o=new Map;for(const t of r){const e=[t.partId??"unknown",t.questionGroup?.id??"none",t.questionType].join(":");o.set(e,[...o.get(e)??[],t])}const i={parts:[],studentAnswers:{},isCorrectMap:{},correctAnswerTextMap:{},correctOptionIdMap:{},essayGradingMap:{},partScoresMap:{},rawCorrectAnswerMap:{},rawStudentAnswerMap:{},questionScoreMap:{}},u=[];for(const r of o.values()){r.sort((t,e)=>t.questionNumber-e.questionNumber);const o=r[0];if(!o)continue;const a=n.find(t=>String(t.id)===String(o.partId)),c=o.partNo??s(a?.name)??0;i.parts.push({partId:o.partId,partNo:c,questionType:o.questionType,name:a?.name,instructions:a?.instructions,audioUrl:a?.audioUrl,questions:r.map(t)});const p=r.filter(t=>!t.isExample);u.push({score:p.reduce((t,e)=>t+(e.score??0),0),maxScore:p.reduce((t,e)=>t+e.maxScore,0),correctCount:p.filter(t=>!0===t.isCorrect).length,totalCount:p.length});for(const t of r)e(i,t)}const a=i.parts.map((t,e)=>e).sort((t,e)=>i.parts[t].partNo-i.parts[e].partNo||(i.parts[t].questions[0]?.question_number??0)-(i.parts[e].questions[0]?.question_number??0));return i.parts=a.map(t=>i.parts[t]),a.forEach((t,e)=>{i.partScoresMap[e]=u[t]}),i}function t(t){return{id:t.questionId,content:t.questionContent,correct_answer:t.correctAnswer,total_points:String(t.maxScore),question_type:t.questionType,is_example:t.isExample??!1,explanation:t.explanation??"",difficult_level:"MEDIUM",question_skill_type:"",exam_id:"",part_id:t.partId??"",part_no:t.partNo??0,question_number:t.questionNumber,questionGroup:t.questionGroup??null}}function e(t,e){const s=function(t,e,o){const s=n(o);if(null==o)return;if("CHOOSE_THE_CORRECT_ANSWER"===e)return r(t,o);if(["FILL_IN_BLANK","WRITE_CORRECT_VERB_FORM","ANSWER_THE_QUESTION","TRUE_FALSE","WRITE_SENTENCES"].includes(e))return"string"==typeof o?o:s.answer??o;return"READ_AND_COLOR_OBJECTS"===e||"IMAGE_OBJECT_MATCHING"===e?s.answers??o:e.startsWith("LISTEN_AND_")?o:s.answer??o}(e.questionId,e.questionType,e.studentAnswer);void 0!==s&&(t.studentAnswers[e.questionId]=s),t.isCorrectMap[e.questionId]=e.isCorrect,t.rawCorrectAnswerMap[e.questionId]=e.correctAnswer,t.rawStudentAnswerMap[e.questionId]=e.studentAnswer,t.questionScoreMap[e.questionId]={score:e.score,maxScore:e.maxScore};const i=function(t){const e=n(t);if("string"==typeof e.answer)return e.answer;if("string"==typeof e.correctAnswer)return e.correctAnswer;const r=e.answers;return Array.isArray(r)?String(r[0]??""):r&&"object"==typeof r?String(Object.values(r)[0]??""):""}(e.correctAnswer);i&&(t.correctAnswerTextMap[e.questionId]=i);const u=r(e.questionId,e.correctAnswer);if(u&&(t.correctOptionIdMap[e.questionId]=u),"CHOOSE_CORRECT_ADJECTIVE"===e.questionType){const r=o(e.studentAnswer,"answer"),n=o(e.correctAnswer,"answer");void 0!==r&&(t.studentAnswers[e.questionId]=`${e.questionId}-opt-${r}`),void 0!==n&&(t.correctOptionIdMap[e.questionId]=`${e.questionId}-opt-${n}`)}"WRITE_A_SHORT_LETTER"!==e.questionType&&"WRITE_SHORT_PARAGRAPH"!==e.questionType||(t.essayGradingMap[e.questionId]={score:e.teacherScore??e.score,maxScore:e.maxScore,teacherFeedback:e.teacherFeedback??null})}function r(t,e){const r=n(e).answer;return"number"==typeof r?`${t}-opt-${r}`:"string"==typeof r?r:Array.isArray(r)?r.map(e=>"number"==typeof e?`${t}-opt-${e}`:String(e)).join(","):""}function n(t){return"object"==typeof t&&null!==t?t:{}}function o(t,e){const r=n(t)[e];return"number"==typeof r?r:void 0}function s(t){const e=t?.match(/\d+/);return e?Number(e[0]):void 0}
@@ -1,97 +1 @@
1
- export function asRecord(value) {
2
- return typeof value === 'object' && value !== null ? value : {};
3
- }
4
- export function readString(value, ...keys) {
5
- const record = asRecord(value);
6
- for (const key of keys) {
7
- const item = record[key];
8
- if (typeof item === 'string')
9
- return item;
10
- }
11
- return '';
12
- }
13
- export function readArray(value, key) {
14
- const item = asRecord(value)[key];
15
- return Array.isArray(item) ? item : [];
16
- }
17
- export function readQuestionText(question) {
18
- return readString(question.content, 'questionText', 'question', 'text', 'sentence') || String(question.content ?? '');
19
- }
20
- export function transformChoiceQuestions(questions) {
21
- return questions.map((question) => {
22
- const content = asRecord(question.content);
23
- const optionType = readString(content, 'optionType');
24
- return {
25
- id: question.id,
26
- questionNumber: question.question_number,
27
- questionText: readQuestionText(question),
28
- imageUrl: readString(content, 'imageUrl'),
29
- optionType,
30
- multipleAnswers: Boolean(content.multipleAnswers),
31
- options: readOptions(content, question.id, optionType),
32
- };
33
- });
34
- }
35
- export function transformFillQuestions(questions) {
36
- return questions.map((question) => {
37
- const content = asRecord(question.content);
38
- return {
39
- id: question.id,
40
- questionNumber: question.question_number,
41
- textBefore: readString(content, 'textBefore', 'sentenceBefore', 'prefixText'),
42
- textAfter: readString(content, 'textAfter', 'sentenceAfter', 'suffixText'),
43
- imageUrl: readString(content, 'imageUrl'),
44
- };
45
- });
46
- }
47
- export function transformAdjectiveQuestions(questions) {
48
- return questions.map((question) => {
49
- const content = asRecord(question.content);
50
- return {
51
- id: question.id,
52
- questionNumber: question.question_number,
53
- textBefore: '',
54
- textAfter: '',
55
- sentenceBefore: readString(content, 'sentenceBefore', 'textBefore'),
56
- sentenceAfter: readString(content, 'sentenceAfter', 'textAfter'),
57
- imageUrl: readString(content, 'imageUrl'),
58
- options: readOptions(content, question.id, 'text'),
59
- };
60
- });
61
- }
62
- export function transformColoring(questions) {
63
- const first = asRecord(questions[0]?.content);
64
- return {
65
- instruction: readString(first, 'instruction'),
66
- instructions: readArray(first, 'instructions').filter((item) => typeof item === 'string'),
67
- imageUrl: readString(first, 'imageUrl', 'backgroundImageUrl'),
68
- audioUrl: readString(first, 'audioUrl'),
69
- hintItems: readArray(first, 'hintItems').map((item) => {
70
- const row = asRecord(item);
71
- return { id: String(row.id ?? ''), label: readString(row, 'label'), color: readString(row, 'color') };
72
- }).filter((item) => item.id),
73
- regions: readArray(first, 'regions').map((item) => {
74
- const row = asRecord(item);
75
- return { id: String(row.id ?? ''), x: Number(row.x ?? 0), y: Number(row.y ?? 0), width: Number(row.width ?? 0), height: Number(row.height ?? 0) };
76
- }).filter((item) => item.id),
77
- };
78
- }
79
- function readOptions(content, questionId, optionType) {
80
- return readArray(content, 'options').map((item, index) => {
81
- const option = asRecord(item);
82
- const id = String(option.id ?? `${questionId}-opt-${index}`);
83
- const text = readString(option, 'text', 'label', 'value', 'imageUrl');
84
- return { id, label: readString(option, 'label') || String.fromCharCode(65 + index), text, imageUrl: optionType === 'image' ? text : readString(option, 'imageUrl') };
85
- });
86
- }
87
- export function normalizeSelected(value) {
88
- if (Array.isArray(value))
89
- return value.map(String);
90
- if (typeof value === 'string' && value)
91
- return value.split(',').map((item) => item.trim()).filter(Boolean);
92
- return [];
93
- }
94
- export function normalizeRecord(value) {
95
- const record = asRecord(value);
96
- return Object.fromEntries(Object.entries(record).map(([key, item]) => [key, String(item ?? '')]));
97
- }
1
+ export function asRecord(e){return"object"==typeof e&&null!==e?e:{}}export function readString(e,...r){const t=asRecord(e);for(const e of r){const r=t[e];if("string"==typeof r)return r}return""}export function readArray(e,r){const t=asRecord(e)[r];return Array.isArray(t)?t:[]}export function readQuestionText(e){return readString(e.content,"questionText","question","text","sentence")||String(e.content??"")}export function transformChoiceQuestions(r){return r.map(r=>{const t=asRecord(r.content),n=readString(t,"optionType");return{id:r.id,questionNumber:r.question_number,questionText:readQuestionText(r),imageUrl:readString(t,"imageUrl"),optionType:n,multipleAnswers:Boolean(t.multipleAnswers),options:e(t,r.id,n)}})}export function transformFillQuestions(e){return e.map(e=>{const r=asRecord(e.content);return{id:e.id,questionNumber:e.question_number,textBefore:readString(r,"textBefore","sentenceBefore","prefixText"),textAfter:readString(r,"textAfter","sentenceAfter","suffixText"),imageUrl:readString(r,"imageUrl")}})}export function transformAdjectiveQuestions(r){return r.map(r=>{const t=asRecord(r.content);return{id:r.id,questionNumber:r.question_number,textBefore:"",textAfter:"",sentenceBefore:readString(t,"sentenceBefore","textBefore"),sentenceAfter:readString(t,"sentenceAfter","textAfter"),imageUrl:readString(t,"imageUrl"),options:e(t,r.id,"text")}})}export function transformColoring(e){const r=asRecord(e[0]?.content);return{instruction:readString(r,"instruction"),instructions:readArray(r,"instructions").filter(e=>"string"==typeof e),imageUrl:readString(r,"imageUrl","backgroundImageUrl"),audioUrl:readString(r,"audioUrl"),hintItems:readArray(r,"hintItems").map(e=>{const r=asRecord(e);return{id:String(r.id??""),label:readString(r,"label"),color:readString(r,"color")}}).filter(e=>e.id),regions:readArray(r,"regions").map(e=>{const r=asRecord(e);return{id:String(r.id??""),x:Number(r.x??0),y:Number(r.y??0),width:Number(r.width??0),height:Number(r.height??0)}}).filter(e=>e.id)}}function e(e,r,t){return readArray(e,"options").map((e,n)=>{const i=asRecord(e),o=String(i.id??`${r}-opt-${n}`),a=readString(i,"text","label","value","imageUrl");return{id:o,label:readString(i,"label")||String.fromCharCode(65+n),text:a,imageUrl:"image"===t?a:readString(i,"imageUrl")}})}export function normalizeSelected(e){return Array.isArray(e)?e.map(String):"string"==typeof e&&e?e.split(",").map(e=>e.trim()).filter(Boolean):[]}export function normalizeRecord(e){const r=asRecord(e);return Object.fromEntries(Object.entries(r).map(([e,r])=>[e,String(r??"")]))}
@@ -1 +1 @@
1
- export {};
1
+ export{};
@@ -1,138 +1 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useRef, useState, useEffect, useMemo } from 'react';
4
- import { Pause, Play, RotateCcw } from 'lucide-react';
5
- import { usePresignedFileUrl } from '../../../shared/lib/hooks/usePresignedFileUrl';
6
- const PLAYBACK_SPEEDS = [0.75, 1, 1.25, 1.5];
7
- const WAVEFORM_BARS = 48;
8
- /** Deterministic pseudo-random bar heights — stable per render, no flicker on re-render. */
9
- function useWaveformHeights(count) {
10
- return useMemo(() => {
11
- const heights = [];
12
- let seed = 7;
13
- for (let i = 0; i < count; i++) {
14
- // LCG-like deterministic noise
15
- seed = (seed * 9301 + 49297) % 233280;
16
- const rnd = seed / 233280;
17
- // Bias towards mid-range heights for a natural-looking waveform
18
- const h = 0.25 + rnd * 0.75;
19
- // Soft envelope — taller in the middle, shorter at edges
20
- const envelope = Math.sin((i / (count - 1)) * Math.PI) * 0.25 + 0.75;
21
- heights.push(Math.max(0.2, h * envelope));
22
- }
23
- return heights;
24
- }, [count]);
25
- }
26
- export default function CambridgeYleAudioPlayer({ audioSrc, onPlay, onPause, compact = false, }) {
27
- const { previewUrl: resolvedAudioSrc } = usePresignedFileUrl(audioSrc);
28
- const audioRef = useRef(null);
29
- const [isPlaying, setIsPlaying] = useState(false);
30
- const [currentTime, setCurrentTime] = useState(0);
31
- const [duration, setDuration] = useState(0);
32
- const [speed, setSpeed] = useState(1);
33
- const barHeights = useWaveformHeights(WAVEFORM_BARS);
34
- useEffect(() => {
35
- const audio = audioRef.current;
36
- if (!audio)
37
- return;
38
- const updateDuration = () => {
39
- if (audio.duration && !isNaN(audio.duration) && isFinite(audio.duration)) {
40
- setDuration(audio.duration);
41
- }
42
- };
43
- const handleTimeUpdate = () => {
44
- setCurrentTime(audio.currentTime);
45
- updateDuration();
46
- };
47
- const handleEnded = () => {
48
- setIsPlaying(false);
49
- setCurrentTime(0);
50
- };
51
- updateDuration();
52
- audio.addEventListener('loadedmetadata', updateDuration);
53
- audio.addEventListener('durationchange', updateDuration);
54
- audio.addEventListener('canplay', updateDuration);
55
- audio.addEventListener('timeupdate', handleTimeUpdate);
56
- audio.addEventListener('ended', handleEnded);
57
- return () => {
58
- audio.removeEventListener('loadedmetadata', updateDuration);
59
- audio.removeEventListener('durationchange', updateDuration);
60
- audio.removeEventListener('canplay', updateDuration);
61
- audio.removeEventListener('timeupdate', handleTimeUpdate);
62
- audio.removeEventListener('ended', handleEnded);
63
- };
64
- }, []);
65
- useEffect(() => {
66
- if (audioRef.current)
67
- audioRef.current.playbackRate = speed;
68
- }, [speed]);
69
- const handlePlayPause = () => {
70
- const audio = audioRef.current;
71
- if (!audio)
72
- return;
73
- if (isPlaying) {
74
- audio.pause();
75
- setIsPlaying(false);
76
- onPause?.();
77
- }
78
- else {
79
- audio.play().catch(() => {
80
- // Source unplayable — reset UI instead of leaving a floating rejection.
81
- setIsPlaying(false);
82
- });
83
- setIsPlaying(true);
84
- onPlay?.();
85
- }
86
- };
87
- const handleRewind = () => {
88
- const audio = audioRef.current;
89
- if (!audio)
90
- return;
91
- audio.currentTime = Math.max(0, audio.currentTime - 5);
92
- setCurrentTime(audio.currentTime);
93
- };
94
- const handleWaveformClick = (e) => {
95
- const audio = audioRef.current;
96
- if (!audio || !duration)
97
- return;
98
- const rect = e.currentTarget.getBoundingClientRect();
99
- const ratio = Math.min(1, Math.max(0, (e.clientX - rect.left) / rect.width));
100
- const newTime = ratio * duration;
101
- audio.currentTime = newTime;
102
- setCurrentTime(newTime);
103
- };
104
- const formatTime = (time) => {
105
- const safe = Math.max(0, time);
106
- const minutes = Math.floor(safe / 60);
107
- const seconds = Math.floor(safe % 60);
108
- return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
109
- };
110
- const progress = duration > 0 ? currentTime / duration : 0;
111
- const progressPct = progress * 100;
112
- const remaining = Math.max(0, duration - currentTime);
113
- // --------------------- COMPACT MODE ---------------------
114
- if (compact) {
115
- return (_jsxs("div", { className: "relative overflow-hidden rounded-2xl border-2 border-blue-100 bg-white p-3 shadow-sm transition-all hover:border-blue-200 hover:shadow-md", children: [_jsx("audio", { ref: audioRef, src: resolvedAudioSrc || undefined, preload: "metadata" }), _jsxs("div", { className: "flex items-center gap-3 sm:gap-4", children: [_jsxs("button", { type: "button", onClick: handlePlayPause, className: "relative flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-coral-500 text-white shadow-lg shadow-coral-500/30 transition-all duration-200 hover:scale-105 hover:bg-coral-600 active:scale-95", "aria-label": isPlaying ? 'Pause' : 'Play', children: [isPlaying ? _jsx(Pause, { className: "h-5 w-5 fill-current" }) : _jsx(Play, { className: "ml-1 h-5 w-5 fill-current" }), isPlaying && (_jsx("span", { className: "pointer-events-none absolute inset-0 animate-ping rounded-full bg-coral-400 opacity-40" }))] }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col justify-center gap-2", children: [_jsxs("div", { className: "flex items-center justify-between px-1", children: [_jsx("span", { className: "text-sm font-black uppercase tracking-widest text-blue-900 font-[family-name:var(--font-nunito)]", children: "Audio" }), _jsxs("div", { className: "flex items-center gap-1.5 font-mono text-xs font-bold text-slate-500 tabular-nums", children: [_jsx("span", { className: "text-blue-600", children: formatTime(currentTime) }), _jsx("span", { children: "/" }), _jsx("span", { children: formatTime(duration) })] })] }), _jsxs("div", { className: "relative flex items-center", children: [_jsx("div", { className: "h-3 w-full overflow-hidden rounded-full bg-blue-50 shadow-inner", children: _jsx("div", { className: "h-full rounded-full bg-blue-500 transition-[width] duration-150", style: { width: `${progressPct}%` } }) }), _jsx("input", { type: "range", min: "0", max: duration || 0, step: "0.1", value: currentTime, onChange: (e) => {
116
- const newTime = parseFloat(e.target.value);
117
- if (audioRef.current)
118
- audioRef.current.currentTime = newTime;
119
- setCurrentTime(newTime);
120
- }, className: "absolute inset-0 w-full cursor-pointer opacity-0", "aria-label": "Seek" })] })] })] })] }));
121
- }
122
- // --------------------- FULL MODE ---------------------
123
- // Circular progress ring math
124
- const RING_SIZE = 56;
125
- const RING_STROKE = 3;
126
- const RING_RADIUS = (RING_SIZE - RING_STROKE) / 2;
127
- const RING_CIRC = 2 * Math.PI * RING_RADIUS;
128
- return (_jsxs("div", { className: "relative overflow-hidden rounded-2xl border-2 border-blue-100 bg-white p-3.5 shadow-sm transition-shadow hover:border-blue-200 hover:shadow-md", children: [_jsx("audio", { ref: audioRef, src: resolvedAudioSrc || undefined, preload: "metadata" }), _jsx("div", { className: "pointer-events-none absolute -right-6 -top-6 h-20 w-20 rounded-full bg-blue-100 opacity-60 blur-xl" }), _jsxs("div", { className: "relative flex items-center gap-3", children: [_jsxs("button", { type: "button", onClick: handleRewind, className: "relative flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-slate-500 transition-all hover:bg-slate-100 hover:text-blue-600 active:scale-95", "aria-label": "Rewind 5 seconds", title: "Quay l\u1EA1i 5 gi\u00E2y", children: [_jsx(RotateCcw, { className: "h-4 w-4", strokeWidth: 2.5 }), _jsx("span", { className: "pointer-events-none absolute bottom-1 right-1 text-[8px] font-black leading-none", children: "5" })] }), _jsxs("div", { className: "relative shrink-0", style: { width: RING_SIZE, height: RING_SIZE }, children: [_jsxs("svg", { width: RING_SIZE, height: RING_SIZE, className: "absolute inset-0 -rotate-90", "aria-hidden": true, children: [_jsx("circle", { cx: RING_SIZE / 2, cy: RING_SIZE / 2, r: RING_RADIUS, fill: "none", stroke: "rgb(239 246 255)", strokeWidth: RING_STROKE }), _jsx("circle", { cx: RING_SIZE / 2, cy: RING_SIZE / 2, r: RING_RADIUS, fill: "none", stroke: "url(#audio-ring-gradient)", strokeWidth: RING_STROKE, strokeLinecap: "round", strokeDasharray: RING_CIRC, strokeDashoffset: RING_CIRC * (1 - progress), className: "transition-[stroke-dashoffset] duration-150" }), _jsx("defs", { children: _jsxs("linearGradient", { id: "audio-ring-gradient", x1: "0", y1: "0", x2: "1", y2: "1", children: [_jsx("stop", { offset: "0%", stopColor: "#3B82F6" }), _jsx("stop", { offset: "100%", stopColor: "#06b6d4" })] }) })] }), _jsxs("button", { type: "button", onClick: handlePlayPause, className: "absolute inset-1.5 flex items-center justify-center rounded-full bg-coral-500 text-white shadow-lg shadow-coral-500/30 transition-all hover:shadow-xl hover:shadow-coral-500/50 hover:bg-coral-600 active:scale-95", "aria-label": isPlaying ? 'Pause' : 'Play', children: [isPlaying ? (_jsx(Pause, { className: "h-4 w-4 fill-current" })) : (_jsx(Play, { className: "ml-0.5 h-4 w-4 fill-current" })), isPlaying && (_jsx("span", { className: "pointer-events-none absolute inset-0 animate-ping rounded-full bg-coral-400 opacity-40" }))] })] }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-1", children: [_jsx("div", { onClick: handleWaveformClick, className: "group/wave relative flex h-8 cursor-pointer items-center gap-[2px]", role: "slider", "aria-label": "Seek", "aria-valuemin": 0, "aria-valuemax": duration || 0, "aria-valuenow": currentTime, children: barHeights.map((h, i) => {
129
- const barPos = (i + 0.5) / WAVEFORM_BARS;
130
- const isFilled = barPos <= progress;
131
- const isCurrent = Math.abs(barPos - progress) < 1 / WAVEFORM_BARS / 2;
132
- return (_jsx("span", { className: `flex-1 rounded-full transition-all duration-200 ${isFilled
133
- ? 'bg-blue-500'
134
- : 'bg-slate-200 group-hover/wave:bg-slate-300'} ${isCurrent && isPlaying ? 'animate-wave-pulse' : ''}`, style: { height: `${h * 100}%` } }, i));
135
- }) }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-baseline gap-1.5", children: [_jsxs("span", { className: "font-mono text-sm font-bold tabular-nums text-slate-800", children: ["\u2212", formatTime(remaining)] }), _jsxs("span", { className: "font-mono text-[10px] tabular-nums text-slate-400", children: ["/ ", formatTime(duration)] })] }), _jsx("div", { className: "flex items-center gap-0.5 rounded-full border border-slate-200 bg-white p-0.5 shadow-sm", children: PLAYBACK_SPEEDS.map((s) => (_jsxs("button", { type: "button", onClick: () => setSpeed(s), className: `rounded-full px-2 py-0.5 font-mono text-[10px] font-semibold tabular-nums transition-all ${speed === s
136
- ? 'bg-coral-500 text-white shadow-sm'
137
- : 'text-slate-500 hover:bg-slate-100 hover:text-slate-700'}`, children: [s, "\u00D7"] }, s))) })] })] })] })] }));
138
- }
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useRef as a,useState as r,useEffect as n,useMemo as l}from"react";import{Pause as s,Play as i,RotateCcw as o}from"lucide-react";import{usePresignedFileUrl as c}from"../../../shared/lib/hooks/usePresignedFileUrl";const d=[.75,1,1.25,1.5];export default function u({audioSrc:u,onPlay:h,onPause:m,compact:f=!1}){const{previewUrl:p}=c(u),v=a(null),[b,x]=r(!1),[g,w]=r(0),[N,y]=r(0),[k,M]=r(1),L=l(()=>{const e=[];let t=7;for(let a=0;a<E;a++){t=(9301*t+49297)%233280;const r=.25+t/233280*.75,n=.25*Math.sin(a/(E-1)*Math.PI)+.75;e.push(Math.max(.2,r*n))}return e},[E=48]);var E;n(()=>{const e=v.current;if(!e)return;const t=()=>{e.duration&&!isNaN(e.duration)&&isFinite(e.duration)&&y(e.duration)},a=()=>{w(e.currentTime),t()},r=()=>{x(!1),w(0)};return t(),e.addEventListener("loadedmetadata",t),e.addEventListener("durationchange",t),e.addEventListener("canplay",t),e.addEventListener("timeupdate",a),e.addEventListener("ended",r),()=>{e.removeEventListener("loadedmetadata",t),e.removeEventListener("durationchange",t),e.removeEventListener("canplay",t),e.removeEventListener("timeupdate",a),e.removeEventListener("ended",r)}},[]),n(()=>{v.current&&(v.current.playbackRate=k)},[k]);const j=()=>{const e=v.current;e&&(b?(e.pause(),x(!1),m?.()):(e.play().catch(()=>{x(!1)}),x(!0),h?.()))},C=e=>{const t=Math.max(0,e),a=Math.floor(t/60),r=Math.floor(t%60);return`${String(a).padStart(2,"0")}:${String(r).padStart(2,"0")}`},P=N>0?g/N:0,S=100*P,T=Math.max(0,N-g);if(f)return t("div",{className:"relative overflow-hidden rounded-2xl border-2 border-blue-100 bg-white p-3 shadow-sm transition-all hover:border-blue-200 hover:shadow-md",children:[e("audio",{ref:v,src:p||void 0,preload:"metadata"}),t("div",{className:"flex items-center gap-3 sm:gap-4",children:[t("button",{type:"button",onClick:j,className:"relative flex h-12 w-12 shrink-0 items-center justify-center rounded-full bg-coral-500 text-white shadow-lg shadow-coral-500/30 transition-all duration-200 hover:scale-105 hover:bg-coral-600 active:scale-95","aria-label":b?"Pause":"Play",children:[b?e(s,{className:"h-5 w-5 fill-current"}):e(i,{className:"ml-1 h-5 w-5 fill-current"}),b&&e("span",{className:"pointer-events-none absolute inset-0 animate-ping rounded-full bg-coral-400 opacity-40"})]}),t("div",{className:"flex min-w-0 flex-1 flex-col justify-center gap-2",children:[t("div",{className:"flex items-center justify-between px-1",children:[e("span",{className:"text-sm font-black uppercase tracking-widest text-blue-900 font-[family-name:var(--font-nunito)]",children:"Audio"}),t("div",{className:"flex items-center gap-1.5 font-mono text-xs font-bold text-slate-500 tabular-nums",children:[e("span",{className:"text-blue-600",children:C(g)}),e("span",{children:"/"}),e("span",{children:C(N)})]})]}),t("div",{className:"relative flex items-center",children:[e("div",{className:"h-3 w-full overflow-hidden rounded-full bg-blue-50 shadow-inner",children:e("div",{className:"h-full rounded-full bg-blue-500 transition-[width] duration-150",style:{width:`${S}%`}})}),e("input",{type:"range",min:"0",max:N||0,step:"0.1",value:g,onChange:e=>{const t=parseFloat(e.target.value);v.current&&(v.current.currentTime=t),w(t)},className:"absolute inset-0 w-full cursor-pointer opacity-0","aria-label":"Seek"})]})]})]})]});const $=56,F=26.5,R=2*Math.PI*F;return t("div",{className:"relative overflow-hidden rounded-2xl border-2 border-blue-100 bg-white p-3.5 shadow-sm transition-shadow hover:border-blue-200 hover:shadow-md",children:[e("audio",{ref:v,src:p||void 0,preload:"metadata"}),e("div",{className:"pointer-events-none absolute -right-6 -top-6 h-20 w-20 rounded-full bg-blue-100 opacity-60 blur-xl"}),t("div",{className:"relative flex items-center gap-3",children:[t("button",{type:"button",onClick:()=>{const e=v.current;e&&(e.currentTime=Math.max(0,e.currentTime-5),w(e.currentTime))},className:"relative flex h-9 w-9 shrink-0 items-center justify-center rounded-full text-slate-500 transition-all hover:bg-slate-100 hover:text-blue-600 active:scale-95","aria-label":"Rewind 5 seconds",title:"Quay lại 5 giây",children:[e(o,{className:"h-4 w-4",strokeWidth:2.5}),e("span",{className:"pointer-events-none absolute bottom-1 right-1 text-[8px] font-black leading-none",children:"5"})]}),t("div",{className:"relative shrink-0",style:{width:$,height:$},children:[t("svg",{width:$,height:$,className:"absolute inset-0 -rotate-90","aria-hidden":!0,children:[e("circle",{cx:28,cy:28,r:F,fill:"none",stroke:"rgb(239 246 255)",strokeWidth:3}),e("circle",{cx:28,cy:28,r:F,fill:"none",stroke:"url(#audio-ring-gradient)",strokeWidth:3,strokeLinecap:"round",strokeDasharray:R,strokeDashoffset:R*(1-P),className:"transition-[stroke-dashoffset] duration-150"}),e("defs",{children:t("linearGradient",{id:"audio-ring-gradient",x1:"0",y1:"0",x2:"1",y2:"1",children:[e("stop",{offset:"0%",stopColor:"#3B82F6"}),e("stop",{offset:"100%",stopColor:"#06b6d4"})]})})]}),t("button",{type:"button",onClick:j,className:"absolute inset-1.5 flex items-center justify-center rounded-full bg-coral-500 text-white shadow-lg shadow-coral-500/30 transition-all hover:shadow-xl hover:shadow-coral-500/50 hover:bg-coral-600 active:scale-95","aria-label":b?"Pause":"Play",children:[b?e(s,{className:"h-4 w-4 fill-current"}):e(i,{className:"ml-0.5 h-4 w-4 fill-current"}),b&&e("span",{className:"pointer-events-none absolute inset-0 animate-ping rounded-full bg-coral-400 opacity-40"})]})]}),t("div",{className:"flex min-w-0 flex-1 flex-col gap-1",children:[e("div",{onClick:e=>{const t=v.current;if(!t||!N)return;const a=e.currentTarget.getBoundingClientRect(),r=Math.min(1,Math.max(0,(e.clientX-a.left)/a.width))*N;t.currentTime=r,w(r)},className:"group/wave relative flex h-8 cursor-pointer items-center gap-[2px]",role:"slider","aria-label":"Seek","aria-valuemin":0,"aria-valuemax":N||0,"aria-valuenow":g,children:L.map((t,a)=>{const r=(a+.5)/48,n=r<=P,l=Math.abs(r-P)<1/48/2;return e("span",{className:`flex-1 rounded-full transition-all duration-200 ${n?"bg-blue-500":"bg-slate-200 group-hover/wave:bg-slate-300"} ${l&&b?"animate-wave-pulse":""}`,style:{height:100*t+"%"}},a)})}),t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-baseline gap-1.5",children:[t("span",{className:"font-mono text-sm font-bold tabular-nums text-slate-800",children:["−",C(T)]}),t("span",{className:"font-mono text-[10px] tabular-nums text-slate-400",children:["/ ",C(N)]})]}),e("div",{className:"flex items-center gap-0.5 rounded-full border border-slate-200 bg-white p-0.5 shadow-sm",children:d.map(e=>t("button",{type:"button",onClick:()=>M(e),className:"rounded-full px-2 py-0.5 font-mono text-[10px] font-semibold tabular-nums transition-all "+(k===e?"bg-coral-500 text-white shadow-sm":"text-slate-500 hover:bg-slate-100 hover:text-slate-700"),children:[e,"×"]},e))})]})]})]})]})}
@@ -1,12 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { ChevronLeft, ChevronRight } from 'lucide-react';
3
- import { cn } from '../../../shared/lib/utils';
4
- import Image from 'next/image';
5
- import rangsanhoImg from './assets/images/rangsanho.png';
6
- export function CambridgeYleBottomNav({ onPrevious, onNext, isFirstQuestion, isLastQuestion, isNavDisabled, className, }) {
7
- return (_jsxs("div", { className: cn("flex items-center justify-between py-2 relative", className), children: [_jsxs("button", { onClick: onPrevious, disabled: isFirstQuestion || isNavDisabled, className: cn("flex items-center gap-2 px-8 py-3 rounded-full font-bold text-[15px] transition-all duration-300 z-10", (isFirstQuestion || isNavDisabled)
8
- ? "opacity-50 cursor-not-allowed bg-white border-2 border-gray-200 text-gray-400"
9
- : "bg-white border-2 border-blue-200 text-blue-600 hover:bg-blue-50 hover:shadow-md cursor-pointer active:scale-95 shadow-sm"), children: [_jsx(ChevronLeft, { size: 20, strokeWidth: 3 }), "Previous"] }), _jsx("div", { className: "absolute left-1/2 bottom-[-4px] -translate-x-1/2 flex items-end pointer-events-none z-0", children: _jsx(Image, { src: rangsanhoImg, alt: "R\u1EB7ng san h\u00F4", className: "h-[72px] w-auto object-contain" }) }), _jsxs("button", { onClick: onNext, disabled: isLastQuestion || isNavDisabled, className: cn("flex items-center gap-2 px-10 py-3 rounded-full font-bold text-[15px] transition-all duration-300 shadow-md z-10 border-2 border-transparent", (isLastQuestion || isNavDisabled)
10
- ? "opacity-50 cursor-not-allowed bg-gray-300 text-gray-500 shadow-none"
11
- : "bg-blue-600 text-white hover:bg-blue-700 hover:shadow-lg cursor-pointer active:scale-95"), children: ["Next", _jsx(ChevronRight, { size: 20, strokeWidth: 3 })] })] }));
12
- }
1
+ import{jsx as e,jsxs as t}from"react/jsx-runtime";import{ChevronLeft as o,ChevronRight as r}from"lucide-react";import{cn as s}from"../../../shared/lib/utils";import a from"next/image";import i from"./assets/images/rangsanho.png";export function CambridgeYleBottomNav({onPrevious:n,onNext:l,isFirstQuestion:d,isLastQuestion:c,isNavDisabled:b,className:m}){return t("div",{className:s("flex items-center justify-between py-2 relative",m),children:[t("button",{onClick:n,disabled:d||b,className:s("flex items-center gap-2 px-8 py-3 rounded-full font-bold text-[15px] transition-all duration-300 z-10",d||b?"opacity-50 cursor-not-allowed bg-white border-2 border-gray-200 text-gray-400":"bg-white border-2 border-blue-200 text-blue-600 hover:bg-blue-50 hover:shadow-md cursor-pointer active:scale-95 shadow-sm"),children:[e(o,{size:20,strokeWidth:3}),"Previous"]}),e("div",{className:"absolute left-1/2 bottom-[-4px] -translate-x-1/2 flex items-end pointer-events-none z-0",children:e(a,{src:i,alt:"Rặng san hô",className:"h-[72px] w-auto object-contain"})}),t("button",{onClick:l,disabled:c||b,className:s("flex items-center gap-2 px-10 py-3 rounded-full font-bold text-[15px] transition-all duration-300 shadow-md z-10 border-2 border-transparent",c||b?"opacity-50 cursor-not-allowed bg-gray-300 text-gray-500 shadow-none":"bg-blue-600 text-white hover:bg-blue-700 hover:shadow-lg cursor-pointer active:scale-95"),children:["Next",e(r,{size:20,strokeWidth:3})]})]})}
@@ -1,18 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { CambridgeYleHeader } from './CambridgeYleHeader';
3
- import { CambridgeYleSidebar } from './CambridgeYleSidebar';
4
- import { CambridgeYleBottomNav } from './CambridgeYleBottomNav';
5
- import { CambridgeYleNavButton } from './CambridgeYleNavButton';
6
- import { cn } from '../../../shared/lib/utils';
7
- import backgroundSummerSky from '../summer-sky/assets/images/background-v2.png';
8
- import oceanFrameImg from '../summer-sky/assets/images/ocean-frame.png';
9
- export function CambridgeYleExamLayout({ children, onPrevious, onNext, onClose, isFirstQuestion, isLastQuestion, isNavDisabled, headerProps, sidebarProps, partName, }) {
10
- // No longer using dynamic decor images for title badge
11
- return (_jsxs("div", { className: "relative flex h-screen flex-col overflow-hidden bg-cover bg-center bg-no-repeat", style: {
12
- backgroundImage: `url(${backgroundSummerSky.src})`,
13
- }, children: [_jsx("div", { className: "absolute inset-0 bg-black/10" }), _jsx("div", { className: "relative z-50", children: _jsx(CambridgeYleHeader, { ...headerProps, onClose: onClose }) }), _jsxs("div", { className: "relative z-10 flex flex-1 min-h-0 items-stretch justify-center p-2 pt-0 pb-2 sm:px-4 sm:pt-0 sm:pb-4 lg:px-[2%] lg:pt-0 lg:pb-[20px] max-w-[1500px] mx-auto w-full gap-2 xl:gap-4", children: [_jsx("div", { className: "hidden xl:flex shrink-0 items-center", children: _jsx(CambridgeYleNavButton, { direction: "prev", onClick: onPrevious, disabled: isFirstQuestion || isNavDisabled }) }), _jsxs("div", { className: "flex flex-1 w-full relative drop-shadow-xl min-h-0", children: [_jsx("div", { className: "absolute inset-0 pointer-events-none", style: {
14
- background: `url(${oceanFrameImg.src}) no-repeat center center`,
15
- backgroundSize: '100% 100%',
16
- zIndex: 0
17
- } }), _jsxs("div", { className: "flex flex-1 w-full relative z-10 p-[12px] sm:p-[16px] md:p-[24px]", children: [sidebarProps && (_jsx("div", { className: "hidden lg:flex w-[30%] shrink-0 bg-[#eef7ff] rounded-l-[20px] md:rounded-l-[32px] border-r-2 border-blue-100 flex-col overflow-hidden", children: _jsx(CambridgeYleSidebar, { ...sidebarProps }) })), _jsxs("div", { className: cn("flex w-full min-h-0 flex-col flex-1 relative bg-white overflow-hidden", sidebarProps ? "rounded-r-[20px] md:rounded-r-[32px]" : "rounded-[20px] md:rounded-[32px]"), children: [_jsx("div", { id: "exam-main-scroll-area", className: "flex-1 min-h-0 overflow-auto", children: children }), _jsx("div", { className: "hidden shrink-0 bg-white p-1 sm:px-6 border-t border-blue-200/50", children: _jsx(CambridgeYleBottomNav, { onPrevious: onPrevious, onNext: onNext, isFirstQuestion: isFirstQuestion, isLastQuestion: isLastQuestion, isNavDisabled: isNavDisabled }) })] })] })] }), _jsx("div", { className: "hidden xl:flex shrink-0 items-center", children: _jsx(CambridgeYleNavButton, { direction: "next", onClick: onNext, disabled: isLastQuestion || isNavDisabled }) })] })] }));
18
- }
1
+ import{jsx as e,jsxs as r}from"react/jsx-runtime";import{CambridgeYleHeader as i}from"./CambridgeYleHeader";import{CambridgeYleSidebar as l}from"./CambridgeYleSidebar";import{CambridgeYleBottomNav as s}from"./CambridgeYleBottomNav";import{CambridgeYleNavButton as a}from"./CambridgeYleNavButton";import{cn as d}from"../../../shared/lib/utils";import o from"../summer-sky/assets/images/background-v2.png";import n from"../summer-sky/assets/images/ocean-frame.png";export function CambridgeYleExamLayout({children:t,onPrevious:m,onNext:c,onClose:p,isFirstQuestion:x,isLastQuestion:f,isNavDisabled:u,headerProps:b,sidebarProps:h,partName:v}){return r("div",{className:"relative flex h-screen flex-col overflow-hidden bg-cover bg-center bg-no-repeat",style:{backgroundImage:`url(${o.src})`},children:[e("div",{className:"absolute inset-0 bg-black/10"}),e("div",{className:"relative z-50",children:e(i,{...b,onClose:p})}),r("div",{className:"relative z-10 flex flex-1 min-h-0 items-stretch justify-center p-2 pt-0 pb-2 sm:px-4 sm:pt-0 sm:pb-4 lg:px-[2%] lg:pt-0 lg:pb-[20px] max-w-[1500px] mx-auto w-full gap-2 xl:gap-4",children:[e("div",{className:"hidden xl:flex shrink-0 items-center",children:e(a,{direction:"prev",onClick:m,disabled:x||u})}),r("div",{className:"flex flex-1 w-full relative drop-shadow-xl min-h-0",children:[e("div",{className:"absolute inset-0 pointer-events-none",style:{background:`url(${n.src}) no-repeat center center`,backgroundSize:"100% 100%",zIndex:0}}),r("div",{className:"flex flex-1 w-full relative z-10 p-[12px] sm:p-[16px] md:p-[24px]",children:[h&&e("div",{className:"hidden lg:flex w-[30%] shrink-0 bg-[#eef7ff] rounded-l-[20px] md:rounded-l-[32px] border-r-2 border-blue-100 flex-col overflow-hidden",children:e(l,{...h})}),r("div",{className:d("flex w-full min-h-0 flex-col flex-1 relative bg-white overflow-hidden",h?"rounded-r-[20px] md:rounded-r-[32px]":"rounded-[20px] md:rounded-[32px]"),children:[e("div",{id:"exam-main-scroll-area",className:"flex-1 min-h-0 overflow-auto",children:t}),e("div",{className:"hidden shrink-0 bg-white p-1 sm:px-6 border-t border-blue-200/50",children:e(s,{onPrevious:m,onNext:c,isFirstQuestion:x,isLastQuestion:f,isNavDisabled:u})})]})]})]}),e("div",{className:"hidden xl:flex shrink-0 items-center",children:e(a,{direction:"next",onClick:c,disabled:f||u})})]})]})}
@@ -1,11 +1 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- export default function CambridgeYleExampleText({ text, highlightedWord, }) {
3
- const renderText = () => {
4
- if (!highlightedWord) {
5
- return _jsx("span", { children: text });
6
- }
7
- const parts = text.split(highlightedWord);
8
- return (_jsx(_Fragment, { children: parts.map((part, index) => (_jsxs("span", { children: [part, index < parts.length - 1 && (_jsx("span", { className: "font-bold", children: highlightedWord }))] }, index))) }));
9
- };
10
- return (_jsxs("div", { className: "text-base", children: [_jsx("span", { className: "font-bold", children: "Example:" }), " ", renderText()] }));
11
- }
1
+ import{jsx as e,jsxs as n,Fragment as s}from"react/jsx-runtime";export default function t({text:t,highlightedWord:a}){return n("div",{className:"text-base",children:[e("span",{className:"font-bold",children:"Example:"})," ",(()=>{if(!a)return e("span",{children:t});const r=t.split(a);return e(s,{children:r.map((s,t)=>n("span",{children:[s,t<r.length-1&&e("span",{className:"font-bold",children:a})]},t))})})()]})}