@tinyweb_dev/oe-exam-sdk 0.1.3 → 0.1.5

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 (371) hide show
  1. package/README.md +112 -13
  2. package/dist/api/contest-rooms.d.ts +13 -0
  3. package/dist/api/contest-rooms.js +1 -0
  4. package/dist/api/contest-rooms.types.d.ts +47 -0
  5. package/dist/api/contest-rooms.types.js +1 -0
  6. package/dist/api/exam-entry.d.ts +14 -0
  7. package/dist/api/exam-entry.js +1 -0
  8. package/dist/api/exam-entry.types.d.ts +93 -0
  9. package/dist/api/exam-entry.types.js +1 -0
  10. package/dist/api/index.d.ts +2 -0
  11. package/dist/api/index.js +1 -1
  12. package/dist/api/result-review.d.ts +2 -1
  13. package/dist/api/result-review.js +1 -1
  14. package/dist/api/result-review.types.d.ts +69 -3
  15. package/dist/api/result-review.types.js +1 -1
  16. package/dist/components/exams/entry/EntryRestartDialog.d.ts +12 -0
  17. package/dist/components/exams/entry/EntryRestartDialog.js +1 -0
  18. package/dist/components/exams/entry/StudentExamEntryPageContainer.d.ts +31 -0
  19. package/dist/components/exams/entry/StudentExamEntryPageContainer.js +1 -0
  20. package/dist/components/exams/entry/exam-entry.states.d.ts +22 -0
  21. package/dist/components/exams/entry/exam-entry.states.js +1 -0
  22. package/dist/components/exams/entry/exam-entry.utils.d.ts +16 -0
  23. package/dist/components/exams/entry/exam-entry.utils.js +1 -0
  24. package/dist/components/exams/entry/fullscreen-loading.d.ts +5 -0
  25. package/dist/components/exams/entry/fullscreen-loading.js +1 -0
  26. package/dist/components/exams/entry/index.d.ts +7 -0
  27. package/dist/components/exams/entry/index.js +1 -0
  28. package/dist/components/exams/index.d.ts +8 -0
  29. package/dist/components/exams/index.js +1 -1
  30. package/dist/components/exams/rooms/StudentContestRoomsPageContainer.d.ts +18 -0
  31. package/dist/components/exams/rooms/StudentContestRoomsPageContainer.js +1 -0
  32. package/dist/components/exams/rooms/contest-rooms.utils.d.ts +32 -0
  33. package/dist/components/exams/rooms/contest-rooms.utils.js +1 -0
  34. package/dist/components/exams/rooms/index.d.ts +3 -0
  35. package/dist/components/exams/rooms/index.js +1 -0
  36. package/dist/components/exams/take/ExamTakingApiContext.d.ts +10 -0
  37. package/dist/components/exams/take/ExamTakingApiContext.js +1 -0
  38. package/dist/components/exams/take/ExamTakingPageContainer.d.ts +28 -4
  39. package/dist/components/exams/take/ExamTakingPageContainer.js +1 -1
  40. package/dist/components/exams/take/components/QuestionRenderer.d.ts +25 -0
  41. package/dist/components/exams/take/components/QuestionRenderer.js +1 -0
  42. package/dist/components/exams/take/components/index.d.ts +10 -0
  43. package/dist/components/exams/take/components/index.js +1 -0
  44. package/dist/components/exams/take/components/question-renderers/ArticlesViewer.d.ts +7 -0
  45. package/dist/components/exams/take/components/question-renderers/ArticlesViewer.js +1 -0
  46. package/dist/components/exams/take/components/question-renderers/MoversAnswerTheQuestionRenderer.d.ts +22 -0
  47. package/dist/components/exams/take/components/question-renderers/MoversAnswerTheQuestionRenderer.js +1 -0
  48. package/dist/components/exams/take/components/question-renderers/MoversChooseAdjectiveRenderer.d.ts +14 -0
  49. package/dist/components/exams/take/components/question-renderers/MoversChooseAdjectiveRenderer.js +1 -0
  50. package/dist/components/exams/take/components/question-renderers/MoversChooseBestAnswerRenderer.d.ts +2 -0
  51. package/dist/components/exams/take/components/question-renderers/MoversChooseBestAnswerRenderer.js +1 -0
  52. package/dist/components/exams/take/components/question-renderers/MoversColoringRenderer.d.ts +3 -0
  53. package/dist/components/exams/take/components/question-renderers/MoversColoringRenderer.js +1 -0
  54. package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.d.ts +2 -0
  55. package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.js +1 -0
  56. package/dist/components/exams/take/components/question-renderers/MoversGridFillRenderer.d.ts +2 -0
  57. package/dist/components/exams/take/components/question-renderers/MoversGridFillRenderer.js +1 -0
  58. package/dist/components/exams/take/components/question-renderers/MoversLabelThePictureRenderer.d.ts +2 -0
  59. package/dist/components/exams/take/components/question-renderers/MoversLabelThePictureRenderer.js +1 -0
  60. package/dist/components/exams/take/components/question-renderers/MoversListenAndWriteRenderer.d.ts +21 -0
  61. package/dist/components/exams/take/components/question-renderers/MoversListenAndWriteRenderer.js +1 -0
  62. package/dist/components/exams/take/components/question-renderers/MoversMatchByWritingAnswerRenderer.d.ts +19 -0
  63. package/dist/components/exams/take/components/question-renderers/MoversMatchByWritingAnswerRenderer.js +1 -0
  64. package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.d.ts +2 -0
  65. package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.js +1 -0
  66. package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.d.ts +2 -0
  67. package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.js +1 -0
  68. package/dist/components/exams/take/components/question-renderers/MoversReadingPassageRenderer.d.ts +2 -0
  69. package/dist/components/exams/take/components/question-renderers/MoversReadingPassageRenderer.js +1 -0
  70. package/dist/components/exams/take/components/question-renderers/MoversTrueFalseRenderer.d.ts +20 -0
  71. package/dist/components/exams/take/components/question-renderers/MoversTrueFalseRenderer.js +1 -0
  72. package/dist/components/exams/take/components/question-renderers/MoversWordFillParagraphRenderer.d.ts +32 -0
  73. package/dist/components/exams/take/components/question-renderers/MoversWordFillParagraphRenderer.js +1 -0
  74. package/dist/components/exams/take/components/question-renderers/MoversWordFillStructuredFormRenderer.d.ts +18 -0
  75. package/dist/components/exams/take/components/question-renderers/MoversWordFillStructuredFormRenderer.js +1 -0
  76. package/dist/components/exams/take/components/question-renderers/MoversWordOrderingRenderer.d.ts +19 -0
  77. package/dist/components/exams/take/components/question-renderers/MoversWordOrderingRenderer.js +1 -0
  78. package/dist/components/exams/take/components/question-renderers/MoversWriteSentencesRenderer.d.ts +22 -0
  79. package/dist/components/exams/take/components/question-renderers/MoversWriteSentencesRenderer.js +1 -0
  80. package/dist/components/exams/take/components/question-renderers/MoversWritingRenderer.d.ts +18 -0
  81. package/dist/components/exams/take/components/question-renderers/MoversWritingRenderer.js +1 -0
  82. package/dist/components/exams/take/components/question-renderers/index.d.ts +19 -0
  83. package/dist/components/exams/take/components/question-renderers/index.js +1 -0
  84. package/dist/components/exams/take/components/speaking/FindObjectInPicture.module.css +133 -0
  85. package/dist/components/exams/take/components/speaking/SpeakingAudioPlayer.d.ts +9 -0
  86. package/dist/components/exams/take/components/speaking/SpeakingAudioPlayer.js +1 -0
  87. package/dist/components/exams/take/components/speaking/SpeakingCompareImages.module.css +153 -0
  88. package/dist/components/exams/take/components/speaking/SpeakingContentFrame.d.ts +7 -0
  89. package/dist/components/exams/take/components/speaking/SpeakingContentFrame.js +1 -0
  90. package/dist/components/exams/take/components/speaking/SpeakingDragObjects.module.css +238 -0
  91. package/dist/components/exams/take/components/speaking/SpeakingExamLayout.d.ts +27 -0
  92. package/dist/components/exams/take/components/speaking/SpeakingExamLayout.js +1 -0
  93. package/dist/components/exams/take/components/speaking/SpeakingHeader.d.ts +6 -0
  94. package/dist/components/exams/take/components/speaking/SpeakingHeader.js +1 -0
  95. package/dist/components/exams/take/components/speaking/SpeakingInfoExchange.module.css +193 -0
  96. package/dist/components/exams/take/components/speaking/SpeakingInfoExchangeSummerSky.module.css +238 -0
  97. package/dist/components/exams/take/components/speaking/SpeakingInstructionBar.d.ts +5 -0
  98. package/dist/components/exams/take/components/speaking/SpeakingInstructionBar.js +1 -0
  99. package/dist/components/exams/take/components/speaking/SpeakingMicButton.d.ts +14 -0
  100. package/dist/components/exams/take/components/speaking/SpeakingMicButton.js +1 -0
  101. package/dist/components/exams/take/components/speaking/SpeakingNavButton.d.ts +7 -0
  102. package/dist/components/exams/take/components/speaking/SpeakingNavButton.js +1 -0
  103. package/dist/components/exams/take/components/speaking/SpeakingPartBadge.d.ts +6 -0
  104. package/dist/components/exams/take/components/speaking/SpeakingPartBadge.js +1 -0
  105. package/dist/components/exams/take/components/speaking/SpeakingReadDisplayedContent.module.css +115 -0
  106. package/dist/components/exams/take/components/speaking/SpeakingRecordingPreview.d.ts +11 -0
  107. package/dist/components/exams/take/components/speaking/SpeakingRecordingPreview.js +1 -0
  108. package/dist/components/exams/take/components/speaking/SpeakingReportErrorButton.d.ts +1 -0
  109. package/dist/components/exams/take/components/speaking/SpeakingReportErrorButton.js +1 -0
  110. package/dist/components/exams/take/components/speaking/SpeakingStoryImages.module.css +231 -0
  111. package/dist/components/exams/take/components/speaking/SpeakingVideoIntro.d.ts +13 -0
  112. package/dist/components/exams/take/components/speaking/SpeakingVideoIntro.js +1 -0
  113. package/dist/components/exams/take/components/speaking/index.d.ts +1 -0
  114. package/dist/components/exams/take/components/speaking/index.js +1 -0
  115. package/dist/components/exams/take/components/speaking/renderers/SpeakingChooseAnswerGroupRenderer.d.ts +16 -0
  116. package/dist/components/exams/take/components/speaking/renderers/SpeakingChooseAnswerGroupRenderer.js +1 -0
  117. package/dist/components/exams/take/components/speaking/renderers/SpeakingChooseObjectsRenderer.d.ts +16 -0
  118. package/dist/components/exams/take/components/speaking/renderers/SpeakingChooseObjectsRenderer.js +1 -0
  119. package/dist/components/exams/take/components/speaking/renderers/SpeakingCompareImagesRenderer.d.ts +17 -0
  120. package/dist/components/exams/take/components/speaking/renderers/SpeakingCompareImagesRenderer.js +1 -0
  121. package/dist/components/exams/take/components/speaking/renderers/SpeakingConversationRenderer.d.ts +16 -0
  122. package/dist/components/exams/take/components/speaking/renderers/SpeakingConversationRenderer.js +1 -0
  123. package/dist/components/exams/take/components/speaking/renderers/SpeakingCueCardRenderer.d.ts +17 -0
  124. package/dist/components/exams/take/components/speaking/renderers/SpeakingCueCardRenderer.js +1 -0
  125. package/dist/components/exams/take/components/speaking/renderers/SpeakingDescribeImageRenderer.d.ts +18 -0
  126. package/dist/components/exams/take/components/speaking/renderers/SpeakingDescribeImageRenderer.js +1 -0
  127. package/dist/components/exams/take/components/speaking/renderers/SpeakingDragObjectsRenderer.d.ts +16 -0
  128. package/dist/components/exams/take/components/speaking/renderers/SpeakingDragObjectsRenderer.js +1 -0
  129. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.d.ts +16 -0
  130. package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.js +1 -0
  131. package/dist/components/exams/take/components/speaking/renderers/SpeakingInfoExchangeRenderer.d.ts +16 -0
  132. package/dist/components/exams/take/components/speaking/renderers/SpeakingInfoExchangeRenderer.js +1 -0
  133. package/dist/components/exams/take/components/speaking/renderers/SpeakingListenAndSpeakAnswerRenderer.d.ts +16 -0
  134. package/dist/components/exams/take/components/speaking/renderers/SpeakingListenAndSpeakAnswerRenderer.js +1 -0
  135. package/dist/components/exams/take/components/speaking/renderers/SpeakingOddOneOutRenderer.d.ts +16 -0
  136. package/dist/components/exams/take/components/speaking/renderers/SpeakingOddOneOutRenderer.js +1 -0
  137. package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionListRenderer.d.ts +26 -0
  138. package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionListRenderer.js +1 -0
  139. package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionRenderer.d.ts +32 -0
  140. package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionRenderer.js +1 -0
  141. package/dist/components/exams/take/components/speaking/renderers/SpeakingReadDisplayedContentRenderer.d.ts +17 -0
  142. package/dist/components/exams/take/components/speaking/renderers/SpeakingReadDisplayedContentRenderer.js +1 -0
  143. package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQARenderer.d.ts +17 -0
  144. package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQARenderer.js +1 -0
  145. package/dist/components/exams/take/components/speaking/renderers/SpeakingStoryImagesRenderer.d.ts +17 -0
  146. package/dist/components/exams/take/components/speaking/renderers/SpeakingStoryImagesRenderer.js +1 -0
  147. package/dist/components/exams/take/components/speaking/renderers/index.d.ts +13 -0
  148. package/dist/components/exams/take/components/speaking/renderers/index.js +1 -0
  149. package/dist/components/exams/take/hooks/index.d.ts +2 -0
  150. package/dist/components/exams/take/hooks/index.js +1 -0
  151. package/dist/components/exams/take/hooks/useAnswerAutosave.d.ts +27 -0
  152. package/dist/components/exams/take/hooks/useAnswerAutosave.js +1 -0
  153. package/dist/components/exams/take/hooks/useAttemptSubmit.d.ts +29 -0
  154. package/dist/components/exams/take/hooks/useAttemptSubmit.js +1 -0
  155. package/dist/components/exams/take/hooks/useAutoSave.d.ts +20 -0
  156. package/dist/components/exams/take/hooks/useAutoSave.js +1 -0
  157. package/dist/components/exams/take/hooks/useExamCountdown.d.ts +22 -0
  158. package/dist/components/exams/take/hooks/useExamCountdown.js +1 -0
  159. package/dist/components/exams/take/hooks/useExamTimer.d.ts +21 -0
  160. package/dist/components/exams/take/hooks/useExamTimer.js +1 -0
  161. package/dist/components/exams/take/hooks/useProctoringCamera.d.ts +19 -0
  162. package/dist/components/exams/take/hooks/useProctoringCamera.js +1 -0
  163. package/dist/components/exams/take/hooks/useProctoringSnapshot.d.ts +25 -0
  164. package/dist/components/exams/take/hooks/useProctoringSnapshot.js +1 -0
  165. package/dist/components/exams/take/hooks/useSpeakingConversationAudio.d.ts +19 -0
  166. package/dist/components/exams/take/hooks/useSpeakingConversationAudio.js +1 -0
  167. package/dist/components/exams/take/hooks/useSpeakingNavigation.d.ts +37 -0
  168. package/dist/components/exams/take/hooks/useSpeakingNavigation.js +1 -0
  169. package/dist/components/exams/take/hooks/useSpeakingRecorder.d.ts +45 -5
  170. package/dist/components/exams/take/hooks/useSpeakingRecorder.js +1 -1
  171. package/dist/components/exams/take/hooks/useSpeakingRendererSetup.d.ts +39 -0
  172. package/dist/components/exams/take/hooks/useSpeakingRendererSetup.js +1 -0
  173. package/dist/components/exams/take/hooks/useToeicAudio.d.ts +17 -0
  174. package/dist/components/exams/take/hooks/useToeicAudio.js +1 -0
  175. package/dist/components/exams/take/index.d.ts +9 -1
  176. package/dist/components/exams/take/index.js +1 -1
  177. package/dist/components/exams/take/types/speaking-cue-card.types.d.ts +10 -0
  178. package/dist/components/exams/take/types/speaking-cue-card.types.js +1 -0
  179. package/dist/components/exams/take/types/speaking.types.d.ts +153 -0
  180. package/dist/components/exams/take/types/speaking.types.js +1 -0
  181. package/dist/components/exams/take/types.d.ts +286 -1
  182. package/dist/components/exams/take/utils/answer-transformers.d.ts +26 -0
  183. package/dist/components/exams/take/utils/answer-transformers.js +1 -0
  184. package/dist/components/exams/take/utils/coloring-assignments.d.ts +5 -0
  185. package/dist/components/exams/take/utils/coloring-assignments.js +1 -0
  186. package/dist/components/exams/take/utils/question-transformers.d.ts +362 -0
  187. package/dist/components/exams/take/utils/question-transformers.js +1 -0
  188. package/dist/components/features/exam-entry/entry-state-cards.d.ts +19 -0
  189. package/dist/components/features/exam-entry/entry-state-cards.js +1 -0
  190. package/dist/components/features/exam-entry/index.d.ts +2 -0
  191. package/dist/components/features/exam-entry/index.js +1 -0
  192. package/dist/components/features/exam-entry/themed/CambridgeYleWaitingRoom.d.ts +12 -0
  193. package/dist/components/features/exam-entry/themed/CambridgeYleWaitingRoom.js +1 -0
  194. package/dist/components/features/exam-entry/themed/ThemedWaitingRoom.d.ts +14 -0
  195. package/dist/components/features/exam-entry/themed/ThemedWaitingRoom.js +1 -0
  196. package/dist/components/features/exam-entry/themed/ToeicWaitingRoom.d.ts +16 -0
  197. package/dist/components/features/exam-entry/themed/ToeicWaitingRoom.js +1 -0
  198. package/dist/components/features/exam-entry/themed/components/CloseButton.d.ts +9 -0
  199. package/dist/components/features/exam-entry/themed/components/CloseButton.js +1 -0
  200. package/dist/components/features/exam-entry/themed/components/ExamInfoPanel.d.ts +13 -0
  201. package/dist/components/features/exam-entry/themed/components/ExamInfoPanel.js +1 -0
  202. package/dist/components/features/exam-entry/themed/components/ModeSelector.d.ts +7 -0
  203. package/dist/components/features/exam-entry/themed/components/ModeSelector.js +1 -0
  204. package/dist/components/features/exam-entry/themed/components/StartButton.d.ts +11 -0
  205. package/dist/components/features/exam-entry/themed/components/StartButton.js +1 -0
  206. package/dist/components/features/exam-entry/themed/components/index.d.ts +4 -0
  207. package/dist/components/features/exam-entry/themed/components/index.js +1 -0
  208. package/dist/components/features/exam-entry/themed/index.d.ts +1 -0
  209. package/dist/components/features/exam-entry/themed/index.js +1 -0
  210. package/dist/components/results/ResultReviewPageContainer.d.ts +16 -2
  211. package/dist/components/results/ResultReviewPageContainer.js +1 -1
  212. package/dist/components/results/ReviewQuestionRenderer.d.ts +11 -1
  213. package/dist/components/results/ReviewQuestionRenderer.js +1 -1
  214. package/dist/components/results/hooks/useResultReviewPolling.d.ts +37 -0
  215. package/dist/components/results/hooks/useResultReviewPolling.js +1 -0
  216. package/dist/components/results/index.d.ts +36 -1
  217. package/dist/components/results/index.js +1 -1
  218. package/dist/components/results/renderers/QuestionExplanations.d.ts +22 -0
  219. package/dist/components/results/renderers/QuestionExplanations.js +1 -0
  220. package/dist/components/results/renderers/ReviewChooseAdjectiveRenderer.d.ts +13 -0
  221. package/dist/components/results/renderers/ReviewChooseAdjectiveRenderer.js +1 -0
  222. package/dist/components/results/renderers/ReviewChooseBestAnswerRenderer.d.ts +22 -0
  223. package/dist/components/results/renderers/ReviewChooseBestAnswerRenderer.js +1 -0
  224. package/dist/components/results/renderers/ReviewColoringRenderer.d.ts +16 -0
  225. package/dist/components/results/renderers/ReviewColoringRenderer.js +1 -0
  226. package/dist/components/results/renderers/ReviewListenAndWriteRenderer.d.ts +18 -0
  227. package/dist/components/results/renderers/ReviewListenAndWriteRenderer.js +1 -0
  228. package/dist/components/results/renderers/ReviewListenChooseAnswerGroupRenderer.d.ts +25 -0
  229. package/dist/components/results/renderers/ReviewListenChooseAnswerGroupRenderer.js +1 -0
  230. package/dist/components/results/renderers/ReviewListenChooseObjectsRenderer.d.ts +25 -0
  231. package/dist/components/results/renderers/ReviewListenChooseObjectsRenderer.js +1 -0
  232. package/dist/components/results/renderers/ReviewListenDragObjectsRenderer.d.ts +25 -0
  233. package/dist/components/results/renderers/ReviewListenDragObjectsRenderer.js +1 -0
  234. package/dist/components/results/renderers/ReviewListeningHeader.d.ts +44 -0
  235. package/dist/components/results/renderers/ReviewListeningHeader.js +1 -0
  236. package/dist/components/results/renderers/ReviewSpeakingAIPanel.d.ts +17 -0
  237. package/dist/components/results/renderers/ReviewSpeakingAIPanel.js +1 -0
  238. package/dist/components/results/renderers/ReviewSpeakingMedia.d.ts +39 -0
  239. package/dist/components/results/renderers/ReviewSpeakingMedia.js +1 -0
  240. package/dist/components/results/renderers/ReviewSpeakingQuestionItem.d.ts +5 -0
  241. package/dist/components/results/renderers/ReviewSpeakingQuestionItem.js +1 -0
  242. package/dist/components/results/renderers/ReviewSpeakingRenderer.d.ts +3 -0
  243. package/dist/components/results/renderers/ReviewSpeakingRenderer.js +1 -0
  244. package/dist/components/results/renderers/ReviewSpeakingRenderer.types.d.ts +80 -0
  245. package/dist/components/results/renderers/ReviewSpeakingRenderer.types.js +1 -0
  246. package/dist/components/results/renderers/review-question-body-dedicated.d.ts +15 -0
  247. package/dist/components/results/renderers/review-question-body-dedicated.js +1 -0
  248. package/dist/components/results/renderers/review-question-body-movers.d.ts +17 -0
  249. package/dist/components/results/renderers/review-question-body-movers.js +1 -0
  250. package/dist/components/results/renderers/review-speaking-map.d.ts +7 -0
  251. package/dist/components/results/renderers/review-speaking-map.js +1 -0
  252. package/dist/components/results/report/BadgeTitle.d.ts +5 -0
  253. package/dist/components/results/report/BadgeTitle.js +1 -0
  254. package/dist/components/results/report/ExamSkillRow.d.ts +14 -0
  255. package/dist/components/results/report/ExamSkillRow.js +1 -0
  256. package/dist/components/results/report/OceanEduExamReport.d.ts +31 -0
  257. package/dist/components/results/report/OceanEduExamReport.js +1 -0
  258. package/dist/components/results/report/OceanEduSpeakingReport.d.ts +26 -0
  259. package/dist/components/results/report/OceanEduSpeakingReport.js +1 -0
  260. package/dist/components/results/report/OverallAssessmentSection.d.ts +24 -0
  261. package/dist/components/results/report/OverallAssessmentSection.js +1 -0
  262. package/dist/components/results/report/ResultErrorState.d.ts +14 -0
  263. package/dist/components/results/report/ResultErrorState.js +1 -0
  264. package/dist/components/results/report/ResultLoadingState.d.ts +11 -0
  265. package/dist/components/results/report/ResultLoadingState.js +1 -0
  266. package/dist/components/results/report/ResultNotPublicState.d.ts +16 -0
  267. package/dist/components/results/report/ResultNotPublicState.js +1 -0
  268. package/dist/components/results/report/SpeakingReportParts.d.ts +4 -0
  269. package/dist/components/results/report/SpeakingReportParts.js +1 -0
  270. package/dist/components/results/report/SpeakingReportShared.d.ts +36 -0
  271. package/dist/components/results/report/SpeakingReportShared.js +1 -0
  272. package/dist/components/results/report/SpeakingRubricSection.d.ts +21 -0
  273. package/dist/components/results/report/SpeakingRubricSection.js +1 -0
  274. package/dist/components/results/report/assets/dofu2.png +0 -0
  275. package/dist/components/results/report/assets/dofu4.png +0 -0
  276. package/dist/components/results/report/assets/logo-ocean.png +0 -0
  277. package/dist/components/results/report/assets/penguin-main.png +0 -0
  278. package/dist/components/results/report/assets/penguin-small.png +0 -0
  279. package/dist/components/results/report/speaking-report.utils.d.ts +37 -0
  280. package/dist/components/results/report/speaking-report.utils.js +1 -0
  281. package/dist/components/results/review-data.d.ts +1 -0
  282. package/dist/components/results/review-data.js +1 -1
  283. package/dist/components/results/review-status.utils.d.ts +12 -0
  284. package/dist/components/results/review-status.utils.js +1 -0
  285. package/dist/components/results/review-types.d.ts +14 -0
  286. package/dist/components/results/speaking/SpeakingResultAudioPlayer.d.ts +17 -0
  287. package/dist/components/results/speaking/SpeakingResultAudioPlayer.js +1 -0
  288. package/dist/components/results/speaking/SpeakingReviewResult.d.ts +30 -0
  289. package/dist/components/results/speaking/SpeakingReviewResult.js +1 -0
  290. package/dist/components/results/utils/review-score.utils.d.ts +21 -0
  291. package/dist/components/results/utils/review-score.utils.js +1 -0
  292. package/dist/components/results/utils/speaking-summary.d.ts +41 -0
  293. package/dist/components/results/utils/speaking-summary.js +1 -0
  294. package/dist/components/results/utils/transform-speaking-review.d.ts +62 -0
  295. package/dist/components/results/utils/transform-speaking-review.js +1 -0
  296. package/dist/components/shared/audio/SpeakingAudioPlayer.d.ts +12 -0
  297. package/dist/components/shared/audio/SpeakingAudioPlayer.js +1 -0
  298. package/dist/components/shared/audio/SpeakingAudioPlayer.module.css +115 -0
  299. package/dist/components/shared/audio/assets/fr-image-part1-border.png +0 -0
  300. package/dist/components/shared/audio/assets/ic-speaker.webp +0 -0
  301. package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.d.ts +11 -3
  302. package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +1 -1
  303. package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.d.ts +5 -1
  304. package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +1 -1
  305. package/dist/components/themes/index.d.ts +11 -0
  306. package/dist/components/themes/index.js +1 -1
  307. package/dist/components/themes/spaceship/SpaceshipAudioPlayer.d.ts +8 -0
  308. package/dist/components/themes/spaceship/SpaceshipAudioPlayer.js +1 -0
  309. package/dist/components/themes/spaceship/SpaceshipLayout.d.ts +2 -0
  310. package/dist/components/themes/spaceship/SpaceshipLayout.js +1 -0
  311. package/dist/components/themes/spaceship/assets/images/X-sunset.png +0 -0
  312. package/dist/components/themes/spaceship/assets/images/X.webp +0 -0
  313. package/dist/components/themes/spaceship/assets/images/audio-bar.webp +0 -0
  314. package/dist/components/themes/spaceship/assets/images/background.webp +0 -0
  315. package/dist/components/themes/spaceship/assets/images/bg_sunset.png +0 -0
  316. package/dist/components/themes/spaceship/assets/images/big-blue-badge-sunset.png +0 -0
  317. package/dist/components/themes/spaceship/assets/images/big-blue-badge.png +0 -0
  318. package/dist/components/themes/spaceship/assets/images/big-blue-badge.webp +0 -0
  319. package/dist/components/themes/spaceship/assets/images/bingo.webp +0 -0
  320. package/dist/components/themes/spaceship/assets/images/blue-badge-sunset.png +0 -0
  321. package/dist/components/themes/spaceship/assets/images/blue-badge.png +0 -0
  322. package/dist/components/themes/spaceship/assets/images/blue-wrapper-sunset.png +0 -0
  323. package/dist/components/themes/spaceship/assets/images/blue-wrapper.webp +0 -0
  324. package/dist/components/themes/spaceship/assets/images/board_red.png +0 -0
  325. package/dist/components/themes/spaceship/assets/images/button_feedback_vi.png +0 -0
  326. package/dist/components/themes/spaceship/assets/images/circle-btn-right-sunset.png +0 -0
  327. package/dist/components/themes/spaceship/assets/images/circle-btn-right.png +0 -0
  328. package/dist/components/themes/spaceship/assets/images/circle-btn-sunset.png +0 -0
  329. package/dist/components/themes/spaceship/assets/images/circle-btn.webp +0 -0
  330. package/dist/components/themes/spaceship/assets/images/focus-vector.webp +0 -0
  331. package/dist/components/themes/spaceship/assets/images/head-phone_red.png +0 -0
  332. package/dist/components/themes/spaceship/assets/images/ic-mute-sunset.png +0 -0
  333. package/dist/components/themes/spaceship/assets/images/ic-mute.png +0 -0
  334. package/dist/components/themes/spaceship/assets/images/ic-play-sunset.png +0 -0
  335. package/dist/components/themes/spaceship/assets/images/ic-play-violet.webp +0 -0
  336. package/dist/components/themes/spaceship/assets/images/ic-play.webp +0 -0
  337. package/dist/components/themes/spaceship/assets/images/micro.png +0 -0
  338. package/dist/components/themes/spaceship/assets/images/next-small.webp +0 -0
  339. package/dist/components/themes/spaceship/assets/images/next-sunset.png +0 -0
  340. package/dist/components/themes/spaceship/assets/images/next.webp +0 -0
  341. package/dist/components/themes/spaceship/assets/images/purple-circle.webp +0 -0
  342. package/dist/components/themes/spaceship/assets/images/question-badge-sunset.png +0 -0
  343. package/dist/components/themes/spaceship/assets/images/question-badge.webp +0 -0
  344. package/dist/components/themes/spaceship/assets/images/red-badge-sunset.png +0 -0
  345. package/dist/components/themes/spaceship/assets/images/red-badge.webp +0 -0
  346. package/dist/components/themes/spaceship/assets/images/red-wrapper-sunset.png +0 -0
  347. package/dist/components/themes/spaceship/assets/images/red-wrapper.webp +0 -0
  348. package/dist/components/themes/spaceship/assets/images/robot-avatar.png +0 -0
  349. package/dist/components/themes/spaceship/assets/images/sound_red.png +0 -0
  350. package/dist/components/themes/spaceship/assets/images/student-avatar.png +0 -0
  351. package/dist/components/themes/spaceship/assets/images/teacher-play-sunset.png +0 -0
  352. package/dist/components/themes/spaceship/assets/images/teacher-play.webp +0 -0
  353. package/dist/components/themes/spaceship/assets/images/timer-badge.png +0 -0
  354. package/dist/components/themes/spaceship/assets/styles/SpaceshipLayout.module.css +347 -0
  355. package/dist/components/themes/spaceship/assets/styles/SpaceshipWaitingRoom.module.css +395 -0
  356. package/dist/components/ui/badge.d.ts +1 -1
  357. package/dist/index.d.ts +4 -4
  358. package/dist/index.js +1 -1
  359. package/dist/shared/lib/stores/examTakingStore.d.ts +54 -0
  360. package/dist/shared/lib/stores/examTakingStore.js +1 -0
  361. package/dist/shared/lib/stores/gradingTransientStore.d.ts +23 -0
  362. package/dist/shared/lib/stores/gradingTransientStore.js +1 -0
  363. package/dist/shared/lib/themes/config/starters.config.d.ts +3 -0
  364. package/dist/shared/lib/themes/config/starters.config.js +1 -0
  365. package/dist/shared/lib/themes/index.d.ts +18 -0
  366. package/dist/shared/lib/themes/index.js +1 -0
  367. package/dist/shared/lib/themes/types.d.ts +39 -0
  368. package/dist/shared/lib/themes/types.js +1 -0
  369. package/dist/shared/lib/toast.d.ts +19 -0
  370. package/dist/shared/lib/toast.js +1 -0
  371. package/package.json +12 -2
@@ -1,13 +1,27 @@
1
1
  import { type ReactNode } from 'react';
2
- import type { ResultReviewSdkApi } from '../../api/result-review';
2
+ import { type ResultReviewSdkApi } from '../../api/result-review';
3
+ export declare enum PartRubricTemplateCode {
4
+ KINDY_VER3 = "PLACEMENT_TEST_KINDY_VER3",
5
+ PRESCHOOL = "PLACEMENT_TEST_PRESCHOOL"
6
+ }
3
7
  export interface ResultReviewPageContainerProps {
4
8
  attemptId: string;
5
9
  token?: string | null;
6
10
  api: ResultReviewSdkApi;
11
+ /** Disables polling and signals `body[data-pdf-ready="true"]` after loading. */
12
+ isPrintMode?: boolean;
13
+ /** Controls the native PDF action independently from API availability. */
14
+ canExportPdf?: boolean;
15
+ /** Canonical URL encoded in report QR codes. Defaults to the current URL. */
16
+ reviewUrl?: string;
17
+ /** Optional public asset prefix; bundled report assets are used by default. */
18
+ assetBaseUrl?: string;
19
+ /** Login destination used by the private-result state. */
20
+ loginHref?: string;
7
21
  onBack?: () => void;
8
22
  onNavigate?: (href: string) => void;
9
23
  topActionsSlot?: ReactNode;
10
24
  beforeReportSlot?: ReactNode;
11
25
  afterReportSlot?: ReactNode;
12
26
  }
13
- export declare function ResultReviewPageContainer({ attemptId, token, api, onBack, onNavigate, topActionsSlot, beforeReportSlot, afterReportSlot }: ResultReviewPageContainerProps): import("react").JSX.Element;
27
+ export declare function ResultReviewPageContainer({ attemptId, token, api, isPrintMode, canExportPdf, reviewUrl, assetBaseUrl, loginHref, onBack, onNavigate, topActionsSlot, beforeReportSlot, afterReportSlot, }: ResultReviewPageContainerProps): import("react").JSX.Element;
@@ -1 +1 @@
1
- "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useEffect as a,useMemo as s,useState as l}from"react";import{ChevronLeft as n,Download as r,Loader2 as i,Sparkles as c,XCircle as o}from"lucide-react";import{Button as m}from"../../components/ui/button";import{Card as d,CardContent as h,CardHeader as p,CardTitle as u}from"../../components/ui/card";import{useToast as x}from"../../shared/lib/hooks/useToast";import{transformResultAnswersForReview as f}from"./review-data";import{ReviewQuestionRenderer as b}from"./ReviewQuestionRenderer";import{unwrap as g}from"./result-review.utils";export function ResultReviewPageContainer({attemptId:c,token:b,api:y,onBack:A,onNavigate:S,topActionsSlot:R,beforeReportSlot:E,afterReportSlot:P}){const{error:j,success:$}=x(),[D,F]=l(null),[C,O]=l(null),[q,U]=l(!0),[K,L]=l(!1);a(()=>{let e=!1;return U(!0),Promise.all([y.getDetailedResult(c,b),y.getAttempt?.(c,b)]).then(([t,a])=>{e||(F(g(t)),O(a?g(a):null))}).catch(e=>j({message:"Không thể tải kết quả",description:e instanceof Error?e.message:void 0})).finally(()=>{e||U(!1)}),()=>{e=!0}},[y,c,b,j]);const M=s(()=>f(D?.answers??[],C?.exam?.template?.parts),[D?.answers,C?.exam?.template?.parts]),Q=C?.studentName??"Học sinh",T=C?.studentUsername,X=C?.branchName??"Chưa tham gia chi nhánh",H=C?.exam?.template?.title??D?.examName??"Kết quả bài thi",I=()=>{A?A():S?S("/student/results"):"undefined"!=typeof window&&window.history.back()},B=async()=>{if(D&&y.exportPdf){L(!0);try{const{blob:e,filename:t}=await y.exportPdf(c,C?.exam?.template?.type);!function(e,t){const a=URL.createObjectURL(e),s=document.createElement("a");s.href=a,s.download=t,document.body.appendChild(s),s.click(),document.body.removeChild(s),URL.revokeObjectURL(a)}(e,t),$({message:"Xuất PDF thành công"})}catch(e){j({message:"Không xuất được PDF",description:e instanceof Error?e.message:void 0})}finally{L(!1)}}};return q?e(k,{icon:e(i,{className:"h-8 w-8 animate-spin text-[#2874A6]"}),title:"Đang tải kết quả bài thi..."}):D?e("div",{className:"min-h-screen bg-[#e0e0e0] py-4 md:py-6",children:t("div",{className:"mx-auto flex w-full max-w-[1160px] flex-col gap-4 px-3 sm:px-4",children:[t("div",{className:"flex items-center justify-between gap-3 print:hidden",children:[t(m,{variant:"outline",onClick:I,children:[e(n,{className:"mr-2 h-4 w-4"}),"Quay lại"]}),t("div",{className:"flex items-center gap-2",children:[y.exportPdf&&t(m,{onClick:()=>{B()},disabled:K,className:"bg-[#00AEEF] hover:bg-[#0099D6]",children:[K?e(i,{className:"mr-2 h-4 w-4 animate-spin"}):e(r,{className:"mr-2 h-4 w-4"}),"Xuất PDF"]}),R]})]}),E,t(d,{className:"overflow-hidden rounded-[28px] border-0 shadow-xl",children:[t(p,{className:"bg-gradient-to-r from-[#0054A6] to-[#00AEEF] text-white",children:[e(u,{className:"text-2xl",children:H}),t("p",{className:"text-sm text-white/80",children:[Q,T?` ${T}`:""," ",X]})]}),t(h,{className:"space-y-6 p-5 md:p-8",children:[e(N,{detail:D}),e(w,{detail:D}),!1!==D.showDetailedResults&&e(v,{reviewData:M})]})]}),P]})}):e(k,{icon:e(o,{className:"h-8 w-8 text-red-400"}),title:"Không thể tải kết quả",action:e(m,{onClick:I,children:"Quay lại"})})}function N({detail:a}){return t("div",{className:"grid gap-3 md:grid-cols-4",children:[e(y,{label:"Tổng điểm",value:`${A(a.totalScore)} / ${A(a.maxScore)}`}),e(y,{label:"Câu đúng",value:`${a.correctAnswers} / ${a.totalQuestions}`}),e(y,{label:"Xếp hạng",value:a.rank?`${a.rank}/${a.totalStudents??"-"}`:"-"}),e(y,{label:"Trạng thái",value:a.isPendingGrading?"Đang chấm":"Hoàn tất"})]})}function w({detail:a}){return a.llmOverallAssessment||a.llmSkillAssessment?t("section",{className:"rounded-2xl border border-[#00AEEF]/30 bg-[#f0f8ff] p-4",children:[t("div",{className:"mb-2 flex items-center gap-2 font-bold text-[#0054A6]",children:[e(c,{className:"h-5 w-5"}),"Phân tích tổng quan"]}),a.llmOverallAssessment&&e("p",{className:"whitespace-pre-wrap text-sm text-slate-700",children:a.llmOverallAssessment}),a.llmSkillAssessment&&e("div",{className:"mt-3 grid gap-2 md:grid-cols-2",children:Object.entries(a.llmSkillAssessment).map(([a,s])=>s?t("div",{className:"rounded-xl bg-white p-3 text-sm",children:[e("strong",{children:s.label}),e("p",{className:"mt-1 text-slate-600",children:s.feedback})]},a):null)})]}):null}function v({reviewData:a}){return e("div",{className:"space-y-6",children:a.parts.map((s,l)=>t("section",{className:"rounded-2xl border border-[#00AEEF] bg-white p-5",children:[t("div",{className:"mb-4 flex items-center justify-between",children:[e("h3",{className:"text-lg font-bold text-[#0054A6]",children:s.name||`Phần ${s.partNo}`}),a.partScoresMap[l]&&t("span",{className:"rounded-full bg-[#0054A6] px-3 py-1 text-sm font-bold text-white",children:[A(a.partScoresMap[l].score)," / ",A(a.partScoresMap[l].maxScore)]})]}),s.instructions&&e("p",{className:"mb-4 font-semibold text-slate-700",children:s.instructions}),e(b,{part:s,reviewData:a})]},`${s.partId}-${l}`))})}function y({label:a,value:s}){return t("div",{className:"rounded-2xl bg-slate-50 p-4",children:[e("p",{className:"text-xs font-semibold uppercase text-slate-500",children:a}),e("p",{className:"mt-1 text-xl font-black text-[#0054A6]",children:s})]})}function k({icon:a,title:s,action:l}){return e("div",{className:"flex min-h-screen items-center justify-center bg-[#e0e0e0]",children:t("div",{className:"rounded-[28px] bg-white p-8 text-center shadow-xl",children:[e("div",{className:"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-slate-50",children:a}),e("p",{className:"font-semibold text-slate-700",children:s}),l&&e("div",{className:"mt-5",children:l})]})})}function A(e){return null!=e&&Number.isFinite(e)?e<10?e.toFixed(1):String(Math.round(e)):"0"}
1
+ "use client";import{jsx as e,Fragment as t,jsxs as r}from"react/jsx-runtime";import{useCallback as n,useEffect as o,useMemo as i,useState as a}from"react";import{ChevronLeft as s,Download as l,Loader2 as c,Sparkles as d}from"lucide-react";import{ResultTemplateType as m}from"../../api/result-review";import{useToast as u}from"../../shared/lib/hooks/useToast";import{ReviewQuestionRenderer as p}from"./ReviewQuestionRenderer";import{useResultReviewPolling as f}from"./hooks/useResultReviewPolling";import{BadgeTitle as g}from"./report/BadgeTitle";import{OceanEduExamReport as x}from"./report/OceanEduExamReport";import{OverallAssessmentSection as h}from"./report/OverallAssessmentSection";import{ResultErrorState as b}from"./report/ResultErrorState";import{ResultLoadingState as w}from"./report/ResultLoadingState";import{ResultNotPublicState as S}from"./report/ResultNotPublicState";import{SpeakingRubricSection as v}from"./report/SpeakingRubricSection";import{SpeakingReviewResult as N}from"./speaking/SpeakingReviewResult";import{transformResultAnswersForReview as R}from"./review-data";import{isResultNotPublic as P,setPdfReady as k}from"./review-status.utils";import{unwrap as E}from"./result-review.utils";import{formatTotalScore as y}from"./utils/review-score.utils";import{transformSpeakingResultForReview as A}from"./utils/transform-speaking-review";export var PartRubricTemplateCode;!function(e){e.KINDY_VER3="PLACEMENT_TEST_KINDY_VER3",e.PRESCHOOL="PLACEMENT_TEST_PRESCHOOL"}(PartRubricTemplateCode||(PartRubricTemplateCode={}));const T=new Set(Object.values(PartRubricTemplateCode));export function ResultReviewPageContainer({attemptId:s,token:l,api:c,isPrintMode:d=!1,canExportPdf:p=!1,reviewUrl:g,assetBaseUrl:y,loginHref:B,onBack:U,onNavigate:F,topActionsSlot:O,beforeReportSlot:I,afterReportSlot:_}){const{error:j,success:Q}=u(),[$,M]=a(null),[H,G]=a(null),[K,V]=a(!0),[q,W]=a(null),[X,Y]=a(!1),z=n(async e=>{e&&V(!0);try{const[e,t]=await Promise.all([c.getDetailedResult(s,l),c.getAttempt?.(s,l)]);M(E(e)),G(t?E(t):null),W(null)}catch(e){W(e),P(e,Boolean(l))||j({message:"Không thể tải kết quả",description:e instanceof Error?e.message:void 0})}finally{e&&V(!1)}},[c,s,l,j]);o(()=>{z(!0)},[z]);const J=n(()=>{z(!1)},[z]),{hasPendingQuestions:Z,hasPendingLlm:ee,hasFailedLlm:te,retryLlm:re}=f({detail:$,isPrintMode:d,isLoading:K,onRefetch:J});o(()=>{if(!d||K)return;const e=requestAnimationFrame(()=>k(!0));return()=>{cancelAnimationFrame(e),k(!1)}},[d,K]);const ne=H?.exam?.template,oe=ne?.parts,ie=H?.studentName??"Học sinh",ae=H?.studentUsername??null,se=H?.branchName??"Chưa tham gia chi nhánh",le=function(e){const t=e?new Date(e):new Date;return Number.isNaN(t.getTime())?(new Date).toLocaleDateString("vi-VN"):t.toLocaleDateString("vi-VN")}($?.submittedAt),ce=i(()=>function(e,t){const r=e??("undefined"!=typeof window?window.location.href:void 0);if(!r||!t)return r;try{const e=new URL(r,"undefined"!=typeof window?window.location.origin:void 0);return e.searchParams.set("token",t),e.toString()}catch{return r}}(g,l??H?.verificationToken),[g,l,H?.verificationToken]),de=i(()=>R($?.answers??[],oe),[$?.answers,oe]),me=i(()=>$&&ne?.type===m.SPEAKING?A($,ie,oe):null,[$,ie,ne?.type,oe]),ue=!!$?.templateCode&&T.has($.templateCode),pe=i(()=>function(e){const t={};return e?.speakingRubric?.criteria?.forEach(e=>{const r=/^C(\d+)$/.exec(e.code);if(!r)return;const n=null!=e.weightedScore&&null!=e.maxScore;t[Number(r[1])]=n?{score:e.weightedScore,maxScore:e.maxScore,weighted:n}:{score:e.score,maxScore:5,weighted:n}}),t}($),[$?.speakingRubric]),fe=n(()=>{if(U)return U();F?F(H?.contestId?`/student/contests/${H.contestId}/rooms`:"/student/results"):"undefined"!=typeof window&&window.history.back()},[H?.contestId,U,F]),ge=async()=>{if($&&c.exportPdf){Y(!0);try{const{blob:e,filename:t}=await c.exportPdf(s,ne?.type);!function(e,t){const r=URL.createObjectURL(e),n=document.createElement("a");n.href=r,n.download=t,document.body.appendChild(n),n.click(),document.body.removeChild(n),URL.revokeObjectURL(r)}(e,t),Q({message:"Xuất PDF thành công"})}catch(e){j({message:"Không xuất được PDF",description:e instanceof Error?e.message:void 0})}finally{Y(!1)}}};return K?e(w,{}):P(q,Boolean(l))?e(S,{loginHref:B,assetBaseUrl:y,onLogin:F}):$?me?e(N,{reviewData:me,studentName:ie,studentCode:ae,branchName:se,examDateText:le,examTitle:ne?.title,showDetailedResults:$.showDetailedResults,speakingRubric:$.speakingRubric,reviewUrl:ce,assetBaseUrl:y,canExportPdf:p&&Boolean(c.exportPdf),isExporting:X,hasPendingQuestions:Z,onBack:fe,onExportPdf:ge,topActionsSlot:O,beforeReportSlot:I,afterReportSlot:_}):e("div",{className:"min-h-screen bg-[#e0e0e0] py-4 md:py-6",children:r("div",{className:"mx-auto flex w-full max-w-[1160px] flex-col gap-4 px-0 sm:px-4",children:[e(D,{onBack:fe,onExport:ge,canExport:p&&Boolean(c.exportPdf),isExporting:X,topActionsSlot:O}),Z?e(L,{}):null,I,e(x,{studentName:ie,studentCode:ae,branchName:se,examDateText:le,totalScore:$.totalScore,maxScore:$.maxScore,isPendingGrading:Boolean($.isPendingGrading||Z),examSubTitle:$.roomName,examTitle:ne?.title,reviewUrl:ce,assetBaseUrl:y,readingScore:$.readingScore,maxReadingScore:$.maxReadingScore,listeningScore:$.listeningScore,maxListeningScore:$.maxListeningScore,writingScore:$.writingScore,maxWritingScore:$.maxWritingScore,speakingScore:$.speakingScore,maxSpeakingScore:$.maxSpeakingScore,overallAssessmentSlot:r(t,{children:[e(h,{isPending:ee,isFailed:te,onRetry:re,llmOverallAssessment:$.llmOverallAssessment,llmSkillAssessment:$.llmSkillAssessment,correctAnswers:$.correctAnswers,totalQuestions:$.totalQuestions}),$.speakingRubric?.criteria?.length?e(v,{rubric:$.speakingRubric}):null]}),children:!1!==$.showDetailedResults?e(C,{reviewData:de,hidePerQuestionGrading:ue,rubricScores:pe}):null}),_]})}):e(b,{error:q,onBack:fe})}function D({onBack:t,onExport:n,canExport:o,isExporting:i,topActionsSlot:a}){return r("div",{className:"flex items-center justify-between gap-3 px-3 print:hidden sm:px-0",children:[r("button",{type:"button",onClick:t,className:"inline-flex items-center gap-2 rounded-xl border border-slate-200 bg-white px-4 py-2.5 text-sm font-semibold text-slate-700 shadow-sm transition hover:bg-slate-50",children:[e(s,{className:"h-4 w-4"}),"Quay lại"]}),r("div",{className:"flex items-center gap-3",children:[o?r("button",{type:"button",onClick:()=>{n()},disabled:i,className:"inline-flex items-center gap-2 rounded-xl bg-gradient-to-r from-[#00AEEF] to-[#0099D6] px-5 py-2.5 text-sm font-bold text-white shadow-lg disabled:opacity-50",children:[i?e(c,{className:"h-4 w-4 animate-spin"}):e(l,{className:"h-4 w-4"}),i?"Đang xuất...":"Xuất PDF"]}):null,a]})]})}function L(){return r("div",{className:"mx-3 flex items-center gap-3 rounded-2xl border border-[#b3dfff] bg-gradient-to-r from-[#e8f6ff] to-[#f0f8ff] px-5 py-4 shadow-sm sm:mx-0",children:[e("div",{className:"flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-[#d0edff]",children:e(d,{className:"h-5 w-5 animate-pulse text-[#00AEEF]"})}),r("div",{children:[e("p",{className:"text-sm font-bold text-[#0054A6]",children:"AI đang chấm điểm..."}),e("p",{className:"text-xs text-[#0078c8]",children:"Trang sẽ tự động cập nhật cho đến khi hoàn tất."})]})]})}function C({reviewData:t,hidePerQuestionGrading:n,rubricScores:o}){return e("div",{className:"space-y-6",children:t.parts.map((i,a)=>{const s=n?o[i.partNo]:void 0,l=t.partScoresMap[a],c=s?`${s.weighted?Number(s.score.toFixed(1)):Math.round(s.score)} / ${Math.round(s.maxScore)}`:l?`${y(l.score)} / ${y(l.maxScore)}`:null;return r("section",{className:"relative rounded-[20px] border-2 border-[#00AEEF] bg-white px-5 pb-6 pt-11 md:px-8",children:[e(g,{number:String(i.partNo),title:i.name||`PHẦN ${i.partNo}`}),r("div",{className:"mb-6 flex flex-col gap-4",children:[c?e("div",{className:"flex justify-end",children:r("div",{className:"rounded-full bg-[#0054A6] px-4 py-2 text-sm font-bold text-white",children:["Điểm: ",c]})}):null,i.instructions?e(B,{value:i.instructions}):null]}),e(p,{part:i,reviewData:t,hidePerQuestionGrading:n})]},`${i.partId}-${a}`)})})}function B({value:t}){return/<[a-z][\s\S]*>/i.test(t)?e("div",{className:"text-[17px] font-bold leading-relaxed text-[#0054A6] [&_p]:mb-2 [&_p:last-child]:mb-0",dangerouslySetInnerHTML:{__html:t}}):e("h3",{className:"text-[17px] font-bold leading-relaxed text-[#0054A6]",children:t})}
@@ -1,6 +1,16 @@
1
+ /**
2
+ * ReviewQuestionRenderer - full FE parity dispatcher.
3
+ * Source: oe-exam-fe ReviewQuestionRenderer.tsx
4
+ *
5
+ * Public props keep `{ part, reviewData }` and add hidePerQuestionGrading.
6
+ */
1
7
  import type { ReviewQuestionPart, ReviewTransformResult } from './review-types';
2
8
  export interface ReviewQuestionRendererProps {
3
9
  part: ReviewQuestionPart;
4
10
  reviewData: ReviewTransformResult;
11
+ /**
12
+ * Hide per-question grading for speaking questions (KINDY VER3 / PRESCHOOL).
13
+ */
14
+ hidePerQuestionGrading?: boolean;
5
15
  }
6
- export declare function ReviewQuestionRenderer({ part, reviewData }: ReviewQuestionRendererProps): import("react").JSX.Element;
16
+ export declare function ReviewQuestionRenderer(props: ReviewQuestionRendererProps): import("react").JSX.Element;
@@ -1 +1 @@
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
+ "use client";import{jsx as e,Fragment as r,jsxs as t}from"react/jsx-runtime";import{QuestionExplanations as s}from"./renderers/QuestionExplanations";import{renderDedicatedReviewBody as n}from"./renderers/review-question-body-dedicated";import{renderMoversReviewBody as a}from"./renderers/review-question-body-movers";const o=new Set(["READ_DISPLAYED_CONTENT","SPEAKING_DESCRIBE_IMAGE","SPEAKING_CUE_CARD","SPONTANEOUS_QA","LISTEN_AND_SPEAK_ANSWER","LISTEN_AND_SPEAK_IMAGE_GROUP","LISTEN_AND_SPEAK_QUESTION_LIST","LISTEN_AND_SPEAK_COMPARE_IMAGES","LISTEN_AND_SPEAK_WITH_STORY_IMAGES","LISTEN_AND_SPEAK_INFO_EXCHANGE","LISTEN_AND_SPEAK_ODD_ONE_OUT"]);export function ReviewQuestionRenderer(n){const{part:a}=n,c=a.questions.map(e=>({id:e.id,questionNumber:e.question_number,explanation:e.explanation??null}));return t(r,{children:[i(n),!o.has(a.questionType)&&e(s,{items:c})]})}function i({part:r,reviewData:s,hidePerQuestionGrading:o=!1}){const i=n({part:r,answers:s.studentAnswers,isCorrectMap:s.isCorrectMap,correctAnswerTextMap:s.correctAnswerTextMap,correctOptionIdMap:s.correctOptionIdMap,rawCorrectAnswerMap:s.rawCorrectAnswerMap,questionScoreMap:s.questionScoreMap,hidePerQuestionGrading:o});if(i)return i;const c=a({part:r,answers:s.studentAnswers,isCorrectMap:s.isCorrectMap,correctAnswerTextMap:s.correctAnswerTextMap,correctOptionIdMap:s.correctOptionIdMap,cellAnswersMap:s.cellAnswersMap,essayGradingMap:s.essayGradingMap,rawCorrectAnswerMap:s.rawCorrectAnswerMap});return c||e("div",{className:"flex h-full items-center justify-center",children:t("div",{className:"text-center",children:[e("p",{className:"text-lg font-semibold text-red-500",children:"Unsupported question type"}),e("p",{className:"mt-2 text-gray-500",children:r.questionType})]})})}
@@ -0,0 +1,37 @@
1
+ import type { ResultReviewDetail } from '../../../api/result-review';
2
+ export interface UseResultReviewPollingOptions {
3
+ /** Latest loaded result detail (null while first load is in flight). */
4
+ detail: ResultReviewDetail | null;
5
+ /** Disable polling entirely (e.g. headless PDF print mode). */
6
+ isPrintMode: boolean;
7
+ /** Whether the initial load has finished. */
8
+ isLoading: boolean;
9
+ /** Poll interval in milliseconds. */
10
+ intervalMs?: number;
11
+ /** How long to wait for the LLM assessment before treating it as failed. */
12
+ llmTimeoutMs?: number;
13
+ /** Called on each tick to refetch the result; should be stable. */
14
+ onRefetch: () => void;
15
+ }
16
+ export interface UseResultReviewPollingResult {
17
+ /** `true` while some answers are still being auto/LLM graded. */
18
+ hasPendingQuestions: boolean;
19
+ /** `true` while the LLM overall assessment is still expected to arrive. */
20
+ hasPendingLlm: boolean;
21
+ /** `true` once the LLM assessment has not arrived within `llmTimeoutMs`. */
22
+ hasFailedLlm: boolean;
23
+ /** `true` while polling is active. */
24
+ isPolling: boolean;
25
+ /** Restarts the LLM wait window and refetches immediately. */
26
+ retryLlm: () => void;
27
+ }
28
+ /**
29
+ * Polls the result endpoint while any answer is still being graded or the LLM
30
+ * assessment hasn't been generated yet. Ports the host app's behaviour: polling
31
+ * is suppressed in print mode so the headless PDF renderer reaches an idle state.
32
+ *
33
+ * A missing assessment is indistinguishable from a failed one — the API exposes
34
+ * no status for it — so the wait is bounded by `llmTimeoutMs` and reported as
35
+ * `hasFailedLlm` once it elapses, instead of polling forever.
36
+ */
37
+ export declare function useResultReviewPolling({ detail, isPrintMode, isLoading, intervalMs, llmTimeoutMs, onRefetch, }: UseResultReviewPollingOptions): UseResultReviewPollingResult;
@@ -0,0 +1 @@
1
+ "use client";import{useCallback as e,useEffect as n,useRef as t,useState as r}from"react";const s=new Set(["PENDING","QUEUED","PROCESSING"]);export function useResultReviewPolling({detail:l,isPrintMode:o,isLoading:i,intervalMs:a=5e3,llmTimeoutMs:u=12e4,onRefetch:c}){const m=(g=l?.answers,!!g&&g.some(e=>null!=e.gradingStatus&&s.has(e.gradingStatus)));var g;const d=Boolean(l)&&!function(e){if(!e)return!1;if(e.llmOverallAssessment)return!0;const n=e.llmSkillAssessment;return Boolean(n&&Object.values(n).some(e=>Boolean(e)))}(l),[f,v]=r(!1),[P,S]=r(0),h=t(c);h.current=c,n(()=>{if(v(!1),!d||o||i)return;const e=setTimeout(()=>v(!0),u);return()=>clearTimeout(e)},[d,o,i,u,P]);const E=d&&!f,I=d&&f,L=!o&&!i&&(m||E);n(()=>{if(!L)return;const e=setInterval(()=>h.current(),a);return()=>clearInterval(e)},[a,L]);const R=e(()=>{S(e=>e+1),h.current()},[]);return{hasPendingQuestions:m,hasPendingLlm:E,hasFailedLlm:I,isPolling:L,retryLlm:R}}
@@ -1,4 +1,39 @@
1
- export { ResultReviewPageContainer } from './ResultReviewPageContainer';
1
+ export { PartRubricTemplateCode, ResultReviewPageContainer, } from './ResultReviewPageContainer';
2
2
  export type { ResultReviewPageContainerProps } from './ResultReviewPageContainer';
3
+ export { transformDetailedResultForReview, transformResultAnswersForReview, } from './review-data';
4
+ export type { PartScoreInfo, ReviewQuestionPart, ReviewTransformResult, } from './review-types';
3
5
  export { ReviewQuestionRenderer } from './ReviewQuestionRenderer';
4
6
  export type { ReviewQuestionRendererProps } from './ReviewQuestionRenderer';
7
+ export { useResultReviewPolling } from './hooks/useResultReviewPolling';
8
+ export type { UseResultReviewPollingOptions, UseResultReviewPollingResult, } from './hooks/useResultReviewPolling';
9
+ export { isResultNotPublic, setPdfReady } from './review-status.utils';
10
+ export { ResultTemplateType, ReviewGradingStatus } from '../../api/result-review';
11
+ export type { ResultReviewAnswer, ResultReviewAttempt, ResultReviewDetail, ResultReviewSdkApi, ResultReviewTemplatePart, SpeakingRubric, } from '../../api/result-review';
12
+ export { transformSpeakingResultForReview, isSpeakingTemplateType, SPEAKING_ANSWER_COMPLETED, SPEAKING_STORY_IMAGES_TYPE, } from './utils/transform-speaking-review';
13
+ export type { SpeakingReviewData, SpeakingReviewPart, SpeakingReviewQuestion, } from './utils/transform-speaking-review';
14
+ export { buildSkillMetrics, deriveFeedback, deriveQuestionSkillMetrics, deriveSkillMetrics, deriveSkillScores, deriveSummaryPoints, getOverallStatusText, getScoreLabel, SPEAKING_SKILL_COLORS, } from './utils/speaking-summary';
15
+ export type { SkillMetric, SkillScores, SpeakingSkillKey, SummaryPoint, SummaryPointIcon, } from './utils/speaking-summary';
16
+ export { SpeakingResultAudioPlayer } from './speaking/SpeakingResultAudioPlayer';
17
+ export type { SpeakingResultAudioPlayerProps, SpeakingResultAudioPlayerVariant, } from './speaking/SpeakingResultAudioPlayer';
18
+ export { SpeakingReviewResult, SPEAKING_REVIEW_POLL_INTERVAL_MS, } from './speaking/SpeakingReviewResult';
19
+ export type { SpeakingReviewResultProps } from './speaking/SpeakingReviewResult';
20
+ export { OceanEduSpeakingReport } from './report/OceanEduSpeakingReport';
21
+ export type { OceanEduSpeakingReportProps } from './report/OceanEduSpeakingReport';
22
+ export { OceanEduExamReport } from './report/OceanEduExamReport';
23
+ export type { OceanEduExamReportProps } from './report/OceanEduExamReport';
24
+ export { OverallAssessmentSection } from './report/OverallAssessmentSection';
25
+ export type { OverallAssessmentSectionProps, LlmSkillAssessmentItem, } from './report/OverallAssessmentSection';
26
+ export { ResultNotPublicState } from './report/ResultNotPublicState';
27
+ export type { ResultNotPublicStateProps } from './report/ResultNotPublicState';
28
+ export { ResultLoadingState } from './report/ResultLoadingState';
29
+ export type { ResultLoadingStateProps } from './report/ResultLoadingState';
30
+ export { ResultErrorState } from './report/ResultErrorState';
31
+ export type { ResultErrorStateProps } from './report/ResultErrorState';
32
+ export { SpeakingRubricSection } from './report/SpeakingRubricSection';
33
+ export type { SpeakingRubricSectionProps } from './report/SpeakingRubricSection';
34
+ export { SpeakingRubricScoringVersion, KidsRubricSectionCode, } from './report/SpeakingRubricSection';
35
+ export { BadgeTitle } from './report/BadgeTitle';
36
+ export type { BadgeTitleProps } from './report/BadgeTitle';
37
+ export { ReviewScoreBand, formatScore, formatTotalScore,
38
+ /** General exam score label (distinct from speaking {@link getScoreLabel}). */
39
+ getScoreLabel as getExamScoreLabel, getScoreMessage as getExamScoreMessage, getScoreStroke as getExamScoreStroke, } from './utils/review-score.utils';
@@ -1 +1 @@
1
- export{ResultReviewPageContainer}from"./ResultReviewPageContainer";export{ReviewQuestionRenderer}from"./ReviewQuestionRenderer";
1
+ export{PartRubricTemplateCode,ResultReviewPageContainer}from"./ResultReviewPageContainer";export{transformDetailedResultForReview,transformResultAnswersForReview}from"./review-data";export{ReviewQuestionRenderer}from"./ReviewQuestionRenderer";export{useResultReviewPolling}from"./hooks/useResultReviewPolling";export{isResultNotPublic,setPdfReady}from"./review-status.utils";export{ResultTemplateType,ReviewGradingStatus}from"../../api/result-review";export{transformSpeakingResultForReview,isSpeakingTemplateType,SPEAKING_ANSWER_COMPLETED,SPEAKING_STORY_IMAGES_TYPE}from"./utils/transform-speaking-review";export{buildSkillMetrics,deriveFeedback,deriveQuestionSkillMetrics,deriveSkillMetrics,deriveSkillScores,deriveSummaryPoints,getOverallStatusText,getScoreLabel,SPEAKING_SKILL_COLORS}from"./utils/speaking-summary";export{SpeakingResultAudioPlayer}from"./speaking/SpeakingResultAudioPlayer";export{SpeakingReviewResult,SPEAKING_REVIEW_POLL_INTERVAL_MS}from"./speaking/SpeakingReviewResult";export{OceanEduSpeakingReport}from"./report/OceanEduSpeakingReport";export{OceanEduExamReport}from"./report/OceanEduExamReport";export{OverallAssessmentSection}from"./report/OverallAssessmentSection";export{ResultNotPublicState}from"./report/ResultNotPublicState";export{ResultLoadingState}from"./report/ResultLoadingState";export{ResultErrorState}from"./report/ResultErrorState";export{SpeakingRubricSection}from"./report/SpeakingRubricSection";export{SpeakingRubricScoringVersion,KidsRubricSectionCode}from"./report/SpeakingRubricSection";export{BadgeTitle}from"./report/BadgeTitle";export{ReviewScoreBand,formatScore,formatTotalScore,getScoreLabel as getExamScoreLabel,getScoreMessage as getExamScoreMessage,getScoreStroke as getExamScoreStroke}from"./utils/review-score.utils";
@@ -0,0 +1,22 @@
1
+ interface ExplanationItem {
2
+ /** Question id (used as React key) */
3
+ id: string;
4
+ /** Display number shown before the explanation */
5
+ questionNumber?: number | string | null;
6
+ /** Explanation text from the questions.explanation column */
7
+ explanation?: string | null;
8
+ }
9
+ interface QuestionExplanationsProps {
10
+ /** All questions in the current part (each may carry an explanation). */
11
+ items: ExplanationItem[];
12
+ }
13
+ /**
14
+ * QuestionExplanations - shared review block that renders the per-question
15
+ * explanation (questions.explanation) for every question that has one.
16
+ *
17
+ * Rendered centrally by ReviewQuestionRenderer after the type-specific
18
+ * renderer output, so ALL question types show explanations uniformly without
19
+ * each renderer having to implement it.
20
+ */
21
+ export declare function QuestionExplanations({ items }: QuestionExplanationsProps): import("react").JSX.Element;
22
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{Lightbulb as a}from"lucide-react";export function QuestionExplanations({items:n}){const s=n.filter(e=>"string"==typeof e.explanation&&e.explanation.trim().length>0);if(0===s.length)return null;const r=s.length>1;return t("div",{className:"mt-4 rounded-xl border border-amber-200 bg-amber-50/80 p-4 shadow-sm",children:[t("div",{className:"mb-2 flex items-center gap-1.5 text-sm font-bold text-amber-700",children:[e(a,{className:"h-4 w-4"}),"Giải thích"]}),e("div",{className:"space-y-3",children:s.map(a=>t("div",{className:"text-sm leading-relaxed text-amber-900",children:[r&&null!=a.questionNumber&&t("span",{className:"mr-1.5 font-bold",children:["Câu ",a.questionNumber,"."]}),e("span",{className:"whitespace-pre-wrap",children:a.explanation})]},a.id))})]})}
@@ -0,0 +1,13 @@
1
+ import type { ChooseAdjectiveQuestion } from '../../../components/exams/take/utils/question-transformers';
2
+ interface ReviewChooseAdjectiveRendererProps {
3
+ partNumber: number;
4
+ questionCount: number;
5
+ instruction: string;
6
+ partName?: string;
7
+ questions: ChooseAdjectiveQuestion[];
8
+ answers: Record<string, string>;
9
+ isCorrectMap: Record<string, boolean | null>;
10
+ correctOptionIdMap: Record<string, string>;
11
+ }
12
+ export declare function ReviewChooseAdjectiveRenderer({ partNumber, questionCount, instruction, partName, questions, answers, isCorrectMap, correctOptionIdMap, }: ReviewChooseAdjectiveRendererProps): import("react").JSX.Element;
13
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{Check as r,X as n}from"lucide-react";import i from"../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import s from"../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";function l({question:i,selectedOptionId:s,isCorrect:l,correctOptionId:c}){const a=function(e,t){if(!t)return null;const r=e.options.find(e=>e.id===t);return r?.text||null}(i,s),o=function(e,t){if(!t)return null;const r=e.options.find(e=>e.id===t);return r?.text||null}(i,c),d=!!s,m=e=>{const t=s===e.id,r=c===e.id;return t&&!0===l?"border-green-500 bg-green-50 text-green-700":t&&!1===l?"border-red-500 bg-red-50 text-red-700":!r||!1!==l&&d?"border-gray-300 bg-white text-gray-500":"border-blue-500 bg-blue-50 text-blue-700"},u=d?!0===l?{borderColor:"#22c55e"}:{borderColor:"#ef4444"}:{borderColor:"#9ca3af"},p=d?!0===l?"correct":"incorrect":"unanswered";return t("div",{className:"mb-6",children:[t("div",{className:"mb-3 flex items-start gap-2",children:[e("span",{className:"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-sm font-bold text-white",children:i.questionNumber}),e("div",{className:"flex-1 text-base leading-relaxed text-gray-800",children:t("div",{className:"flex flex-wrap items-end gap-1",children:[i.sentenceBefore&&e("span",{children:i.sentenceBefore}),t("span",{className:"inline-block min-w-[120px]",style:{borderBottom:`2px solid ${u.borderColor}`},children:["correct"===p&&e("span",{className:"block w-full text-center",children:a}),"unanswered"===p&&e("span",{className:"block w-full text-center text-gray-400",children:"_______"}),"incorrect"===p&&t("span",{className:"flex items-center justify-center gap-2",children:[e("span",{className:"text-red-500 line-through",children:a}),e("span",{className:"text-blue-600",children:o})]})]}),i.sentenceAfter&&e("span",{children:i.sentenceAfter}),t("span",{className:"ml-2 text-sm italic text-gray-500",children:["(",i.options.map(e=>e.text).join(" / "),")"]})]})})]}),e("div",{className:"flex flex-wrap gap-2 pl-9",children:i.options.map(i=>{const a=s===i.id,o=c===i.id,u=a&&!0===l,p=a&&!1===l,f=o&&(!1===l||!d);return t("div",{className:`flex items-center gap-2 rounded-lg border-2 px-4 py-2 text-sm font-medium ${m(i)}`,children:[t("span",{children:[i.label,". ",i.text]}),u&&e("div",{className:"flex h-5 w-5 items-center justify-center rounded-full bg-green-500",children:e(r,{className:"h-3 w-3 text-white",strokeWidth:3})}),p&&e("div",{className:"flex h-5 w-5 items-center justify-center rounded-full bg-red-500",children:e(n,{className:"h-3 w-3 text-white",strokeWidth:3})}),f&&e("div",{className:"flex h-5 w-5 items-center justify-center rounded-full bg-blue-500",children:e(r,{className:"h-3 w-3 text-white",strokeWidth:3})})]},i.id)})})]})}export function ReviewChooseAdjectiveRenderer({partNumber:r,questionCount:n,instruction:c,partName:a,questions:o,answers:d,isCorrectMap:m,correctOptionIdMap:u}){return t("div",{className:"flex flex-col gap-6 h-full",children:[t("div",{className:"flex flex-col gap-4 pb-0 pt-4",children:[e(i,{partNumber:r,questionCount:n,partName:a,isReviewMode:!0}),e(s,{instruction:c,isReviewMode:!0})]}),e("div",{className:"flex flex-col gap-4 pb-6 pl-2 pt-2",children:o.map(t=>e(l,{question:t,selectedOptionId:d[t.id],isCorrect:m[t.id],correctOptionId:u[t.id]},t.id))})]})}
@@ -0,0 +1,22 @@
1
+ import type { ChooseBestAnswerQuestion, ChooseBestAnswerExample } from '../../../components/exams/take/types';
2
+ interface ReviewChooseBestAnswerRendererProps {
3
+ partNumber: number;
4
+ questionCount: number;
5
+ instruction: string;
6
+ partName?: string;
7
+ imageUrl?: string;
8
+ audioUrl?: string;
9
+ example?: ChooseBestAnswerExample;
10
+ groupContent?: string;
11
+ articles?: Array<{
12
+ name: string;
13
+ content: string;
14
+ imageUrl?: string;
15
+ }>;
16
+ questions: ChooseBestAnswerQuestion[];
17
+ answers: Record<string, string | string[]>;
18
+ isCorrectMap: Record<string, boolean | null>;
19
+ correctOptionIdMap: Record<string, string>;
20
+ }
21
+ export declare function ReviewChooseBestAnswerRenderer({ partNumber, questionCount, instruction, partName, imageUrl, audioUrl, groupContent, articles, questions, answers, isCorrectMap, correctOptionIdMap, }: ReviewChooseBestAnswerRendererProps): import("react").JSX.Element;
22
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as t,Fragment as r}from"react/jsx-runtime";import{Check as l,X as a,ZoomIn as i}from"lucide-react";import{useState as s}from"react";import{ResolvedImage as n}from"../../../components/common/ResolvedImage";import o from"../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import c from"../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";import d from"../../../components/themes/cambridge-yle/CambridgeYleAudioPlayer";function m({imageUrl:l}){const[a,o]=s(!1);return t(r,{children:[t("div",{className:"relative flex items-center justify-center",children:[e(n,{src:l,alt:"Question illustration",className:"max-h-[250px] cursor-pointer rounded-lg object-contain shadow-md",onClick:()=>o(!0)}),e("button",{type:"button",onClick:()=>o(!0),className:"absolute right-2 top-2 rounded-full bg-white/80 p-1.5 shadow-md hover:bg-white","aria-label":"Zoom image",children:e(i,{className:"h-4 w-4 text-gray-600"})})]}),a&&e("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/70",onClick:()=>o(!1),children:e(n,{src:l,alt:"Question illustration (zoomed)",className:"max-h-[90vh] max-w-[90vw] rounded-lg object-contain",showLoadingState:!1})})]})}function u({question:r,selectedOptionIds:i,isCorrect:s,correctOptionIds:o}){const c=i.length>0,d="image"===r.optionType,u=Boolean(r.multipleAnswers||o.length>1),p=e=>{const t=i.includes(e),r=o.includes(e);return t&&r?"border-green-500 bg-green-50":t&&!r?"border-red-500 bg-red-50":!r||c&&!1!==s&&t?"border-gray-300 bg-white":"border-blue-500 bg-blue-50"},h=t=>{const r=i.includes(t),n=o.includes(t),d=r&&!n,m=n&&(!c||!1===s||!r),p=u?"rounded-md":"rounded-full";return e("div",r&&n?{className:`flex h-6 w-6 items-center justify-center bg-green-500 ${p}`,children:e(l,{className:"h-4 w-4 text-white",strokeWidth:3})}:d?{className:`flex h-6 w-6 items-center justify-center bg-red-500 ${p}`,children:e(a,{className:"h-4 w-4 text-white",strokeWidth:3})}:m?{className:`flex h-6 w-6 items-center justify-center bg-blue-500 ${p}`,children:e(l,{className:"h-4 w-4 text-white",strokeWidth:3})}:{className:`flex h-5 w-5 items-center justify-center border-2 border-gray-300 bg-white ${p}`})};return t("div",{className:"mb-4",children:[t("p",{className:"mb-2 text-lg font-bold text-gray-900",children:[r.questionNumber,". ",r.questionText]}),u&&e("p",{className:"mb-3 pl-2 text-sm font-medium text-blue-600",children:"Chọn nhiều đáp án"}),r.imageUrl&&e("div",{className:"mb-3",children:e(m,{imageUrl:r.imageUrl})}),e("div",d?{className:"grid gap-3 px-2",style:{gridTemplateColumns:`repeat(${r.options.length}, minmax(0, 1fr))`},children:r.options.map(r=>t("div",{className:`relative flex flex-col items-center gap-1 rounded-lg border-2 p-2 cursor-default ${p(r.id)}`,children:[e("span",{className:"text-xs font-medium text-gray-600",children:r.label}),e(n,{src:r.text,alt:`Option ${r.label}`,className:"h-28 w-28 rounded object-contain"}),h(r.id)]},r.id))}:{className:"flex flex-col gap-2 pl-2",children:r.options.map(r=>t("div",{className:`flex items-center gap-3 rounded-lg border-2 px-3 py-2 cursor-default ${p(r.id)}`,children:[t("span",{className:"text-sm font-medium text-gray-600",children:[r.label,"."]}),e("span",{className:"flex-1 text-sm text-gray-700",children:r.text}),h(r.id)]},r.id))})]})}function p({article:r,label:l}){return t("div",{className:"rounded-lg border border-gray-200 bg-white p-4 shadow-sm",children:[t("div",{className:"mb-2 flex items-center gap-2",children:[e("span",{className:"flex h-7 w-7 items-center justify-center rounded-full bg-teal-500 text-xs font-bold text-white",children:l}),e("h3",{className:"text-sm font-bold text-gray-900",children:r.name})]}),r.imageUrl&&e("div",{className:"mb-2",children:e(n,{src:r.imageUrl,alt:r.name,className:"h-24 w-full rounded-md object-cover"})}),e("p",{className:"text-sm leading-relaxed text-gray-700 whitespace-pre-line",children:r.content})]})}export function ReviewChooseBestAnswerRenderer({partNumber:r,questionCount:l,instruction:a,partName:i,imageUrl:s,audioUrl:m,groupContent:h,articles:g,questions:x,answers:b,isCorrectMap:f,correctOptionIdMap:N}){return t("div",{className:"flex flex-col gap-6 h-full",children:[t("div",{className:"flex flex-col gap-4 pb-0 pt-4",children:[e(o,{partNumber:r,questionCount:l,partName:i,isReviewMode:!0}),e(c,{instruction:a,isReviewMode:!0}),m&&e(d,{audioSrc:m,compact:!0}),s&&e("div",{className:"flex items-center justify-center",children:e(n,{src:s,alt:"Shared illustration",className:"max-h-[300px] rounded-lg object-contain shadow-md"})}),g&&g.length>0?e("div",{className:"flex flex-col gap-3",children:g.map((t,r)=>e(p,{article:t,label:String.fromCharCode(65+r)},r))}):h?e("div",{className:"rounded-lg bg-amber-50 p-4 text-sm leading-relaxed text-gray-700 max-h-[500px] overflow-y-auto pr-2 custom-scrollbar",children:h}):null]}),e("div",{className:"grid grid-cols-1 md:grid-cols-2 gap-6 pb-6 pl-2 pt-2",children:x.map(t=>{const r=b[t.id],l=Array.isArray(r)?r:"string"==typeof r&&r?[r]:[],a=N[t.id]?N[t.id].split(",").map(e=>e.trim()).filter(Boolean):[],i=t.correctOptionIds?.length?t.correctOptionIds:a;return e(u,{question:t,selectedOptionIds:l,isCorrect:f[t.id],correctOptionIds:i},t.id)})})]})}
@@ -0,0 +1,16 @@
1
+ import type { ColoringHintItem, ColoringRegion } from '../../../components/exams/take/types';
2
+ interface ReviewColoringRendererProps {
3
+ partNumber: number;
4
+ questionCount: number;
5
+ instruction: string;
6
+ instructions?: string[];
7
+ partName?: string;
8
+ imageUrl: string;
9
+ audioUrl?: string;
10
+ hintItems: ColoringHintItem[];
11
+ regions: ColoringRegion[];
12
+ assignments: Record<string, string>;
13
+ correctAnswers: Record<string, string>;
14
+ }
15
+ export declare function ReviewColoringRenderer({ partNumber, questionCount, instruction, instructions, partName, imageUrl, audioUrl, hintItems, regions, assignments, correctAnswers, }: ReviewColoringRendererProps): import("react").JSX.Element;
16
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Check as t,X as s}from"lucide-react";import{useMemo as o,useState as n}from"react";import{usePresignedFileUrl as a}from"../../../shared/lib/hooks/usePresignedFileUrl";import i from"../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";import l from"../../../components/themes/cambridge-yle/CambridgeYleAudioPlayer";import c from"../../../components/themes/cambridge-yle/CambridgeYlePartBanner";function d({imageUrl:o,regions:i,hintItems:l,assignments:c,correctAnswers:d}){const{previewUrl:m}=a(o),[u,p]=n(null),h=e=>{if(!e)return"#333";if(!e.startsWith("#"))return"#fff";const r=e.replace("#","");if(r.length<6)return"#fff";return(.299*parseInt(r.substring(0,2),16)+.587*parseInt(r.substring(2,4),16)+.114*parseInt(r.substring(4,6),16))/255>.5?"#000":"#fff"};return r("div",{className:"relative inline-block",children:[e("img",{src:m||o,alt:"Coloring question",className:"max-h-[600px] w-full max-w-[900px] rounded-lg object-contain shadow-sm",onLoad:e=>{const r=e.currentTarget;p({width:r.naturalWidth,height:r.naturalHeight})}}),u&&i.map(o=>{const n=c[o.id],a=n?l.find(e=>e.color?.toUpperCase()===n.toUpperCase()):void 0,i=d[o.id],m=i?l.find(e=>e.color?.toUpperCase()===i.toUpperCase()):void 0,u=function(e,r,t){const s=t[e];return s?s.toUpperCase()===r.toUpperCase()?"correct":"incorrect":"unanswered"}(o.id,i||"",c),p=function(e){switch(e){case"correct":return"border-green-500";case"incorrect":return"border-red-500";case"unanswered":return"border-blue-500";default:return"border-gray-400"}}(u);return r("div",{className:`absolute rounded border-2 ${p} flex items-center justify-center`,style:{left:`${o.x}%`,top:`${o.y}%`,width:`${o.width}%`,height:`${o.height}%`,backgroundColor:a?.color||"transparent"},children:[a?.label&&e("span",{className:"z-10 text-[8px] sm:text-[10px] font-bold uppercase tracking-wide drop-shadow-sm font-[family-name:var(--font-nunito)]",style:{color:a.color?h(a.color):"#333"},children:a.label}),r("div",{className:"absolute -right-2 -top-2 z-20",children:["correct"===u&&e("div",{className:"flex h-5 w-5 items-center justify-center rounded-full bg-green-500 shadow-sm",children:e(t,{className:"h-3 w-3 text-white",strokeWidth:3})}),"incorrect"===u&&e("div",{className:"flex h-5 w-5 items-center justify-center rounded-full bg-red-500 shadow-sm",children:e(s,{className:"h-3 w-3 text-white",strokeWidth:3})})]}),"correct"!==u&&m&&r("div",{className:"absolute left-1/2 -translate-x-1/2 z-30 flex items-center gap-1 rounded shadow-md px-1.5 py-0.5 border-2 border-green-500",style:{bottom:"-22px",backgroundColor:m.color||"#fff"},children:[e(t,{className:"h-2 w-2 sm:h-2.5 sm:w-2.5",style:{color:m.color?h(m.color):"#333"},strokeWidth:4}),e("span",{className:"text-[7px] sm:text-[9px] font-bold uppercase tracking-wide whitespace-nowrap drop-shadow-sm font-[family-name:var(--font-nunito)]",style:{color:m.color?h(m.color):"#333"},children:m.label})]})]},o.id)})]})}function m({hintItems:t}){return e("div",{className:"flex flex-wrap gap-2",children:t.map(t=>r("div",{className:"flex items-center gap-2 rounded-lg border border-gray-200 px-3 py-1.5",children:[t.color&&e("div",{className:"h-5 w-5 rounded border border-gray-300",style:{backgroundColor:t.color}}),t.label&&e("span",{className:"text-sm text-gray-700",children:t.label})]},t.id))})}function u(){return r("div",{className:"flex items-center gap-2 rounded-lg bg-green-50 px-4 py-3 text-green-700",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-green-500",children:e(t,{className:"h-4 w-4 text-white",strokeWidth:3})}),e("span",{className:"font-medium",children:"All answers are correct!"})]})}export function ReviewColoringRenderer({partNumber:t,questionCount:s,instruction:n,instructions:a,partName:p,imageUrl:h,audioUrl:f,hintItems:g,regions:b,assignments:w,correctAnswers:x}){const y=o(()=>b.every(e=>{const r=w[e.id],t=x[e.id];return!(!r||!t)&&r.toUpperCase()===t.toUpperCase()}),[b,w,x]);return r("div",{className:"flex h-full flex-col gap-4 overflow-y-auto p-6",children:[e(c,{partNumber:t,questionCount:s,partName:p,isReviewMode:!0}),e(i,{instruction:n,instructions:a,isReviewMode:!0,children:f&&e(l,{audioSrc:f,compact:!0})}),y&&e(u,{}),e(m,{hintItems:g}),e("div",{className:"flex justify-center mt-4",children:e(d,{imageUrl:h,regions:b,hintItems:g,assignments:w,correctAnswers:x})})]})}
@@ -0,0 +1,18 @@
1
+ import type { FillBlankQuestion } from '../../../components/exams/take/types';
2
+ interface ReviewListenAndWriteRendererProps {
3
+ partNumber: number;
4
+ questionCount: number;
5
+ instruction: string;
6
+ partName?: string;
7
+ imageUrl?: string;
8
+ audioUrl?: string;
9
+ title: string;
10
+ exampleText?: string;
11
+ exampleHighlight?: string;
12
+ questions: FillBlankQuestion[];
13
+ answers: Record<string, string>;
14
+ isCorrectMap: Record<string, boolean | null>;
15
+ correctAnswerTextMap: Record<string, string>;
16
+ }
17
+ export declare function ReviewListenAndWriteRenderer({ partNumber, questionCount, instruction, partName, audioUrl, title, exampleText, questions, answers, isCorrectMap, correctAnswerTextMap, }: ReviewListenAndWriteRendererProps): import("react").JSX.Element;
18
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{Check as r,X as s}from"lucide-react";import{ResolvedImage as l}from"../../../components/common/ResolvedImage";import n from"../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import a from"../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";import c from"../../../components/themes/cambridge-yle/CambridgeYleAudioPlayer";export function ReviewListenAndWriteRenderer({partNumber:i,questionCount:o,instruction:d,partName:m,audioUrl:u,title:x,exampleText:f,questions:h,answers:p,isCorrectMap:N,correctAnswerTextMap:b}){const g=e=>{const t=N[e],r=p[e];return null==r||""===r?"unanswered":!0===t?"correct":!1===t?"incorrect":"unanswered"},w=e=>{switch(g(e)){case"correct":return"#22c55e";case"incorrect":return"#ef4444";case"unanswered":return"#3b82f6";default:return"#9ca3af"}};return t("div",{className:"flex flex-col gap-6 h-full",children:[t("div",{className:"flex flex-col gap-4 pb-0 pt-4",children:[e(n,{partNumber:i,questionCount:o,partName:m,isReviewMode:!0}),e(a,{instruction:d,hasExample:!!f,isReviewMode:!0}),u&&e(c,{audioSrc:u,compact:!0})]}),t("div",{className:"flex flex-col gap-6 pb-6 pl-2 pt-2",children:[e("h2",{className:"text-center text-3xl font-bold uppercase text-coral-500",children:x}),e("div",{className:"flex flex-col gap-4",children:h.map(n=>{const a=g(n.id),c=p[n.id]||"";return t("div",{className:"flex flex-col gap-2",children:[n.imageUrl&&e("div",{className:"flex items-center justify-center",children:e(l,{src:n.imageUrl,alt:`Question ${n.questionNumber} illustration`,className:"max-h-[150px] rounded-lg object-contain shadow-md"})}),t("div",{className:"flex items-start gap-2",children:[e("span",{className:"flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-sm font-bold text-white",children:n.questionNumber}),e("div",{className:"flex-1 text-base leading-relaxed text-gray-800",children:t("div",{className:"flex items-center gap-2",children:[n.textBefore&&e("span",{children:n.textBefore}),t("span",{className:"inline-block min-w-[120px]",style:{borderBottom:`2px solid ${w(n.id)}`},children:["correct"===a&&e("span",{className:"block w-full text-center",children:c}),"unanswered"===a&&e("span",{className:"block w-full text-center text-blue-600",children:b[n.id]}),"incorrect"===a&&t("span",{className:"flex items-center justify-center gap-2",children:[e("span",{className:"text-red-500 line-through",children:c}),e("span",{className:"text-gray-400",children:"→"}),e("span",{className:"text-blue-600",children:b[n.id]})]})]}),n.textAfter&&e("span",{children:n.textAfter}),"correct"===a&&e("div",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-green-500",children:e(r,{className:"h-4 w-4 text-white",strokeWidth:3})}),"incorrect"===a&&e("div",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-red-500",children:e(s,{className:"h-4 w-4 text-white",strokeWidth:3})}),"unanswered"===a&&e("div",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-blue-500",children:e(r,{className:"h-4 w-4 text-white",strokeWidth:3})})]})})]})]},n.id)})})]})]})}
@@ -0,0 +1,25 @@
1
+ export interface ReviewAnswerGroupQuestion {
2
+ id: string;
3
+ question_number: number;
4
+ content?: Record<string, unknown>;
5
+ questionGroup?: {
6
+ payload?: unknown;
7
+ } | null;
8
+ total_points?: string | number;
9
+ }
10
+ interface Props {
11
+ partNumber: number;
12
+ questionCount: number;
13
+ partName?: string;
14
+ instruction?: string;
15
+ questions: ReviewAnswerGroupQuestion[];
16
+ studentAnswers: Record<string, unknown>;
17
+ rawCorrectAnswerMap: Record<string, unknown>;
18
+ isCorrectMap: Record<string, boolean | null>;
19
+ questionScoreMap: Record<string, {
20
+ score: number | null;
21
+ maxScore: number;
22
+ }>;
23
+ }
24
+ export declare function ReviewListenChooseAnswerGroupRenderer({ partNumber, questionCount, partName, questions, studentAnswers, rawCorrectAnswerMap, isCorrectMap, questionScoreMap, }: Props): import("react").JSX.Element;
25
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Check as t,X as i}from"lucide-react";import{ResolvedImage as l}from"../../../components/common/ResolvedImage";import a from"../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import{cn as s}from"../../../shared/lib/utils";import{ReviewListeningHeader as n,ListeningLegend as o,getListeningStatus as d}from"./ReviewListeningHeader";function c(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function m(e,r){return c(e)&&"string"==typeof e[r]?e[r]:""}function u({option:a,isCorrect:n,isStudent:o}){let d="border-dashed border-slate-300 bg-white",c=null,m="",u=a.label;return n&&o?(d="border-solid border-emerald-500 bg-emerald-50 ring-2 ring-emerald-500/30",u=`${a.label} – Đáp án đúng (bạn đã chọn)`,c=e("span",{className:"absolute right-1 top-1 flex h-7 w-7 items-center justify-center rounded-full bg-emerald-500 text-white shadow-md ring-2 ring-white",children:e(t,{className:"h-4 w-4",strokeWidth:3})})):n?(d="border-solid border-emerald-500 bg-emerald-50 ring-2 ring-emerald-500/30",u=`${a.label} – Đáp án đúng`,c=e("span",{className:"absolute right-1 top-1 flex h-7 w-7 items-center justify-center rounded-full bg-emerald-500 text-white shadow-md ring-2 ring-white",children:e(t,{className:"h-4 w-4",strokeWidth:3})})):o?(d="border-solid border-red-500 bg-red-50 ring-2 ring-red-500/30",u=`${a.label} – Lựa chọn của bạn (sai)`,c=e("span",{className:"absolute right-1 top-1 flex h-7 w-7 items-center justify-center rounded-full bg-red-500 text-white shadow-md ring-2 ring-white",children:e(i,{className:"h-4 w-4",strokeWidth:3})})):m="opacity-60",r("div",{"aria-label":u,className:s("relative flex flex-col items-center rounded-xl border-2 p-2 transition",d,m),children:[e("div",{className:"aspect-square w-full overflow-hidden rounded-lg bg-white",children:a.imageUrl?e(l,{src:a.imageUrl,alt:a.label,className:"h-full w-full object-contain",draggable:!1}):e("div",{className:"flex h-full w-full items-center justify-center text-xs text-slate-400",children:a.label})}),e("p",{className:"mt-2 line-clamp-1 w-full text-center text-xs font-medium text-slate-700",children:a.label}),c]})}export function ReviewListenChooseAnswerGroupRenderer({partNumber:t,questionCount:i,partName:l,questions:s,studentAnswers:p,rawCorrectAnswerMap:b,isCorrectMap:g,questionScoreMap:h}){return r("div",{className:"flex flex-col gap-6 pt-4",children:[e(a,{partNumber:t,questionCount:i,partName:l,isReviewMode:!0}),s.map(t=>{const i=function(e){let r=e?.payload;if("string"==typeof r)try{r=JSON.parse(r)}catch{return{}}return c(r)&&Array.isArray(r.options)?{options:r.options.flatMap(e=>c(e)&&"string"==typeof e.id?[{id:e.id,label:"string"==typeof e.label?e.label:"",imageUrl:"string"==typeof e.imageUrl?e.imageUrl:""}]:[])}:{}}(t.questionGroup).options??[],l=m(t.content,"audioUrl")||m(t.content,"audio"),a=m(p[t.id],"answer"),s=m(b[t.id],"answer"),f=g[t.id]??null,w=d(f,!!a),x=h[t.id]??{score:null,maxScore:Number(t.total_points||0)};return r("div",{className:"rounded-2xl border border-slate-100 bg-slate-50/60 p-4 md:p-5",children:[e(n,{questionNumber:t.question_number,typeLabel:"Listen & choose answer",status:w,score:x.score,maxScore:x.maxScore,audioKey:l}),e(o,{showCorrect:!0,showStudent:!!a&&!f}),i.length>0?e("div",{className:"mt-3 grid grid-cols-2 gap-3 sm:grid-cols-3 md:grid-cols-4",children:i.map(r=>e(u,{option:r,isCorrect:r.id===s,isStudent:r.id===a},r.id))}):e("div",{className:"mt-3 rounded-lg border border-slate-200 bg-white p-4 text-center text-sm text-slate-400",children:"No options available"}),!a&&e("p",{className:"mt-3 text-xs italic text-slate-500",children:"Bạn chưa trả lời câu này."})]},t.id)})]})}
@@ -0,0 +1,25 @@
1
+ export interface ReviewChooseObjectsQuestion {
2
+ id: string;
3
+ question_number: number;
4
+ content?: Record<string, unknown>;
5
+ questionGroup?: {
6
+ payload?: unknown;
7
+ } | null;
8
+ total_points?: string | number;
9
+ }
10
+ interface Props {
11
+ partNumber: number;
12
+ questionCount: number;
13
+ partName?: string;
14
+ instruction?: string;
15
+ questions: ReviewChooseObjectsQuestion[];
16
+ studentAnswers: Record<string, unknown>;
17
+ rawCorrectAnswerMap: Record<string, unknown>;
18
+ isCorrectMap: Record<string, boolean | null>;
19
+ questionScoreMap: Record<string, {
20
+ score: number | null;
21
+ maxScore: number;
22
+ }>;
23
+ }
24
+ export declare function ReviewListenChooseObjectsRenderer({ partNumber, questionCount, partName, questions, studentAnswers, rawCorrectAnswerMap, isCorrectMap, questionScoreMap, }: Props): import("react").JSX.Element;
25
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Check as t,X as l}from"lucide-react";import{ResolvedImage as a}from"../../../components/common/ResolvedImage";import i from"../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import{cn as d}from"../../../shared/lib/utils";import{ReviewListeningHeader as s,ListeningLegend as n,getListeningStatus as o}from"./ReviewListeningHeader";function c(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function m(e,r){return c(e)&&"string"==typeof e[r]?e[r]:""}export function ReviewListenChooseObjectsRenderer({partNumber:h,questionCount:p,partName:u,questions:b,studentAnswers:g,rawCorrectAnswerMap:f,isCorrectMap:x,questionScoreMap:w}){return r("div",{className:"flex flex-col gap-6 pt-4",children:[e(i,{partNumber:h,questionCount:p,partName:u,isReviewMode:!0}),b.map(i=>{const h=function(e){let r=e?.payload;if("string"==typeof r)try{r=JSON.parse(r)}catch{return{}}if(!c(r))return{};const t=Array.isArray(r.objectNodes)?r.objectNodes.flatMap(e=>c(e)&&"string"==typeof e.id?[{id:e.id,label:"string"==typeof e.label?e.label:"",imageUrl:"string"==typeof e.imageUrl?e.imageUrl:"",x:"number"==typeof e.x?e.x:0,y:"number"==typeof e.y?e.y:0,width:"number"==typeof e.width?e.width:0,height:"number"==typeof e.height?e.height:0}]:[]):[];return{backgroundImageUrl:"string"==typeof r.backgroundImageUrl?r.backgroundImageUrl:void 0,objectNodes:t}}(i.questionGroup),p=h.objectNodes??[],u=h.backgroundImageUrl??"",b=m(i.content,"audioUrl")||m(i.content,"audio"),N=m(g[i.id],"objectId"),y=m(f[i.id],"objectId"),v=x[i.id]??null,j=o(v,!!N),k=w[i.id]??{score:null,maxScore:Number(i.total_points||0)},U=p.find(e=>e.id===y),q=p.find(e=>e.id===N);return r("div",{className:"rounded-2xl border border-slate-100 bg-slate-50/60 p-4 md:p-5",children:[e(s,{questionNumber:i.question_number,typeLabel:"Listen & choose object",status:j,score:k.score,maxScore:k.maxScore,audioKey:b}),e(n,{showCorrect:!0,showStudent:!!N&&!v}),e("div",{className:"mt-3 overflow-hidden rounded-xl border border-slate-200 bg-white",children:r("div",{className:"relative w-full",style:{aspectRatio:"16 / 9"},children:[u?e(a,{src:u,alt:"Scene",className:"absolute inset-0 h-full w-full object-contain",draggable:!1}):e("div",{className:"absolute inset-0 flex items-center justify-center text-sm text-slate-400",children:"No scene image"}),p.map(i=>{const s=i.id===y,n=i.id===N;let o="",c=null,m=i.label;return s&&n?(o="ring-4 ring-emerald-500 shadow-lg shadow-emerald-500/30",m=`${i.label} – Đáp án đúng (bạn đã chọn)`,c=e("span",{className:"absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500 text-white shadow ring-2 ring-white",children:e(t,{className:"h-3.5 w-3.5",strokeWidth:3})})):s?(o="ring-4 ring-emerald-500 shadow-lg shadow-emerald-500/30",m=`${i.label} – Đáp án đúng`,c=e("span",{className:"absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500 text-white shadow ring-2 ring-white",children:e(t,{className:"h-3.5 w-3.5",strokeWidth:3})})):n&&(o="ring-4 ring-red-500 shadow-lg shadow-red-500/30",m=`${i.label} – Lựa chọn của bạn (sai)`,c=e("span",{className:"absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-red-500 text-white shadow ring-2 ring-white",children:e(l,{className:"h-3.5 w-3.5",strokeWidth:3})})),e("div",{className:"absolute",style:{left:`${i.x}%`,top:`${i.y}%`,width:`${i.width}%`,height:`${i.height}%`},"aria-label":m,children:r("div",{className:d("relative h-full w-full rounded-md transition",o,!o&&"rounded-md border border-dashed border-slate-300/80 bg-white/40"),children:[e("div",{className:d("h-full w-full overflow-hidden rounded-md",!o&&"opacity-60"),children:i.imageUrl?e(a,{src:i.imageUrl,alt:i.label,className:"h-full w-full object-contain",draggable:!1}):e("div",{className:"flex h-full w-full items-center justify-center bg-white/60 text-[10px] text-slate-500",children:i.label})}),c]})},i.id)})]})}),r("div","correct"===j?{className:"mt-3 rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2 text-sm",children:[e("p",{className:"text-[11px] font-semibold uppercase tracking-wide text-emerald-700",children:"Đáp án đúng"}),e("p",{className:"mt-0.5 text-emerald-800",children:U?.label||y||"—"})]}:"unanswered"===j?{className:"mt-3 grid grid-cols-1 gap-2 text-sm sm:grid-cols-2",children:[r("div",{className:"rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2",children:[e("p",{className:"text-[11px] font-semibold uppercase tracking-wide text-emerald-700",children:"Đáp án đúng"}),e("p",{className:"mt-0.5 text-emerald-800",children:U?.label||y||"—"})]}),r("div",{className:"rounded-lg border border-slate-200 bg-white px-3 py-2",children:[e("p",{className:"text-[11px] font-semibold uppercase tracking-wide text-slate-500",children:"Đáp án của bạn"}),e("p",{className:"mt-0.5 italic text-slate-500",children:"Bạn chưa trả lời"})]})]}:{className:"mt-3 grid grid-cols-1 gap-2 text-sm sm:grid-cols-2",children:[r("div",{className:"rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2",children:[e("p",{className:"text-[11px] font-semibold uppercase tracking-wide text-emerald-700",children:"Đáp án đúng"}),e("p",{className:"mt-0.5 text-emerald-800",children:U?.label||y||"—"})]}),r("div",{className:"rounded-lg border border-red-200 bg-red-50 px-3 py-2",children:[e("p",{className:"text-[11px] font-semibold uppercase tracking-wide text-red-700",children:"Đáp án của bạn"}),e("p",{className:"mt-0.5 text-red-800",children:q?.label||N||"—"})]})]})]},i.id)})]})}
@@ -0,0 +1,25 @@
1
+ export interface ReviewDragObjectsQuestion {
2
+ id: string;
3
+ question_number: number;
4
+ content?: Record<string, unknown>;
5
+ questionGroup?: {
6
+ payload?: unknown;
7
+ } | null;
8
+ total_points?: string | number;
9
+ }
10
+ interface Props {
11
+ partNumber: number;
12
+ questionCount: number;
13
+ partName?: string;
14
+ instruction?: string;
15
+ questions: ReviewDragObjectsQuestion[];
16
+ studentAnswers: Record<string, unknown>;
17
+ rawCorrectAnswerMap: Record<string, unknown>;
18
+ isCorrectMap: Record<string, boolean | null>;
19
+ questionScoreMap: Record<string, {
20
+ score: number | null;
21
+ maxScore: number;
22
+ }>;
23
+ }
24
+ export declare function ReviewListenDragObjectsRenderer({ partNumber, questionCount, partName, questions, studentAnswers, rawCorrectAnswerMap, isCorrectMap, questionScoreMap, }: Props): import("react").JSX.Element;
25
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Check as t,X as a}from"lucide-react";import{ResolvedImage as i}from"../../../components/common/ResolvedImage";import l from"../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import{cn as d}from"../../../shared/lib/utils";import{ReviewListeningHeader as s,ListeningLegend as n,getListeningStatus as o}from"./ReviewListeningHeader";function c(e){return"object"==typeof e&&null!==e&&!Array.isArray(e)}function m(e,r){return c(e)&&"string"==typeof e[r]?e[r]:""}function u({zone:l,studentObject:s,correctObject:n,isStudentZone:o,isCorrectZone:c,isStudentCorrect:m}){let u,b="border-2 border-dashed border-slate-300/80 bg-white/30",p=null;return c&&o?(b=m?"border-2 border-emerald-500 bg-emerald-50/70 ring-2 ring-emerald-400/40":"border-2 border-red-500 bg-red-50/70 ring-2 ring-red-400/40",p=e("span",m?{className:"absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500 text-white shadow ring-2 ring-white",children:e(t,{className:"h-3.5 w-3.5",strokeWidth:3})}:{className:"absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-red-500 text-white shadow ring-2 ring-white",children:e(a,{className:"h-3.5 w-3.5",strokeWidth:3})}),u=s):o?(b="border-2 border-red-500 bg-red-50/70 ring-2 ring-red-400/40",p=e("span",{className:"absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-red-500 text-white shadow ring-2 ring-white",children:e(a,{className:"h-3.5 w-3.5",strokeWidth:3})}),u=s):c&&(b="border-2 border-emerald-500 bg-emerald-50/70 ring-2 ring-emerald-400/40",p=e("span",{className:"absolute right-1 top-1 flex h-6 w-6 items-center justify-center rounded-full bg-emerald-500 text-white shadow ring-2 ring-white",children:e(t,{className:"h-3.5 w-3.5",strokeWidth:3})}),u=n),e("div",{className:"absolute",style:{left:`${l.x}%`,top:`${l.y}%`,width:`${l.width}%`,height:`${l.height}%`},children:r("div",{className:d("relative flex h-full w-full items-center justify-center rounded-md",b),children:[u?.imageUrl?e(i,{src:u.imageUrl,alt:u.label,className:"h-full w-full object-contain p-1",draggable:!1}):e("span",{className:"px-1 text-center text-[10px] font-medium text-slate-500",children:l.label}),p]})})}export function ReviewListenDragObjectsRenderer({partNumber:t,questionCount:a,partName:b,questions:p,studentAnswers:h,rawCorrectAnswerMap:g,isCorrectMap:f,questionScoreMap:x}){return r("div",{className:"flex flex-col gap-6 pt-4",children:[e(l,{partNumber:t,questionCount:a,partName:b,isReviewMode:!0}),p.map(t=>{const a=function(e){let r=e?.payload;if("string"==typeof r)try{r=JSON.parse(r)}catch{return{}}if(!c(r))return{};const t=Array.isArray(r.objectNodes)?r.objectNodes.flatMap(e=>c(e)&&"string"==typeof e.id?[{id:e.id,label:"string"==typeof e.label?e.label:"",imageUrl:"string"==typeof e.imageUrl?e.imageUrl:""}]:[]):[],a=Array.isArray(r.dropZones)?r.dropZones.flatMap(e=>c(e)&&"string"==typeof e.id?[{id:e.id,label:"string"==typeof e.label?e.label:"",x:"number"==typeof e.x?e.x:0,y:"number"==typeof e.y?e.y:0,width:"number"==typeof e.width?e.width:0,height:"number"==typeof e.height?e.height:0}]:[]):[];return{backgroundImageUrl:"string"==typeof r.backgroundImageUrl?r.backgroundImageUrl:void 0,objectNodes:t,dropZones:a}}(t.questionGroup),l=a.objectNodes??[],b=a.dropZones??[],p=a.backgroundImageUrl??"",N=m(t.content,"audioUrl")||m(t.content,"audio"),w=m(h[t.id],"objectId"),y=m(h[t.id],"dropZoneId"),j=m(g[t.id],"objectId"),v=m(g[t.id],"dropZoneId"),k=!!w||!!y,S=f[t.id]??null,C=o(S,k),U=x[t.id]??{score:null,maxScore:Number(t.total_points||0)},Z=l.find(e=>e.id===w),I=l.find(e=>e.id===j),A=b.find(e=>e.id===y),q=b.find(e=>e.id===v);return r("div",{className:"rounded-2xl border border-slate-100 bg-slate-50/60 p-4 md:p-5",children:[e(s,{questionNumber:t.question_number,typeLabel:"Listen & drag object",status:C,score:U.score,maxScore:U.maxScore,audioKey:N}),e(n,{showCorrect:!0,showStudent:k&&!S}),e("div",{className:"mt-3 overflow-hidden rounded-xl border border-slate-200 bg-white",children:r("div",{className:"relative w-full",style:{aspectRatio:"16 / 9"},children:[p?e(i,{src:p,alt:"Scene",className:"absolute inset-0 h-full w-full object-contain",draggable:!1}):e("div",{className:"absolute inset-0 flex items-center justify-center text-sm text-slate-400",children:"No scene image"}),b.map(r=>e(u,{zone:r,studentObject:Z,correctObject:I,isStudentZone:r.id===y,isCorrectZone:r.id===v,isStudentCorrect:!!S},r.id))]})}),r("div","correct"===C?{className:"mt-3 rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2 text-sm",children:[e("p",{className:"text-[11px] font-semibold uppercase tracking-wide text-emerald-700",children:"Vị trí đúng"}),r("p",{className:"mt-0.5 text-emerald-800",children:[e("span",{className:"font-medium",children:I?.label||j||"—"}),e("span",{className:"mx-1.5 text-emerald-500",children:"→"}),e("span",{children:q?.label||v||"—"})]})]}:{className:"mt-3 grid grid-cols-1 gap-2 text-sm sm:grid-cols-2",children:[r("div",{className:"rounded-lg border border-emerald-200 bg-emerald-50 px-3 py-2",children:[e("p",{className:"text-[11px] font-semibold uppercase tracking-wide text-emerald-700",children:"Vị trí đúng"}),r("p",{className:"mt-0.5 text-emerald-800",children:[e("span",{className:"font-medium",children:I?.label||j||"—"}),e("span",{className:"mx-1.5 text-emerald-500",children:"→"}),e("span",{children:q?.label||v||"—"})]})]}),r("div",{className:d("rounded-lg border px-3 py-2","incorrect"===C?"border-red-200 bg-red-50":"border-slate-200 bg-white"),children:[e("p",{className:d("text-[11px] font-semibold uppercase tracking-wide","incorrect"===C?"text-red-700":"text-slate-500"),children:"Vị trí của bạn"}),k?r("p",{className:"mt-0.5 text-red-800",children:[e("span",{className:"font-medium",children:Z?.label||w||"—"}),e("span",{className:"mx-1.5 text-red-500",children:"→"}),e("span",{children:A?.label||y||"—"})]}):e("p",{className:"mt-0.5 italic text-slate-500",children:"Bạn chưa trả lời"})]})]})]},t.id)})]})}
@@ -0,0 +1,44 @@
1
+ export type ListeningQuestionStatus = 'correct' | 'incorrect' | 'unanswered';
2
+ export declare function getListeningStatus(isCorrect: boolean | null | undefined, hasStudentAnswer: boolean): ListeningQuestionStatus;
3
+ interface ListeningStatusBadgeProps {
4
+ status: ListeningQuestionStatus;
5
+ }
6
+ /**
7
+ * Combined icon + textual correctness badge. Provides redundancy beyond colour
8
+ * for accessibility / quick scanning.
9
+ */
10
+ export declare function ListeningStatusBadge({ status }: ListeningStatusBadgeProps): import("react").JSX.Element;
11
+ interface ListeningScorePillProps {
12
+ score: number | null;
13
+ maxScore: number;
14
+ status: ListeningQuestionStatus;
15
+ }
16
+ export declare function ListeningScorePill({ score, maxScore, status }: ListeningScorePillProps): import("react").JSX.Element;
17
+ interface ListeningAudioBarProps {
18
+ audioKey: string;
19
+ }
20
+ /**
21
+ * Minimal audio bar tailored for listening prompts in review mode.
22
+ * Uses a speaker icon (not a microphone) so it reads as "audio to listen to",
23
+ * not "your recording".
24
+ */
25
+ export declare function ListeningAudioBar({ audioKey }: ListeningAudioBarProps): import("react").JSX.Element;
26
+ interface ReviewListeningHeaderProps {
27
+ questionNumber: number;
28
+ typeLabel: string;
29
+ status: ListeningQuestionStatus;
30
+ score: number | null;
31
+ maxScore: number;
32
+ audioKey?: string;
33
+ /** Optional title rendered below the type badge row. */
34
+ title?: string;
35
+ /** Optional extra slot rendered to the right of the type badge row. */
36
+ rightExtra?: React.ReactNode;
37
+ }
38
+ export declare function ReviewListeningHeader({ questionNumber, typeLabel, status, score, maxScore, audioKey, title, rightExtra, }: ReviewListeningHeaderProps): import("react").JSX.Element;
39
+ /** Small legend chip rendered above the visualization. Shown only when relevant. */
40
+ export declare function ListeningLegend({ showCorrect, showStudent, }: {
41
+ showCorrect?: boolean;
42
+ showStudent?: boolean;
43
+ }): import("react").JSX.Element;
44
+ export {};
@@ -0,0 +1 @@
1
+ "use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{Check as r,Volume2 as n,X as a,Pause as s,Play as l}from"lucide-react";import{useCallback as i,useEffect as d,useRef as c,useState as o}from"react";import{Badge as u}from"../../../components/ui/badge";import{cn as m}from"../../../shared/lib/utils";import{usePresignedFileUrl as x}from"../../../shared/lib/hooks/usePresignedFileUrl";export function getListeningStatus(e,t){return t?!0===e?"correct":!1===e?"incorrect":"unanswered":"unanswered"}export function ListeningStatusBadge({status:n}){return t("span","correct"===n?{className:"inline-flex items-center gap-1 rounded-full border border-emerald-200 bg-emerald-50 px-2 py-0.5 text-xs font-semibold text-emerald-700",children:[e("span",{className:"flex h-4 w-4 items-center justify-center rounded-full bg-emerald-500",children:e(r,{className:"h-2.5 w-2.5 text-white",strokeWidth:3})}),"Đúng"]}:"incorrect"===n?{className:"inline-flex items-center gap-1 rounded-full border border-red-200 bg-red-50 px-2 py-0.5 text-xs font-semibold text-red-700",children:[e("span",{className:"flex h-4 w-4 items-center justify-center rounded-full bg-red-500",children:e(a,{className:"h-2.5 w-2.5 text-white",strokeWidth:3})}),"Sai"]}:{className:"inline-flex items-center gap-1 rounded-full border border-slate-200 bg-slate-50 px-2 py-0.5 text-xs font-semibold text-slate-600",children:[e("span",{className:"flex h-4 w-4 items-center justify-center rounded-full bg-slate-400",children:e(a,{className:"h-2.5 w-2.5 text-white",strokeWidth:3})}),"Chưa trả lời"]})}export function ListeningScorePill({score:r,maxScore:n,status:a}){const s=null==r?"—":r;return t("div",{className:m("flex flex-shrink-0 items-baseline gap-1 rounded-full border px-3 py-1","correct"===a?"border-emerald-200 bg-emerald-50 text-emerald-700":"incorrect"===a?"border-red-200 bg-red-50 text-red-700":"border-slate-200 bg-slate-50 text-slate-600"),children:[e("span",{className:"text-sm font-bold leading-none",children:s}),t("span",{className:"text-xs font-medium opacity-80",children:["/ ",n," điểm"]})]})}function h(e){if(!isFinite(e))return"0:00";return`${Math.floor(e/60)}:${Math.floor(e%60).toString().padStart(2,"0")}`}export function ListeningAudioBar({audioKey:r}){const{previewUrl:a}=x(r),u=c(null),[m,p]=o(!1),[f,b]=o(0),[g,w]=o(0),N=i(()=>{const e=u.current;e&&(m?e.pause():e.play().catch(e=>{console.warn("Audio play request was interrupted:",e)}))},[m]),y=i(e=>{const t=u.current;if(!t)return;const r=Number(e.target.value);t.currentTime=r,b(r)},[]);return d(()=>{p(!1),b(0)},[a]),t("div",{className:"flex items-center gap-3 rounded-lg border border-slate-200 bg-white px-3 py-2",children:[a&&e("audio",{ref:u,src:a,onPlay:()=>p(!0),onPause:()=>p(!1),onEnded:()=>p(!1),onTimeUpdate:e=>b(e.currentTarget.currentTime),onLoadedMetadata:e=>w(e.currentTarget.duration),preload:"metadata"}),e("div",{className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-blue-50 text-blue-600",children:e(n,{className:"h-4 w-4"})}),e("button",{type:"button",onClick:N,disabled:!a,className:"flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full bg-blue-600 text-white shadow-sm transition hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-40","aria-label":m?"Tạm dừng":"Phát âm thanh câu hỏi",children:m?e(s,{className:"h-4 w-4"}):e(l,{className:"ml-0.5 h-4 w-4"})}),e("span",{className:"w-10 text-xs tabular-nums text-slate-500",children:h(f)}),e("input",{type:"range",min:0,max:g||0,step:.01,value:f,onChange:y,className:"h-1 flex-1 cursor-pointer appearance-none rounded-full bg-slate-200 accent-blue-600","aria-label":"Tua âm thanh"}),e("span",{className:"w-10 text-right text-xs tabular-nums text-slate-500",children:h(g)})]})}export function ReviewListeningHeader({questionNumber:r,typeLabel:n,status:a,score:s,maxScore:l,audioKey:i,title:d,rightExtra:c}){return t("div",{className:"mb-3 space-y-3",children:[t("div",{className:"flex items-start gap-3",children:[e("span",{className:"mt-0.5 flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-sm font-bold text-white",children:r}),t("div",{className:"min-w-0 flex-1",children:[t("div",{className:"flex flex-wrap items-center gap-2",children:[e(u,{variant:"secondary",className:"bg-slate-100 text-slate-700 hover:bg-slate-100",children:n}),e(ListeningStatusBadge,{status:a}),c]}),d?e("p",{className:"mt-1.5 text-sm font-medium text-slate-700",children:d}):null]}),e(ListeningScorePill,{score:s,maxScore:l,status:a})]}),i?e(ListeningAudioBar,{audioKey:i}):null]})}export function ListeningLegend({showCorrect:r=!0,showStudent:n=!0}){return r||n?t("div",{className:"flex flex-wrap items-center gap-3 text-xs text-slate-600",children:[r?t("span",{className:"inline-flex items-center gap-1.5",children:[e("span",{className:"inline-block h-3 w-3 rounded-full border-2 border-emerald-500 bg-emerald-50"}),"Đáp án đúng"]}):null,n?t("span",{className:"inline-flex items-center gap-1.5",children:[e("span",{className:"inline-block h-3 w-3 rounded-full border-2 border-red-500 bg-red-50"}),"Đáp án của bạn"]}):null]}):null}
@@ -0,0 +1,17 @@
1
+ import type { SpeakingReviewItem } from './ReviewSpeakingRenderer.types';
2
+ interface ReviewSpeakingAIPanelProps {
3
+ question: SpeakingReviewItem;
4
+ }
5
+ /**
6
+ * AI review panel for the /student/results/:attemptId/review page.
7
+ *
8
+ * Layout mirrors SpeakingAIAssessmentPanel (speaking-review page) but uses the
9
+ * slate / blue-500 / emerald / amber / rose palette of the existing review page
10
+ * (instead of the #2874A6 tone used on speaking-review).
11
+ *
12
+ * Only renders for questions with a student audio recording. If there's no
13
+ * content score / feedback the right-hand card falls back to a neutral
14
+ * placeholder.
15
+ */
16
+ export declare function ReviewSpeakingAIPanel({ question }: ReviewSpeakingAIPanelProps): import("react").JSX.Element;
17
+ export {};