@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.
- package/README.md +112 -13
- package/dist/api/contest-rooms.d.ts +13 -0
- package/dist/api/contest-rooms.js +1 -0
- package/dist/api/contest-rooms.types.d.ts +47 -0
- package/dist/api/contest-rooms.types.js +1 -0
- package/dist/api/exam-entry.d.ts +14 -0
- package/dist/api/exam-entry.js +1 -0
- package/dist/api/exam-entry.types.d.ts +93 -0
- package/dist/api/exam-entry.types.js +1 -0
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +1 -1
- package/dist/api/result-review.d.ts +2 -1
- package/dist/api/result-review.js +1 -1
- package/dist/api/result-review.types.d.ts +69 -3
- package/dist/api/result-review.types.js +1 -1
- package/dist/components/exams/entry/EntryRestartDialog.d.ts +12 -0
- package/dist/components/exams/entry/EntryRestartDialog.js +1 -0
- package/dist/components/exams/entry/StudentExamEntryPageContainer.d.ts +31 -0
- package/dist/components/exams/entry/StudentExamEntryPageContainer.js +1 -0
- package/dist/components/exams/entry/exam-entry.states.d.ts +22 -0
- package/dist/components/exams/entry/exam-entry.states.js +1 -0
- package/dist/components/exams/entry/exam-entry.utils.d.ts +16 -0
- package/dist/components/exams/entry/exam-entry.utils.js +1 -0
- package/dist/components/exams/entry/fullscreen-loading.d.ts +5 -0
- package/dist/components/exams/entry/fullscreen-loading.js +1 -0
- package/dist/components/exams/entry/index.d.ts +7 -0
- package/dist/components/exams/entry/index.js +1 -0
- package/dist/components/exams/index.d.ts +8 -0
- package/dist/components/exams/index.js +1 -1
- package/dist/components/exams/rooms/StudentContestRoomsPageContainer.d.ts +18 -0
- package/dist/components/exams/rooms/StudentContestRoomsPageContainer.js +1 -0
- package/dist/components/exams/rooms/contest-rooms.utils.d.ts +32 -0
- package/dist/components/exams/rooms/contest-rooms.utils.js +1 -0
- package/dist/components/exams/rooms/index.d.ts +3 -0
- package/dist/components/exams/rooms/index.js +1 -0
- package/dist/components/exams/take/ExamTakingApiContext.d.ts +10 -0
- package/dist/components/exams/take/ExamTakingApiContext.js +1 -0
- package/dist/components/exams/take/ExamTakingPageContainer.d.ts +28 -4
- package/dist/components/exams/take/ExamTakingPageContainer.js +1 -1
- package/dist/components/exams/take/components/QuestionRenderer.d.ts +25 -0
- package/dist/components/exams/take/components/QuestionRenderer.js +1 -0
- package/dist/components/exams/take/components/index.d.ts +10 -0
- package/dist/components/exams/take/components/index.js +1 -0
- package/dist/components/exams/take/components/question-renderers/ArticlesViewer.d.ts +7 -0
- package/dist/components/exams/take/components/question-renderers/ArticlesViewer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversAnswerTheQuestionRenderer.d.ts +22 -0
- package/dist/components/exams/take/components/question-renderers/MoversAnswerTheQuestionRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversChooseAdjectiveRenderer.d.ts +14 -0
- package/dist/components/exams/take/components/question-renderers/MoversChooseAdjectiveRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversChooseBestAnswerRenderer.d.ts +2 -0
- package/dist/components/exams/take/components/question-renderers/MoversChooseBestAnswerRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversColoringRenderer.d.ts +3 -0
- package/dist/components/exams/take/components/question-renderers/MoversColoringRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.d.ts +2 -0
- package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversGridFillRenderer.d.ts +2 -0
- package/dist/components/exams/take/components/question-renderers/MoversGridFillRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversLabelThePictureRenderer.d.ts +2 -0
- package/dist/components/exams/take/components/question-renderers/MoversLabelThePictureRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversListenAndWriteRenderer.d.ts +21 -0
- package/dist/components/exams/take/components/question-renderers/MoversListenAndWriteRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversMatchByWritingAnswerRenderer.d.ts +19 -0
- package/dist/components/exams/take/components/question-renderers/MoversMatchByWritingAnswerRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.d.ts +2 -0
- package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.d.ts +2 -0
- package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversReadingPassageRenderer.d.ts +2 -0
- package/dist/components/exams/take/components/question-renderers/MoversReadingPassageRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversTrueFalseRenderer.d.ts +20 -0
- package/dist/components/exams/take/components/question-renderers/MoversTrueFalseRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversWordFillParagraphRenderer.d.ts +32 -0
- package/dist/components/exams/take/components/question-renderers/MoversWordFillParagraphRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversWordFillStructuredFormRenderer.d.ts +18 -0
- package/dist/components/exams/take/components/question-renderers/MoversWordFillStructuredFormRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversWordOrderingRenderer.d.ts +19 -0
- package/dist/components/exams/take/components/question-renderers/MoversWordOrderingRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversWriteSentencesRenderer.d.ts +22 -0
- package/dist/components/exams/take/components/question-renderers/MoversWriteSentencesRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/MoversWritingRenderer.d.ts +18 -0
- package/dist/components/exams/take/components/question-renderers/MoversWritingRenderer.js +1 -0
- package/dist/components/exams/take/components/question-renderers/index.d.ts +19 -0
- package/dist/components/exams/take/components/question-renderers/index.js +1 -0
- package/dist/components/exams/take/components/speaking/FindObjectInPicture.module.css +133 -0
- package/dist/components/exams/take/components/speaking/SpeakingAudioPlayer.d.ts +9 -0
- package/dist/components/exams/take/components/speaking/SpeakingAudioPlayer.js +1 -0
- package/dist/components/exams/take/components/speaking/SpeakingCompareImages.module.css +153 -0
- package/dist/components/exams/take/components/speaking/SpeakingContentFrame.d.ts +7 -0
- package/dist/components/exams/take/components/speaking/SpeakingContentFrame.js +1 -0
- package/dist/components/exams/take/components/speaking/SpeakingDragObjects.module.css +238 -0
- package/dist/components/exams/take/components/speaking/SpeakingExamLayout.d.ts +27 -0
- package/dist/components/exams/take/components/speaking/SpeakingExamLayout.js +1 -0
- package/dist/components/exams/take/components/speaking/SpeakingHeader.d.ts +6 -0
- package/dist/components/exams/take/components/speaking/SpeakingHeader.js +1 -0
- package/dist/components/exams/take/components/speaking/SpeakingInfoExchange.module.css +193 -0
- package/dist/components/exams/take/components/speaking/SpeakingInfoExchangeSummerSky.module.css +238 -0
- package/dist/components/exams/take/components/speaking/SpeakingInstructionBar.d.ts +5 -0
- package/dist/components/exams/take/components/speaking/SpeakingInstructionBar.js +1 -0
- package/dist/components/exams/take/components/speaking/SpeakingMicButton.d.ts +14 -0
- package/dist/components/exams/take/components/speaking/SpeakingMicButton.js +1 -0
- package/dist/components/exams/take/components/speaking/SpeakingNavButton.d.ts +7 -0
- package/dist/components/exams/take/components/speaking/SpeakingNavButton.js +1 -0
- package/dist/components/exams/take/components/speaking/SpeakingPartBadge.d.ts +6 -0
- package/dist/components/exams/take/components/speaking/SpeakingPartBadge.js +1 -0
- package/dist/components/exams/take/components/speaking/SpeakingReadDisplayedContent.module.css +115 -0
- package/dist/components/exams/take/components/speaking/SpeakingRecordingPreview.d.ts +11 -0
- package/dist/components/exams/take/components/speaking/SpeakingRecordingPreview.js +1 -0
- package/dist/components/exams/take/components/speaking/SpeakingReportErrorButton.d.ts +1 -0
- package/dist/components/exams/take/components/speaking/SpeakingReportErrorButton.js +1 -0
- package/dist/components/exams/take/components/speaking/SpeakingStoryImages.module.css +231 -0
- package/dist/components/exams/take/components/speaking/SpeakingVideoIntro.d.ts +13 -0
- package/dist/components/exams/take/components/speaking/SpeakingVideoIntro.js +1 -0
- package/dist/components/exams/take/components/speaking/index.d.ts +1 -0
- package/dist/components/exams/take/components/speaking/index.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingChooseAnswerGroupRenderer.d.ts +16 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingChooseAnswerGroupRenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingChooseObjectsRenderer.d.ts +16 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingChooseObjectsRenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingCompareImagesRenderer.d.ts +17 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingCompareImagesRenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingConversationRenderer.d.ts +16 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingConversationRenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingCueCardRenderer.d.ts +17 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingCueCardRenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingDescribeImageRenderer.d.ts +18 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingDescribeImageRenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingDragObjectsRenderer.d.ts +16 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingDragObjectsRenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.d.ts +16 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingInfoExchangeRenderer.d.ts +16 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingInfoExchangeRenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingListenAndSpeakAnswerRenderer.d.ts +16 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingListenAndSpeakAnswerRenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingOddOneOutRenderer.d.ts +16 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingOddOneOutRenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionListRenderer.d.ts +26 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionListRenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionRenderer.d.ts +32 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionRenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingReadDisplayedContentRenderer.d.ts +17 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingReadDisplayedContentRenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQARenderer.d.ts +17 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQARenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingStoryImagesRenderer.d.ts +17 -0
- package/dist/components/exams/take/components/speaking/renderers/SpeakingStoryImagesRenderer.js +1 -0
- package/dist/components/exams/take/components/speaking/renderers/index.d.ts +13 -0
- package/dist/components/exams/take/components/speaking/renderers/index.js +1 -0
- package/dist/components/exams/take/hooks/index.d.ts +2 -0
- package/dist/components/exams/take/hooks/index.js +1 -0
- package/dist/components/exams/take/hooks/useAnswerAutosave.d.ts +27 -0
- package/dist/components/exams/take/hooks/useAnswerAutosave.js +1 -0
- package/dist/components/exams/take/hooks/useAttemptSubmit.d.ts +29 -0
- package/dist/components/exams/take/hooks/useAttemptSubmit.js +1 -0
- package/dist/components/exams/take/hooks/useAutoSave.d.ts +20 -0
- package/dist/components/exams/take/hooks/useAutoSave.js +1 -0
- package/dist/components/exams/take/hooks/useExamCountdown.d.ts +22 -0
- package/dist/components/exams/take/hooks/useExamCountdown.js +1 -0
- package/dist/components/exams/take/hooks/useExamTimer.d.ts +21 -0
- package/dist/components/exams/take/hooks/useExamTimer.js +1 -0
- package/dist/components/exams/take/hooks/useProctoringCamera.d.ts +19 -0
- package/dist/components/exams/take/hooks/useProctoringCamera.js +1 -0
- package/dist/components/exams/take/hooks/useProctoringSnapshot.d.ts +25 -0
- package/dist/components/exams/take/hooks/useProctoringSnapshot.js +1 -0
- package/dist/components/exams/take/hooks/useSpeakingConversationAudio.d.ts +19 -0
- package/dist/components/exams/take/hooks/useSpeakingConversationAudio.js +1 -0
- package/dist/components/exams/take/hooks/useSpeakingNavigation.d.ts +37 -0
- package/dist/components/exams/take/hooks/useSpeakingNavigation.js +1 -0
- package/dist/components/exams/take/hooks/useSpeakingRecorder.d.ts +45 -5
- package/dist/components/exams/take/hooks/useSpeakingRecorder.js +1 -1
- package/dist/components/exams/take/hooks/useSpeakingRendererSetup.d.ts +39 -0
- package/dist/components/exams/take/hooks/useSpeakingRendererSetup.js +1 -0
- package/dist/components/exams/take/hooks/useToeicAudio.d.ts +17 -0
- package/dist/components/exams/take/hooks/useToeicAudio.js +1 -0
- package/dist/components/exams/take/index.d.ts +9 -1
- package/dist/components/exams/take/index.js +1 -1
- package/dist/components/exams/take/types/speaking-cue-card.types.d.ts +10 -0
- package/dist/components/exams/take/types/speaking-cue-card.types.js +1 -0
- package/dist/components/exams/take/types/speaking.types.d.ts +153 -0
- package/dist/components/exams/take/types/speaking.types.js +1 -0
- package/dist/components/exams/take/types.d.ts +286 -1
- package/dist/components/exams/take/utils/answer-transformers.d.ts +26 -0
- package/dist/components/exams/take/utils/answer-transformers.js +1 -0
- package/dist/components/exams/take/utils/coloring-assignments.d.ts +5 -0
- package/dist/components/exams/take/utils/coloring-assignments.js +1 -0
- package/dist/components/exams/take/utils/question-transformers.d.ts +362 -0
- package/dist/components/exams/take/utils/question-transformers.js +1 -0
- package/dist/components/features/exam-entry/entry-state-cards.d.ts +19 -0
- package/dist/components/features/exam-entry/entry-state-cards.js +1 -0
- package/dist/components/features/exam-entry/index.d.ts +2 -0
- package/dist/components/features/exam-entry/index.js +1 -0
- package/dist/components/features/exam-entry/themed/CambridgeYleWaitingRoom.d.ts +12 -0
- package/dist/components/features/exam-entry/themed/CambridgeYleWaitingRoom.js +1 -0
- package/dist/components/features/exam-entry/themed/ThemedWaitingRoom.d.ts +14 -0
- package/dist/components/features/exam-entry/themed/ThemedWaitingRoom.js +1 -0
- package/dist/components/features/exam-entry/themed/ToeicWaitingRoom.d.ts +16 -0
- package/dist/components/features/exam-entry/themed/ToeicWaitingRoom.js +1 -0
- package/dist/components/features/exam-entry/themed/components/CloseButton.d.ts +9 -0
- package/dist/components/features/exam-entry/themed/components/CloseButton.js +1 -0
- package/dist/components/features/exam-entry/themed/components/ExamInfoPanel.d.ts +13 -0
- package/dist/components/features/exam-entry/themed/components/ExamInfoPanel.js +1 -0
- package/dist/components/features/exam-entry/themed/components/ModeSelector.d.ts +7 -0
- package/dist/components/features/exam-entry/themed/components/ModeSelector.js +1 -0
- package/dist/components/features/exam-entry/themed/components/StartButton.d.ts +11 -0
- package/dist/components/features/exam-entry/themed/components/StartButton.js +1 -0
- package/dist/components/features/exam-entry/themed/components/index.d.ts +4 -0
- package/dist/components/features/exam-entry/themed/components/index.js +1 -0
- package/dist/components/features/exam-entry/themed/index.d.ts +1 -0
- package/dist/components/features/exam-entry/themed/index.js +1 -0
- package/dist/components/results/ResultReviewPageContainer.d.ts +16 -2
- package/dist/components/results/ResultReviewPageContainer.js +1 -1
- package/dist/components/results/ReviewQuestionRenderer.d.ts +11 -1
- package/dist/components/results/ReviewQuestionRenderer.js +1 -1
- package/dist/components/results/hooks/useResultReviewPolling.d.ts +37 -0
- package/dist/components/results/hooks/useResultReviewPolling.js +1 -0
- package/dist/components/results/index.d.ts +36 -1
- package/dist/components/results/index.js +1 -1
- package/dist/components/results/renderers/QuestionExplanations.d.ts +22 -0
- package/dist/components/results/renderers/QuestionExplanations.js +1 -0
- package/dist/components/results/renderers/ReviewChooseAdjectiveRenderer.d.ts +13 -0
- package/dist/components/results/renderers/ReviewChooseAdjectiveRenderer.js +1 -0
- package/dist/components/results/renderers/ReviewChooseBestAnswerRenderer.d.ts +22 -0
- package/dist/components/results/renderers/ReviewChooseBestAnswerRenderer.js +1 -0
- package/dist/components/results/renderers/ReviewColoringRenderer.d.ts +16 -0
- package/dist/components/results/renderers/ReviewColoringRenderer.js +1 -0
- package/dist/components/results/renderers/ReviewListenAndWriteRenderer.d.ts +18 -0
- package/dist/components/results/renderers/ReviewListenAndWriteRenderer.js +1 -0
- package/dist/components/results/renderers/ReviewListenChooseAnswerGroupRenderer.d.ts +25 -0
- package/dist/components/results/renderers/ReviewListenChooseAnswerGroupRenderer.js +1 -0
- package/dist/components/results/renderers/ReviewListenChooseObjectsRenderer.d.ts +25 -0
- package/dist/components/results/renderers/ReviewListenChooseObjectsRenderer.js +1 -0
- package/dist/components/results/renderers/ReviewListenDragObjectsRenderer.d.ts +25 -0
- package/dist/components/results/renderers/ReviewListenDragObjectsRenderer.js +1 -0
- package/dist/components/results/renderers/ReviewListeningHeader.d.ts +44 -0
- package/dist/components/results/renderers/ReviewListeningHeader.js +1 -0
- package/dist/components/results/renderers/ReviewSpeakingAIPanel.d.ts +17 -0
- package/dist/components/results/renderers/ReviewSpeakingAIPanel.js +1 -0
- package/dist/components/results/renderers/ReviewSpeakingMedia.d.ts +39 -0
- package/dist/components/results/renderers/ReviewSpeakingMedia.js +1 -0
- package/dist/components/results/renderers/ReviewSpeakingQuestionItem.d.ts +5 -0
- package/dist/components/results/renderers/ReviewSpeakingQuestionItem.js +1 -0
- package/dist/components/results/renderers/ReviewSpeakingRenderer.d.ts +3 -0
- package/dist/components/results/renderers/ReviewSpeakingRenderer.js +1 -0
- package/dist/components/results/renderers/ReviewSpeakingRenderer.types.d.ts +80 -0
- package/dist/components/results/renderers/ReviewSpeakingRenderer.types.js +1 -0
- package/dist/components/results/renderers/review-question-body-dedicated.d.ts +15 -0
- package/dist/components/results/renderers/review-question-body-dedicated.js +1 -0
- package/dist/components/results/renderers/review-question-body-movers.d.ts +17 -0
- package/dist/components/results/renderers/review-question-body-movers.js +1 -0
- package/dist/components/results/renderers/review-speaking-map.d.ts +7 -0
- package/dist/components/results/renderers/review-speaking-map.js +1 -0
- package/dist/components/results/report/BadgeTitle.d.ts +5 -0
- package/dist/components/results/report/BadgeTitle.js +1 -0
- package/dist/components/results/report/ExamSkillRow.d.ts +14 -0
- package/dist/components/results/report/ExamSkillRow.js +1 -0
- package/dist/components/results/report/OceanEduExamReport.d.ts +31 -0
- package/dist/components/results/report/OceanEduExamReport.js +1 -0
- package/dist/components/results/report/OceanEduSpeakingReport.d.ts +26 -0
- package/dist/components/results/report/OceanEduSpeakingReport.js +1 -0
- package/dist/components/results/report/OverallAssessmentSection.d.ts +24 -0
- package/dist/components/results/report/OverallAssessmentSection.js +1 -0
- package/dist/components/results/report/ResultErrorState.d.ts +14 -0
- package/dist/components/results/report/ResultErrorState.js +1 -0
- package/dist/components/results/report/ResultLoadingState.d.ts +11 -0
- package/dist/components/results/report/ResultLoadingState.js +1 -0
- package/dist/components/results/report/ResultNotPublicState.d.ts +16 -0
- package/dist/components/results/report/ResultNotPublicState.js +1 -0
- package/dist/components/results/report/SpeakingReportParts.d.ts +4 -0
- package/dist/components/results/report/SpeakingReportParts.js +1 -0
- package/dist/components/results/report/SpeakingReportShared.d.ts +36 -0
- package/dist/components/results/report/SpeakingReportShared.js +1 -0
- package/dist/components/results/report/SpeakingRubricSection.d.ts +21 -0
- package/dist/components/results/report/SpeakingRubricSection.js +1 -0
- package/dist/components/results/report/assets/dofu2.png +0 -0
- package/dist/components/results/report/assets/dofu4.png +0 -0
- package/dist/components/results/report/assets/logo-ocean.png +0 -0
- package/dist/components/results/report/assets/penguin-main.png +0 -0
- package/dist/components/results/report/assets/penguin-small.png +0 -0
- package/dist/components/results/report/speaking-report.utils.d.ts +37 -0
- package/dist/components/results/report/speaking-report.utils.js +1 -0
- package/dist/components/results/review-data.d.ts +1 -0
- package/dist/components/results/review-data.js +1 -1
- package/dist/components/results/review-status.utils.d.ts +12 -0
- package/dist/components/results/review-status.utils.js +1 -0
- package/dist/components/results/review-types.d.ts +14 -0
- package/dist/components/results/speaking/SpeakingResultAudioPlayer.d.ts +17 -0
- package/dist/components/results/speaking/SpeakingResultAudioPlayer.js +1 -0
- package/dist/components/results/speaking/SpeakingReviewResult.d.ts +30 -0
- package/dist/components/results/speaking/SpeakingReviewResult.js +1 -0
- package/dist/components/results/utils/review-score.utils.d.ts +21 -0
- package/dist/components/results/utils/review-score.utils.js +1 -0
- package/dist/components/results/utils/speaking-summary.d.ts +41 -0
- package/dist/components/results/utils/speaking-summary.js +1 -0
- package/dist/components/results/utils/transform-speaking-review.d.ts +62 -0
- package/dist/components/results/utils/transform-speaking-review.js +1 -0
- package/dist/components/shared/audio/SpeakingAudioPlayer.d.ts +12 -0
- package/dist/components/shared/audio/SpeakingAudioPlayer.js +1 -0
- package/dist/components/shared/audio/SpeakingAudioPlayer.module.css +115 -0
- package/dist/components/shared/audio/assets/fr-image-part1-border.png +0 -0
- package/dist/components/shared/audio/assets/ic-speaker.webp +0 -0
- package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.d.ts +11 -3
- package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +1 -1
- package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.d.ts +5 -1
- package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +1 -1
- package/dist/components/themes/index.d.ts +11 -0
- package/dist/components/themes/index.js +1 -1
- package/dist/components/themes/spaceship/SpaceshipAudioPlayer.d.ts +8 -0
- package/dist/components/themes/spaceship/SpaceshipAudioPlayer.js +1 -0
- package/dist/components/themes/spaceship/SpaceshipLayout.d.ts +2 -0
- package/dist/components/themes/spaceship/SpaceshipLayout.js +1 -0
- package/dist/components/themes/spaceship/assets/images/X-sunset.png +0 -0
- package/dist/components/themes/spaceship/assets/images/X.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/audio-bar.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/background.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/bg_sunset.png +0 -0
- package/dist/components/themes/spaceship/assets/images/big-blue-badge-sunset.png +0 -0
- package/dist/components/themes/spaceship/assets/images/big-blue-badge.png +0 -0
- package/dist/components/themes/spaceship/assets/images/big-blue-badge.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/bingo.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/blue-badge-sunset.png +0 -0
- package/dist/components/themes/spaceship/assets/images/blue-badge.png +0 -0
- package/dist/components/themes/spaceship/assets/images/blue-wrapper-sunset.png +0 -0
- package/dist/components/themes/spaceship/assets/images/blue-wrapper.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/board_red.png +0 -0
- package/dist/components/themes/spaceship/assets/images/button_feedback_vi.png +0 -0
- package/dist/components/themes/spaceship/assets/images/circle-btn-right-sunset.png +0 -0
- package/dist/components/themes/spaceship/assets/images/circle-btn-right.png +0 -0
- package/dist/components/themes/spaceship/assets/images/circle-btn-sunset.png +0 -0
- package/dist/components/themes/spaceship/assets/images/circle-btn.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/focus-vector.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/head-phone_red.png +0 -0
- package/dist/components/themes/spaceship/assets/images/ic-mute-sunset.png +0 -0
- package/dist/components/themes/spaceship/assets/images/ic-mute.png +0 -0
- package/dist/components/themes/spaceship/assets/images/ic-play-sunset.png +0 -0
- package/dist/components/themes/spaceship/assets/images/ic-play-violet.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/ic-play.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/micro.png +0 -0
- package/dist/components/themes/spaceship/assets/images/next-small.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/next-sunset.png +0 -0
- package/dist/components/themes/spaceship/assets/images/next.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/purple-circle.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/question-badge-sunset.png +0 -0
- package/dist/components/themes/spaceship/assets/images/question-badge.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/red-badge-sunset.png +0 -0
- package/dist/components/themes/spaceship/assets/images/red-badge.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/red-wrapper-sunset.png +0 -0
- package/dist/components/themes/spaceship/assets/images/red-wrapper.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/robot-avatar.png +0 -0
- package/dist/components/themes/spaceship/assets/images/sound_red.png +0 -0
- package/dist/components/themes/spaceship/assets/images/student-avatar.png +0 -0
- package/dist/components/themes/spaceship/assets/images/teacher-play-sunset.png +0 -0
- package/dist/components/themes/spaceship/assets/images/teacher-play.webp +0 -0
- package/dist/components/themes/spaceship/assets/images/timer-badge.png +0 -0
- package/dist/components/themes/spaceship/assets/styles/SpaceshipLayout.module.css +347 -0
- package/dist/components/themes/spaceship/assets/styles/SpaceshipWaitingRoom.module.css +395 -0
- package/dist/components/ui/badge.d.ts +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/shared/lib/stores/examTakingStore.d.ts +54 -0
- package/dist/shared/lib/stores/examTakingStore.js +1 -0
- package/dist/shared/lib/stores/gradingTransientStore.d.ts +23 -0
- package/dist/shared/lib/stores/gradingTransientStore.js +1 -0
- package/dist/shared/lib/themes/config/starters.config.d.ts +3 -0
- package/dist/shared/lib/themes/config/starters.config.js +1 -0
- package/dist/shared/lib/themes/index.d.ts +18 -0
- package/dist/shared/lib/themes/index.js +1 -0
- package/dist/shared/lib/themes/types.d.ts +39 -0
- package/dist/shared/lib/themes/types.js +1 -0
- package/dist/shared/lib/toast.d.ts +19 -0
- package/dist/shared/lib/toast.js +1 -0
- package/package.json +12 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as l}from"react/jsx-runtime";import{Activity as t,Award as a,Bot as r,Brain as s,MessageSquare as n,TrendingUp as c}from"lucide-react";import{cn as d}from"../../../shared/lib/utils";export function ReviewSpeakingAIPanel({question:t}){if(!t.studentAudioKey)return null;return null!=t.pronunciationScore||null!=t.accuracyScore||null!=t.fluencyScore||null!=t.prosodyScore||null!=t.completenessScore||!!t.recognizedText||null!=t.contentScore||t.feedbackEn?l("div",{className:"space-y-4",children:[t.classificationTitle?e(i,{title:t.classificationTitle,tag:t.classificationTag}):null,l("div",{className:"grid grid-cols-1 gap-4 lg:grid-cols-2",children:[e(o,{question:t}),null!=t.contentScore||t.feedbackEn?e(x,{contentScore:t.contentScore,feedback:t.feedbackEn}):e(m,{})]})]}):null}function i({title:t,tag:r}){const s={Platinum:{bg:"bg-slate-50 border-slate-200",text:"text-slate-800",iconBg:"bg-slate-200/60"},Gold:{bg:"bg-amber-50/60 border-amber-200/60",text:"text-amber-800",iconBg:"bg-amber-100"},Silver:{bg:"bg-gray-50 border-gray-200",text:"text-gray-700",iconBg:"bg-gray-200"},Bronze:{bg:"bg-orange-50/60 border-orange-200/60",text:"text-orange-800",iconBg:"bg-orange-100"}}[t]??{bg:"bg-blue-50/60 border-blue-100",text:"text-blue-800",iconBg:"bg-blue-100/60"};return l("div",{className:d("flex items-center gap-3 rounded-xl border px-4 py-2.5",s.bg),children:[e("div",{className:d("flex h-8 w-8 items-center justify-center rounded-full",s.iconBg),children:e(a,{className:d("h-4 w-4",s.text)})}),e("div",{className:"flex-1",children:l("div",{className:"flex items-center gap-2",children:[e("span",{className:d("text-sm font-black",s.text),children:t}),r?e("span",{className:"rounded-[4px] border border-gray-100/60 bg-white/70 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider text-gray-600 shadow-sm",children:r}):null]})}),e("span",{className:"text-[10px] font-semibold uppercase tracking-wider text-gray-400",children:"Phân loại năng lực"})]})}function o({question:a}){const r=[{label:"Phát âm",sublabel:"Pronunciation",score:a.pronunciationScore},{label:"Lưu loát",sublabel:"Fluency",score:a.fluencyScore},{label:"Ngữ điệu",sublabel:"Prosody",score:a.prosodyScore},{label:"Trọn vẹn",sublabel:"Completeness",score:a.completenessScore},{label:"Chính xác",sublabel:"Accuracy",score:a.accuracyScore}];return l("div",{className:"flex flex-col rounded-2xl border border-slate-200 bg-slate-50/60 shadow-[0_2px_8px_rgba(15,23,42,0.03)]",children:[l("div",{className:"flex items-center gap-3 px-4 py-3",children:[e(t,{className:"h-5 w-5 text-blue-600"}),e("h4",{className:"text-sm font-black tracking-wide text-slate-800",children:"Phân tích Khẩu hình"})]}),l("div",{className:"mx-4 mb-4 rounded-xl border border-slate-200 bg-white p-3.5 shadow-sm",children:[e("p",{className:"mb-1 text-[9px] font-bold uppercase tracking-wider text-blue-500",children:"AI Transcribed"}),l("p",{className:"text-sm font-medium italic leading-relaxed text-slate-800",children:["“",a.recognizedText||"Không rõ chữ / Không thể phiên âm","”"]})]}),e("div",{className:"grid grid-cols-2 gap-3 px-4 pb-4",children:r.map(l=>e(b,{...l},l.sublabel))})]})}function b({label:t,score:a}){const r=null!=a?Math.round(a):null,s=null==r?{text:"text-slate-400",bar:"bg-slate-200",bg:"bg-white border-slate-100"}:r>=80?{text:"text-emerald-600",bar:"bg-emerald-500",bg:"bg-emerald-50/50 border-emerald-100"}:r>=50?{text:"text-amber-600",bar:"bg-amber-400",bg:"bg-amber-50/50 border-amber-100"}:{text:"text-rose-600",bar:"bg-rose-400",bg:"bg-rose-50/50 border-rose-100"};return l("div",{className:d("flex flex-col gap-2 rounded-[14px] border p-3 shadow-[0_2px_10px_rgba(15,23,42,0.03)]",s.bg),children:[l("div",{className:"flex items-center justify-between",children:[e("span",{className:"text-xs font-black text-slate-700",children:t}),e("span",{className:d("text-lg font-black tracking-tight",s.text),children:null!=r?r:"N/A"})]}),e("div",{className:"h-1.5 w-full overflow-hidden rounded-full bg-slate-100",children:e("div",{className:d("h-full rounded-full transition-all duration-700 ease-out",s.bar),style:{width:`${Math.max(0,Math.min(100,r??0))}%`}})})]})}function x({contentScore:t,feedback:a}){return l("div",{className:"flex h-full flex-col rounded-2xl border border-slate-200 bg-slate-50/60 shadow-[0_2px_8px_rgba(15,23,42,0.03)]",children:[l("div",{className:"flex shrink-0 items-center gap-3 px-4 py-3",children:[e(s,{className:"h-5 w-5 text-blue-600"}),e("h4",{className:"text-sm font-black tracking-wide text-slate-800",children:"Phân tích Nội dung"})]}),null!=t?e("div",{className:"flex flex-col gap-3 px-4 pb-3",children:e(u,{label:"Phân tích Nội dung (LLM)",score:t,icon:e(c,{className:"h-3.5 w-3.5 text-blue-500"})})}):null,a?l("div",{className:"mx-4 mb-4 flex flex-1 flex-col rounded-xl border border-slate-200 border-l-[6px] border-l-blue-500 bg-white p-4 shadow-sm",children:[l("div",{className:"mb-2 flex shrink-0 items-center gap-2",children:[e("div",{className:"flex h-7 w-7 items-center justify-center rounded-full bg-blue-50 text-blue-600",children:e(r,{className:"h-4 w-4"})}),e("span",{className:"text-xs font-black uppercase tracking-wider text-blue-600",children:"Nhận xét AI"})]}),e("div",{className:"flex-1 overflow-y-auto pr-1",children:e("p",{className:"text-[15px] font-medium leading-relaxed text-slate-700",children:a})})]}):null]})}function u({label:t,score:a,icon:r}){const s=Math.round(a),n=s>=80?{bar:"bg-emerald-500",text:"text-emerald-600"}:s>=50?{bar:"bg-amber-400",text:"text-amber-600"}:{bar:"bg-rose-400",text:"text-rose-600"};return l("div",{className:"flex flex-col gap-1.5 rounded-xl border border-slate-200 bg-white p-2.5 shadow-sm",children:[l("div",{className:"flex items-center justify-between",children:[l("div",{className:"flex items-center gap-1.5",children:[e("span",{className:d("opacity-70",n.text),children:r}),e("span",{className:"text-xs font-bold text-slate-700",children:t})]}),e("span",{className:d("text-xs font-black",n.text),children:s})]}),e("div",{className:"h-1 w-full overflow-hidden rounded-full bg-slate-100",children:e("div",{className:d("h-full rounded-full transition-all duration-1000 ease-out",n.bar),style:{width:`${Math.max(0,Math.min(100,a))}%`}})})]})}function m(){return l("div",{className:"flex flex-col items-center justify-center gap-2 rounded-2xl border border-dashed border-slate-200 bg-slate-50/60 p-6 text-center",children:[e(n,{className:"h-5 w-5 text-slate-300"}),l("div",{children:[e("p",{className:"text-sm font-semibold text-slate-500",children:"Không chấm nội dung"}),e("p",{className:"mt-0.5 text-xs text-slate-400",children:"Dạng câu hỏi có kịch bản mẫu"})]})]})}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { SpeakingReviewItem } from './ReviewSpeakingRenderer.types';
|
|
2
|
+
export declare function PromptImage({ imageKey, alt }: {
|
|
3
|
+
imageKey: string;
|
|
4
|
+
alt: string;
|
|
5
|
+
}): import("react").JSX.Element;
|
|
6
|
+
export declare function ResolvedImageItem({ imageKey, alt, label, highlight, aspectRatio, }: {
|
|
7
|
+
imageKey: string;
|
|
8
|
+
alt: string;
|
|
9
|
+
label?: string;
|
|
10
|
+
highlight?: boolean;
|
|
11
|
+
aspectRatio?: string;
|
|
12
|
+
}): import("react").JSX.Element;
|
|
13
|
+
export declare function CompareImagesSection({ candidateImageUrl, examinerImageUrl, }: {
|
|
14
|
+
candidateImageUrl: string;
|
|
15
|
+
examinerImageUrl: string;
|
|
16
|
+
}): import("react").JSX.Element;
|
|
17
|
+
export declare function StoryImagesSection({ images }: {
|
|
18
|
+
images: string[];
|
|
19
|
+
}): import("react").JSX.Element;
|
|
20
|
+
export declare function InfoExchangeSection({ data, }: {
|
|
21
|
+
data: NonNullable<SpeakingReviewItem['infoExchange']>;
|
|
22
|
+
}): import("react").JSX.Element;
|
|
23
|
+
export declare function OddOneOutImagesSection({ groupImages, contentImages, activeId, }: {
|
|
24
|
+
groupImages: Array<{
|
|
25
|
+
id: string;
|
|
26
|
+
url: string;
|
|
27
|
+
label: string;
|
|
28
|
+
}>;
|
|
29
|
+
contentImages: string[];
|
|
30
|
+
activeId?: string;
|
|
31
|
+
}): import("react").JSX.Element;
|
|
32
|
+
export declare function AudioSection({ title, subtitle, icon, audioKey, emptyMessage, useListeningBar, }: {
|
|
33
|
+
title: string;
|
|
34
|
+
subtitle: string;
|
|
35
|
+
icon: React.ReactNode;
|
|
36
|
+
audioKey: string;
|
|
37
|
+
emptyMessage: string;
|
|
38
|
+
useListeningBar?: boolean;
|
|
39
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import{ImageIcon as t,ImageOff as r}from"lucide-react";import{useState as l}from"react";import{ResolvedImage as s}from"../../../components/common/ResolvedImage";import{usePresignedFileUrl as i}from"../../../shared/lib/hooks/usePresignedFileUrl";import{cn as n}from"../../../shared/lib/utils";import{SpeakingResultAudioPlayer as d}from"../speaking/SpeakingResultAudioPlayer";import{ListeningAudioBar as o}from"./ReviewListeningHeader";export function PromptImage({imageKey:s,alt:n}){const{previewUrl:d,isLoading:o}=i(s||void 0),[c,m]=l(!1);return s?a("div",{className:"rounded-lg border border-gray-200 bg-white p-4 shadow-sm",children:[a("div",{className:"mb-3 flex items-center gap-2",children:[e(t,{className:"h-4 w-4 text-violet-500"}),e("p",{className:"text-sm font-semibold text-gray-700",children:"Question image"})]}),e("div",{className:"flex min-h-[200px] items-center justify-center rounded-lg bg-gray-50 p-4",children:o?e("span",{className:"text-sm text-gray-400",children:"Đang tải hình ảnh..."}):d&&!c?e("img",{src:d,alt:n,className:"max-h-[320px] w-auto rounded-lg object-contain",onError:()=>m(!0)}):a("div",{className:"flex flex-col items-center justify-center text-gray-400",children:[e(r,{className:"mb-2 h-8 w-8"}),e("span",{className:"text-sm",children:"Không thể tải hình ảnh"})]})})]}):null}export function ResolvedImageItem({imageKey:t,alt:s,label:d,highlight:o,aspectRatio:c="3.3"}){const{previewUrl:m,isLoading:g}=i(t||void 0),[h,x]=l(!1);return a("div",{className:n("relative w-full overflow-hidden rounded-xl border-2 bg-slate-50",o?"border-amber-400 ring-2 ring-amber-300":"border-slate-200"),style:{aspectRatio:c},children:[d&&e("span",{className:"absolute left-1/2 top-2 z-10 -translate-x-1/2 rounded-full bg-slate-800/80 px-3 py-0.5 text-xs font-bold uppercase tracking-wide text-white shadow backdrop-blur-sm",children:d}),g?e("div",{className:"flex h-full w-full items-center justify-center",children:e("span",{className:"text-xs text-gray-400",children:"Đang tải..."})}):m&&!h?e("img",{src:m,alt:s,className:"h-full w-full object-cover",onError:()=>x(!0)}):a("div",{className:"flex h-full w-full flex-col items-center justify-center text-gray-400",children:[e(r,{className:"mb-2 h-6 w-6"}),e("span",{className:"text-xs",children:"Không có ảnh"})]})]})}export function CompareImagesSection({candidateImageUrl:r,examinerImageUrl:l}){return r||l?a("div",{className:"space-y-3 rounded-lg border border-gray-200 bg-white p-4 shadow-sm",children:[a("div",{className:"mb-1 flex items-center gap-2",children:[e(t,{className:"h-4 w-4 text-violet-500"}),e("p",{className:"text-sm font-semibold text-gray-700",children:"Hình ảnh câu hỏi"})]}),e(ResolvedImageItem,{imageKey:r,alt:"Candidate's copy",label:"Candidate's copy"}),e(ResolvedImageItem,{imageKey:l,alt:"Examiner's copy",label:"Examiner's copy"})]}):null}export function StoryImagesSection({images:r}){return r&&0!==r.length?a("div",{className:"rounded-lg border border-gray-200 bg-white p-4 shadow-sm",children:[a("div",{className:"mb-3 flex items-center gap-2",children:[e(t,{className:"h-4 w-4 text-violet-500"}),a("p",{className:"text-sm font-semibold text-gray-700",children:["Hình ảnh câu chuyện (",r.length," ảnh)"]})]}),e("div",{className:"grid grid-cols-2 gap-3",children:r.map((a,t)=>e(ResolvedImageItem,{imageKey:a,alt:`Story image ${t+1}`,label:`Ảnh ${t+1}`,aspectRatio:"1/1"},a+t))})]}):null}export function InfoExchangeSection({data:r}){const{title:l,imageUrl:d,candidateFields:o,examinerFields:c}=r,m=o.length>0?o:c,{previewUrl:g,isLoading:h}=i(d||void 0);return a("div",{className:"space-y-3",children:[l?e("h3",{className:"text-sm font-bold text-gray-800",children:l}):null,d?e("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:h?e("div",{className:"flex h-[220px] items-center justify-center text-sm text-gray-400",children:"Loading image..."}):g?e(s,{src:d,alt:l||"Info exchange",className:"mx-auto h-auto max-h-[260px] w-auto max-w-full object-contain p-2"}):a("div",{className:"flex h-[220px] items-center justify-center text-sm text-gray-400",children:[e(t,{className:"mr-2 h-4 w-4"}),"Image unavailable"]})}):null,m.length>0?e("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm",children:e("table",{className:"w-full text-sm",children:e("tbody",{children:m.map((t,r)=>a("tr",{className:n("border-b border-gray-100 transition-colors last:border-0 hover:bg-blue-50/50",r%2==0?"bg-white":"bg-gray-50/50"),children:[e("td",{className:"w-1/3 px-4 py-2.5 font-bold text-gray-700",children:t.key}),e("td",{className:"px-4 py-2.5",children:t.value||t.answer?e("span",{className:"font-medium text-gray-800",children:t.value||t.answer}):e("span",{className:"italic text-gray-300",children:"—"})})]},`${t.key}-${r}`))})})}):null]})}export function OddOneOutImagesSection({groupImages:r,contentImages:l,activeId:s}){const i=r.length>0?r:l.map((e,a)=>({id:String(a),url:e,label:String(a+1)}));return 0===i.length?null:a("div",{className:"rounded-lg border border-gray-200 bg-white p-4 shadow-sm",children:[a("div",{className:"mb-3 flex items-center gap-2",children:[e(t,{className:"h-4 w-4 text-violet-500"}),e("p",{className:"text-sm font-semibold text-gray-700",children:"Hình ảnh Odd One Out"})]}),e("div",{className:"grid grid-cols-2 gap-3",children:i.map(a=>e(ResolvedImageItem,{imageKey:a.url,alt:`Image ${a.label}`,label:a.id===s?`Ảnh ${a.label} ★`:`Ảnh ${a.label}`,highlight:a.id===s},a.id))})]})}export function AudioSection({title:t,subtitle:r,icon:l,audioKey:s,emptyMessage:i,useListeningBar:n=!1}){return a("div",{className:"audio-player-wrapper rounded-lg border border-gray-200 bg-white p-4 shadow-sm",children:[a("div",{className:"mb-3 flex items-center gap-2",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-full bg-gray-100 text-gray-700",children:l}),a("div",{children:[e("p",{className:"text-sm font-semibold text-gray-800",children:t}),e("p",{className:"text-xs text-gray-500",children:r})]})]}),s?n?e(o,{audioKey:s}):e(d,{audioKey:s,variant:"admin"}):e("div",{className:"rounded-lg border border-dashed border-gray-300 bg-gray-50 px-4 py-3 text-sm text-gray-500",children:i})]})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Check as t,Mic as a,Volume2 as s,X as l}from"lucide-react";import{Badge as n}from"../../../components/ui/badge";import{cn as i}from"../../../shared/lib/utils";import{ReviewSpeakingAIPanel as d}from"./ReviewSpeakingAIPanel";import{AudioSection as c,CompareImagesSection as o,InfoExchangeSection as m,OddOneOutImagesSection as u,PromptImage as p,StoryImagesSection as b}from"./ReviewSpeakingMedia";import{AI_PANEL_QUESTION_TYPES as h,LISTENING_AUDIO_BAR_QUESTION_TYPES as g,NO_RECORDING_QUESTION_TYPES as x,getAnswerStatus as f,getQuestionTypeLabel as N}from"./ReviewSpeakingRenderer.types";function y({question:t}){const a=f(t);return r("div",{className:i("rounded-lg border px-3 py-2 text-sm shadow-sm","correct"===a?"border-green-200 bg-green-50 text-green-700":"incorrect"===a?"border-red-200 bg-red-50 text-red-700":"border-blue-200 bg-blue-50 text-blue-700"),children:[e("p",{className:"text-[11px] font-semibold uppercase tracking-wide opacity-80",children:"Score"}),r("div",{className:"mt-1 flex items-baseline gap-1",children:[e("span",{className:"text-xl font-bold leading-none",children:t.score??"—"}),r("span",{className:"text-xs font-medium opacity-80",children:["/ ",t.maxScore]})]})]})}function w({status:r}){return e("div","correct"===r?{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})}:"incorrect"===r?{className:"flex h-6 w-6 items-center justify-center rounded-full bg-red-500",children:e(l,{className:"h-4 w-4 text-white",strokeWidth:3})}:{className:"flex h-6 w-6 items-center justify-center rounded-full bg-blue-500",children:e(t,{className:"h-4 w-4 text-white",strokeWidth:3})})}function v({question:t}){return r("div",{className:"space-y-3",children:[e(d,{question:t}),t.expectedAnswers.length>0?r("div",{className:"rounded-lg border border-blue-100 bg-blue-50 p-3",children:[e("p",{className:"mb-2 text-xs font-semibold uppercase tracking-wide text-blue-700",children:"Expected answer"}),e("div",{className:"flex flex-wrap gap-2",children:t.expectedAnswers.map(r=>e("span",{className:"rounded-full border border-blue-200 bg-white px-3 py-1 text-sm font-medium text-blue-700",children:r},r))})]}):null,t.wordErrors.length>0?r("div",{className:"rounded-lg border border-amber-100 bg-amber-50 p-3",children:[e("p",{className:"mb-2 text-xs font-semibold uppercase tracking-wide text-amber-700",children:"Pronunciation notes"}),e("div",{className:"flex flex-wrap gap-2",children:t.wordErrors.map((e,t)=>r("span",{className:"rounded-full border border-amber-200 bg-white px-3 py-1 text-sm text-amber-800",children:[e.word,e.errorType?` · ${e.errorType}`:"","number"==typeof e.accuracyScore?` · ${e.accuracyScore}%`:""]},`${e.word}-${t}`))})]}):null]})}function I({label:t,value:a}){const s=a??0;return r("div",{className:"rounded-lg border border-gray-100 bg-white px-3 py-2 shadow-sm",children:[r("div",{className:"mb-1 flex items-center justify-between gap-3",children:[e("p",{className:"text-sm font-medium text-gray-700",children:t}),e("p",{className:"text-sm font-semibold text-emerald-600",children:null!=a?`${a}%`:"—"})]}),e("div",{className:"h-2 overflow-hidden rounded-full bg-emerald-100",children:e("div",{className:"h-full rounded-full bg-emerald-500 transition-all",style:{width:`${Math.max(0,Math.min(100,s))}%`}})})]})}function _({question:t}){return r("div",{className:"grid gap-3 lg:grid-cols-[minmax(0,1fr)_minmax(0,1fr)]",children:[r("div",{className:"rounded-xl border border-slate-200 bg-white p-4 shadow-sm",children:[e("div",{className:"mb-3 flex items-center gap-2",children:e("p",{className:"text-sm font-semibold text-slate-800",children:"Phân tích AI"})}),r("div",{className:"space-y-2.5",children:[e(I,{label:"Pronunciation",value:t.pronunciationScore}),e(I,{label:"Accuracy",value:t.accuracyScore}),e(I,{label:"Fluency",value:t.fluencyScore}),e(I,{label:"Prosody",value:t.prosodyScore}),e(I,{label:"Completeness",value:t.completenessScore}),e(I,{label:"Content",value:t.contentScore})]}),t.classificationTag?r("div",{className:"mt-3 rounded-lg border border-emerald-100 bg-emerald-50 px-3 py-2",children:[e("p",{className:"text-xs font-semibold uppercase tracking-wide text-emerald-700",children:"Speaking level"}),e("p",{className:"mt-1 text-sm font-medium text-emerald-700",children:t.classificationTag})]}):null]}),r("div",{className:"rounded-xl border border-sky-200 bg-white p-4 shadow-sm",children:[e("p",{className:"mb-3 text-sm font-semibold text-slate-800",children:"Phản hồi AI"}),r("div",{className:"space-y-3",children:[r("div",{className:"rounded-lg border border-slate-200 bg-slate-50 p-3",children:[e("p",{className:"mb-1 text-xs font-semibold uppercase tracking-wide text-slate-500",children:"Recognized text"}),e("p",{className:"text-sm leading-6 text-slate-700",children:t.recognizedText||"Chưa có transcript từ hệ thống."})]}),r("div",{className:"rounded-lg border border-sky-100 bg-sky-50 p-3",children:[e("p",{className:"mb-1 text-xs font-semibold uppercase tracking-wide text-sky-700",children:"Feedback"}),e("p",{className:"text-sm leading-6 text-slate-700",children:t.feedbackEn||"Chưa có nhận xét chi tiết từ hệ thống."})]}),t.expectedAnswers.length>0?r("div",{className:"rounded-lg border border-blue-100 bg-blue-50 p-3",children:[e("p",{className:"mb-2 text-xs font-semibold uppercase tracking-wide text-blue-700",children:"Expected answer"}),e("div",{className:"flex flex-wrap gap-2",children:t.expectedAnswers.map(r=>e("span",{className:"rounded-full border border-blue-200 bg-white px-3 py-1 text-sm font-medium text-blue-700",children:r},r))})]}):null,t.wordErrors.length>0?r("div",{className:"rounded-lg border border-amber-100 bg-amber-50 p-3",children:[e("p",{className:"mb-2 text-xs font-semibold uppercase tracking-wide text-amber-700",children:"Pronunciation notes"}),e("div",{className:"flex flex-wrap gap-2",children:t.wordErrors.map((e,t)=>r("span",{className:"rounded-full border border-amber-200 bg-white px-3 py-1 text-sm text-amber-800",children:[e.word,e.errorType?` · ${e.errorType}`:"","number"==typeof e.accuracyScore?` · ${e.accuracyScore}%`:""]},`${e.word}-${t}`))})]}):null]})]})]})}export function ReviewSpeakingQuestionItem({question:t,hidePerQuestionGrading:l=!1}){const i=f(t),d=h.has(t.questionType),I=x.has(t.questionType);return r("div",{className:"rounded-2xl border border-slate-200 bg-white p-4 shadow-[0_8px_24px_rgba(15,23,42,0.06)]",children:[r("div",{className:"mb-4 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:t.questionNumber}),r("div",{className:"min-w-0 flex-1",children:[r("div",{className:"mb-2 flex flex-wrap items-center gap-2",children:[e(n,{variant:"secondary",className:"bg-slate-100 text-slate-700 hover:bg-slate-100",children:N(t.questionType)}),!l&&t.classificationTitle?e(n,{variant:"outline",className:"border-amber-200 bg-amber-50 text-amber-700 hover:bg-amber-50",children:t.classificationTitle}):null,!l&&e(w,{status:i})]}),t.promptText?"SPEAKING_CUE_CARD"===t.questionType?e("div",{className:"prose prose-sm max-w-none text-slate-800 [&_ul]:list-disc [&_ul]:pl-5 [&_ol]:list-decimal [&_ol]:pl-5 [&_li]:my-0.5 [&_p]:my-1",dangerouslySetInnerHTML:{__html:t.promptText}}):e("p",{className:"whitespace-pre-line text-base font-semibold leading-relaxed text-slate-800",children:t.promptText}):null]}),!l&&e(y,{question:t})]}),r("div",{className:"space-y-3",children:[t.promptAudioKey?e(c,{title:"Question audio",subtitle:"Nghe lại âm thanh câu hỏi",icon:e(s,{className:"h-4 w-4"}),audioKey:t.promptAudioKey,emptyMessage:"Không có audio câu hỏi cho mục này.",useListeningBar:g.has(t.questionType)}):null,!I&&e(c,{title:"Student recording",subtitle:"Phát lại câu trả lời đã nộp",icon:e(a,{className:"h-4 w-4"}),audioKey:t.studentAudioKey,emptyMessage:"Học sinh không gửi câu trả lời ghi âm cho câu hỏi này.",useListeningBar:g.has(t.questionType)}),"LISTEN_AND_SPEAK_COMPARE_IMAGES"===t.questionType&&t.compareImages?e(o,{candidateImageUrl:t.compareImages.candidateImageUrl,examinerImageUrl:t.compareImages.examinerImageUrl}):"LISTEN_AND_SPEAK_WITH_STORY_IMAGES"===t.questionType&&t.storyImages?e(b,{images:t.storyImages}):"LISTEN_AND_SPEAK_INFO_EXCHANGE"===t.questionType&&t.infoExchange?e(m,{data:t.infoExchange}):"LISTEN_AND_SPEAK_ODD_ONE_OUT"===t.questionType&&t.oddOneOutImages?e(u,{groupImages:t.oddOneOutImages.groupImages,contentImages:t.oddOneOutImages.contentImages,activeId:t.oddOneOutImages.activeId}):t.promptImageKey?e(p,{imageKey:t.promptImageKey,alt:`Question ${t.questionNumber}`}):null,l?null:e(d?v:_,{question:t})]})]})}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { ReviewSpeakingRendererProps, SpeakingReviewItem } from './ReviewSpeakingRenderer.types';
|
|
2
|
+
export type { ReviewSpeakingRendererProps, SpeakingReviewItem };
|
|
3
|
+
export declare function ReviewSpeakingRenderer({ partNumber, partName, instruction, questions, hidePerQuestionGrading, }: ReviewSpeakingRendererProps): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as i}from"react/jsx-runtime";import r from"../../../components/themes/cambridge-yle/CambridgeYleInstructionBanner";import n from"../../../components/themes/cambridge-yle/CambridgeYlePartBanner";import{ReviewSpeakingQuestionItem as t}from"./ReviewSpeakingQuestionItem";export function ReviewSpeakingRenderer({partNumber:s,partName:o,instruction:a,questions:m,hidePerQuestionGrading:u=!1}){return i("div",{className:"flex flex-col gap-4 pt-6",children:[e(n,{partNumber:s,questionCount:m.length,partName:o,isReviewMode:!0}),e(r,{instruction:a||"Listen and review your speaking answers.",isReviewMode:!0}),m.map(i=>e(t,{question:i,hidePerQuestionGrading:u},i.questionId))]})}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared types for speaking review renderers.
|
|
3
|
+
* Source: oe-exam-fe ReviewSpeakingRenderer.tsx
|
|
4
|
+
*/
|
|
5
|
+
export interface SpeakingReviewItem {
|
|
6
|
+
questionId: string;
|
|
7
|
+
questionNumber: number;
|
|
8
|
+
questionType: 'READ_DISPLAYED_CONTENT' | 'SPEAKING_DESCRIBE_IMAGE' | 'SPONTANEOUS_QA' | string;
|
|
9
|
+
promptText: string;
|
|
10
|
+
promptAudioKey: string;
|
|
11
|
+
promptImageKey: string;
|
|
12
|
+
studentAudioKey: string;
|
|
13
|
+
score: number | null;
|
|
14
|
+
maxScore: number;
|
|
15
|
+
isCorrect: boolean | null;
|
|
16
|
+
expectedAnswers: string[];
|
|
17
|
+
recognizedText: string;
|
|
18
|
+
pronunciationScore: number | null;
|
|
19
|
+
accuracyScore: number | null;
|
|
20
|
+
fluencyScore: number | null;
|
|
21
|
+
prosodyScore: number | null;
|
|
22
|
+
completenessScore: number | null;
|
|
23
|
+
wordErrors: Array<{
|
|
24
|
+
word: string;
|
|
25
|
+
errorType?: string;
|
|
26
|
+
accuracyScore?: number;
|
|
27
|
+
}>;
|
|
28
|
+
weightedScore: number | null;
|
|
29
|
+
contentScore: number | null;
|
|
30
|
+
feedbackEn: string;
|
|
31
|
+
classificationTitle: string;
|
|
32
|
+
classificationTag: string;
|
|
33
|
+
compareImages?: {
|
|
34
|
+
candidateImageUrl: string;
|
|
35
|
+
examinerImageUrl: string;
|
|
36
|
+
};
|
|
37
|
+
storyImages?: string[];
|
|
38
|
+
oddOneOutImages?: {
|
|
39
|
+
groupImages: Array<{
|
|
40
|
+
id: string;
|
|
41
|
+
url: string;
|
|
42
|
+
label: string;
|
|
43
|
+
}>;
|
|
44
|
+
contentImages: string[];
|
|
45
|
+
activeId?: string;
|
|
46
|
+
};
|
|
47
|
+
infoExchange?: {
|
|
48
|
+
title: string;
|
|
49
|
+
imageUrl: string;
|
|
50
|
+
candidateFields: Array<{
|
|
51
|
+
key: string;
|
|
52
|
+
value?: string;
|
|
53
|
+
answer?: string;
|
|
54
|
+
}>;
|
|
55
|
+
examinerFields: Array<{
|
|
56
|
+
key: string;
|
|
57
|
+
value?: string;
|
|
58
|
+
answer?: string;
|
|
59
|
+
}>;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export interface ReviewSpeakingRendererProps {
|
|
63
|
+
partNumber: number;
|
|
64
|
+
partName?: string;
|
|
65
|
+
instruction?: string;
|
|
66
|
+
questions: SpeakingReviewItem[];
|
|
67
|
+
/**
|
|
68
|
+
* Hide per-question grading (score pill, verdict, classification, AI panel),
|
|
69
|
+
* keeping prompt + audio only. Used for KINDY VER3 / PRESCHOOL rubric parts.
|
|
70
|
+
*/
|
|
71
|
+
hidePerQuestionGrading?: boolean;
|
|
72
|
+
}
|
|
73
|
+
/** Question types where the question audio uses the compact listening audio bar. */
|
|
74
|
+
export declare const LISTENING_AUDIO_BAR_QUESTION_TYPES: Set<string>;
|
|
75
|
+
/** Question types that receive the redesigned AI review panel. */
|
|
76
|
+
export declare const AI_PANEL_QUESTION_TYPES: Set<string>;
|
|
77
|
+
/** Question types where the student selects an answer (no audio recording). */
|
|
78
|
+
export declare const NO_RECORDING_QUESTION_TYPES: Set<string>;
|
|
79
|
+
export declare function getQuestionTypeLabel(questionType: string): string;
|
|
80
|
+
export declare function getAnswerStatus(question: SpeakingReviewItem): 'correct' | 'incorrect' | 'unanswered';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const LISTENING_AUDIO_BAR_QUESTION_TYPES=new Set(["LISTEN_AND_SPEAK_QUESTION_LIST","LISTEN_AND_SPEAK_IMAGE_GROUP","LISTEN_AND_SPEAK_ANSWER","LISTEN_AND_SPEAK_COMPARE_IMAGES","LISTEN_AND_SPEAK_WITH_STORY_IMAGES","LISTEN_AND_SPEAK_INFO_EXCHANGE","LISTEN_AND_SPEAK_ODD_ONE_OUT"]);export const AI_PANEL_QUESTION_TYPES=new Set(["READ_DISPLAYED_CONTENT","SPEAKING_DESCRIBE_IMAGE","SPEAKING_CUE_CARD","SPONTANEOUS_QA","LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE","LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP","LISTEN_AND_DRAG_OBJECTS_INTO_SCENE","LISTEN_AND_SPEAK_ANSWER","LISTEN_AND_SPEAK_IMAGE_GROUP","LISTEN_AND_SPEAK_QUESTION_LIST","LISTEN_AND_SPEAK_COMPARE_IMAGES","LISTEN_AND_SPEAK_WITH_STORY_IMAGES","LISTEN_AND_SPEAK_INFO_EXCHANGE","LISTEN_AND_SPEAK_ODD_ONE_OUT"]);export const NO_RECORDING_QUESTION_TYPES=new Set(["LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE","LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP","LISTEN_AND_DRAG_OBJECTS_INTO_SCENE"]);export function getQuestionTypeLabel(_){switch(_){case"READ_DISPLAYED_CONTENT":return"Read aloud";case"SPEAKING_DESCRIBE_IMAGE":return"Describe image";case"SPEAKING_CUE_CARD":return"Cue card";case"SPONTANEOUS_QA":return"Q&A";case"LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE":return"Choose object";case"LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP":return"Choose answer";case"LISTEN_AND_DRAG_OBJECTS_INTO_SCENE":return"Drag object";case"LISTEN_AND_SPEAK_ANSWER":return"Speak answer";case"LISTEN_AND_SPEAK_IMAGE_GROUP":return"Speak image";case"LISTEN_AND_SPEAK_QUESTION_LIST":return"Question list";case"LISTEN_AND_SPEAK_COMPARE_IMAGES":return"Compare images";case"LISTEN_AND_SPEAK_WITH_STORY_IMAGES":return"Story images";case"LISTEN_AND_SPEAK_INFO_EXCHANGE":return"Info exchange";case"LISTEN_AND_SPEAK_ODD_ONE_OUT":return"Odd one out";default:return _.replaceAll("_"," ")}}export function getAnswerStatus(_){return _.studentAudioKey?!0===_.isCorrect?"correct":!1===_.isCorrect?"incorrect":"unanswered":"unanswered"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ReviewQuestionPart } from '../review-types';
|
|
2
|
+
export interface DedicatedBodyContext {
|
|
3
|
+
part: ReviewQuestionPart;
|
|
4
|
+
answers: Record<string, unknown>;
|
|
5
|
+
isCorrectMap: Record<string, boolean | null>;
|
|
6
|
+
correctAnswerTextMap: Record<string, string>;
|
|
7
|
+
correctOptionIdMap: Record<string, string>;
|
|
8
|
+
rawCorrectAnswerMap: Record<string, unknown>;
|
|
9
|
+
questionScoreMap: Record<string, {
|
|
10
|
+
score: number | null;
|
|
11
|
+
maxScore: number;
|
|
12
|
+
}>;
|
|
13
|
+
hidePerQuestionGrading: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function renderDedicatedReviewBody(ctx: DedicatedBodyContext): React.ReactNode | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e}from"react/jsx-runtime";import{transformChooseCorrectAdjective as r,transformChooseCorrectAnswer as t,transformColorObjects as s,transformFillInBlank as n,transformWriteCorrectVerbForm as i}from"../../../components/exams/take/utils/question-transformers";import{normalizeColoringAssignments as o}from"../../../components/exams/take/utils/coloring-assignments";import{ReviewChooseAdjectiveRenderer as a}from"./ReviewChooseAdjectiveRenderer";import{ReviewChooseBestAnswerRenderer as u}from"./ReviewChooseBestAnswerRenderer";import{ReviewColoringRenderer as c}from"./ReviewColoringRenderer";import{ReviewListenAndWriteRenderer as m}from"./ReviewListenAndWriteRenderer";import{ReviewListenChooseAnswerGroupRenderer as p}from"./ReviewListenChooseAnswerGroupRenderer";import{ReviewListenChooseObjectsRenderer as _}from"./ReviewListenChooseObjectsRenderer";import{ReviewListenDragObjectsRenderer as N}from"./ReviewListenDragObjectsRenderer";import{ReviewSpeakingRenderer as E}from"./ReviewSpeakingRenderer";import{mapSpeakingReviewItems as A}from"./review-speaking-map";export function renderDedicatedReviewBody(S){const{part:l,answers:R,isCorrectMap:I,correctAnswerTextMap:d,correctOptionIdMap:O,rawCorrectAnswerMap:w,questionScoreMap:T,hidePerQuestionGrading:M}=S,{partNo:g,questionType:q,questions:U}=l,D=U.length,L=U;switch(q){case"FILL_IN_BLANK":{const r=n(L);return e(m,{partNumber:g,questionCount:D,partName:l.name,instruction:l.instructions||r.instruction,title:r.title,imageUrl:r.imageUrl,audioUrl:r.audioUrl,exampleText:r.exampleText,exampleHighlight:r.exampleHighlight,questions:r.questions,answers:R,isCorrectMap:I,correctAnswerTextMap:d})}case"WRITE_CORRECT_VERB_FORM":{const r=i(L);return e(m,{partNumber:g,questionCount:D,partName:l.name,instruction:l.instructions||r.instruction,title:r.title,questions:r.questions,answers:R,isCorrectMap:I,correctAnswerTextMap:d})}case"CHOOSE_THE_CORRECT_ANSWER":{const r=t(L);return e(u,{partNumber:g,questionCount:D,partName:l.name,instruction:l.instructions||r.instruction,imageUrl:r.groupImageUrl,audioUrl:r.audioUrl||l.audioUrl,groupContent:r.groupContent,articles:r.articles,questions:r.questions,answers:R,isCorrectMap:I,correctOptionIdMap:O,example:r.example})}case"CHOOSE_CORRECT_ADJECTIVE":{const t=r(L);return e(a,{partNumber:g,questionCount:D,partName:l.name,instruction:l.instructions||t.instruction,questions:t.questions,answers:R,isCorrectMap:I,correctOptionIdMap:O})}case"READ_AND_COLOR_OBJECTS":{const r=s(L),t=R,n=U[0],i=n?C(w[n.id]).answers:{};return e(c,{partNumber:g,questionCount:D,instructions:r.instructions,instruction:l.instructions||r.instruction,partName:l.name,imageUrl:r.imageUrl,audioUrl:r.audioUrl,hintItems:r.hintItems,regions:r.regions,assignments:t,correctAnswers:i||{}})}case"IMAGE_OBJECT_MATCHING":{const r=s(L),t=o(R,r.hintItems),n=U[0],i=n?C(w[n.id]).answers:{};return e(c,{partNumber:g,questionCount:D,instructions:r.instructions,instruction:l.instructions||r.instruction,partName:l.name,imageUrl:r.imageUrl,audioUrl:r.audioUrl||l.audioUrl,hintItems:r.hintItems,regions:r.regions,assignments:t,correctAnswers:i||{}})}case"LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE":return e(_,{partNumber:g,questionCount:D,partName:l.name,questions:U,studentAnswers:R,rawCorrectAnswerMap:w,isCorrectMap:I,questionScoreMap:T});case"LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP":return e(p,{partNumber:g,questionCount:D,partName:l.name,questions:U,studentAnswers:R,rawCorrectAnswerMap:w,isCorrectMap:I,questionScoreMap:T});case"LISTEN_AND_DRAG_OBJECTS_INTO_SCENE":return e(N,{partNumber:g,questionCount:D,partName:l.name,questions:U,studentAnswers:R,rawCorrectAnswerMap:w,isCorrectMap:I,questionScoreMap:T});case"READ_DISPLAYED_CONTENT":case"SPEAKING_DESCRIBE_IMAGE":case"SPEAKING_CUE_CARD":case"SPONTANEOUS_QA":case"LISTEN_AND_SPEAK_ANSWER":case"LISTEN_AND_SPEAK_IMAGE_GROUP":case"LISTEN_AND_SPEAK_QUESTION_LIST":case"LISTEN_AND_SPEAK_COMPARE_IMAGES":case"LISTEN_AND_SPEAK_WITH_STORY_IMAGES":case"LISTEN_AND_SPEAK_INFO_EXCHANGE":case"LISTEN_AND_SPEAK_ODD_ONE_OUT":{const r=A(U,q,R,I);return e(E,{partNumber:g,partName:l.name,questions:r,hidePerQuestionGrading:M})}default:return null}}function C(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { EssayGradingInfo, ReviewQuestionPart } from '../review-types';
|
|
2
|
+
export interface MoversBodyContext {
|
|
3
|
+
part: ReviewQuestionPart;
|
|
4
|
+
answers: Record<string, unknown>;
|
|
5
|
+
isCorrectMap: Record<string, boolean | null>;
|
|
6
|
+
correctAnswerTextMap: Record<string, string>;
|
|
7
|
+
correctOptionIdMap: Record<string, string>;
|
|
8
|
+
cellAnswersMap: Record<string, {
|
|
9
|
+
cells: Record<string, {
|
|
10
|
+
correctAnswer: string;
|
|
11
|
+
}>;
|
|
12
|
+
caseSensitive: boolean;
|
|
13
|
+
}>;
|
|
14
|
+
essayGradingMap: Record<string, EssayGradingInfo>;
|
|
15
|
+
rawCorrectAnswerMap: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
export declare function renderMoversReviewBody(ctx: MoversBodyContext): React.ReactNode | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e}from"react/jsx-runtime";import{MoversAnswerTheQuestionRenderer as n,MoversFillWordHintRenderer as t,MoversGridFillRenderer as r,MoversLabelThePictureRenderer as s,MoversMatchByWritingAnswerRenderer as o,MoversPictureChooseRenderer as i,MoversPictureFillBlankChooseRenderer as a,MoversReadingPassageRenderer as u,MoversTrueFalseRenderer as c,MoversWordFillParagraphRenderer as m,MoversWordFillStructuredFormRenderer as p,MoversWordOrderingRenderer as l,MoversWriteSentencesRenderer as w,MoversWritingRenderer as g}from"../../../components/exams/take/components/question-renderers";import{transformAnswerTheQuestion as d,transformFillWordHint as C,transformGridFill as R,transformLabelPicture as A,transformMatchByWritingAnswer as N,transformPictureChoose as _,transformPictureFillBlankChoose as M,transformReadingPassage as q,transformTrueFalse as T,transformWordFillParagraph as I,transformWordFillStructuredForm as U,transformWordOrdering as E,transformWriteSentences as h,transformWriteShortLetter as b}from"../../../components/exams/take/utils/question-transformers";const O=()=>{};export function renderMoversReviewBody(S){const{part:v,answers:L,isCorrectMap:x,correctAnswerTextMap:W,correctOptionIdMap:F,cellAnswersMap:D,essayGradingMap:f,rawCorrectAnswerMap:k}=S,{partNo:B,questionType:H,questions:P}=v,Q=P.length,G=P;switch(H){case"WRITE_A_SHORT_LETTER":{const n=G[0];if(!n)return null;const t=b(n),r=L[n.id]||"",s=f[n.id];return e(g,{partNumber:B,partName:v.name,prompt:t.prompt,requirements:t.requirements,minWords:t.minWords,maxWords:t.maxWords,answer:r,onAnswerChange:O,isReviewMode:!0,teacherScore:s?.score,maxScore:s?.maxScore,teacherFeedback:s?.teacherFeedback})}case"LOOK_PICTURE_CHOOSE_CORRECT_ANSWER":{const n=_(G),t=n.subQuestions.map(e=>({...e,correctOptionId:F[e.id]}));return e(i,{partNumber:B,partName:v.name,instruction:v.instructions||n.instruction,questionCount:Q,subQuestions:t,answers:L,onAnswerChange:O,isReviewMode:!0})}case"LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER":{const n=M(G),t=n.subQuestions.map(e=>({...e,correctOptionId:F[e.id]}));return e(a,{partNumber:B,partName:v.name,instruction:v.instructions||n.instruction,questionCount:Q,subQuestions:t,answers:L,onAnswerChange:O,isReviewMode:!0})}case"READ_PASSAGE_AND_ANSWER_QUESTIONS":{const n=q(G);return e(u,{partNumber:B,partName:v.name,instruction:v.instructions||n.instruction,questionCount:n.questions.length,passageTitle:n.passageTitle,passage:n.passage,passageImageUrl:n.passageImageUrl,questions:n.questions,answers:L,onAnswerChange:O,isReviewMode:!0,correctOptionIdMap:F})}case"LABEL_THE_PICTURE":{const n=A(G),t=n.labels.map(e=>({...e,correctAnswer:e.isPreFilled?e.correctAnswer:W[e.id]||e.correctAnswer}));return e(s,{partNumber:B,partName:v.name,instruction:v.instructions||n.instruction,questionCount:n.labels.length,imageUrl:n.imageUrl,wordBank:n.wordBank,labels:t,answers:L,onAnswerChange:O,isReviewMode:!0})}case"LOOK_PICTURE_FILL_WORD_HINT":{const n=C(G),r=n.subQuestions.map(e=>({...e,correctAnswer:W[e.id]}));return e(t,{partNumber:B,partName:v.name,instruction:v.instructions||n.instruction,questionCount:Q,hintLetters:n.hintLetters,subQuestions:r,answers:L,onAnswerChange:O,isReviewMode:!0})}case"FILL_MISSING_WORDS_IN_GRID":{const n=R(G),t=P[0]?.id,s=t?D[t]:null,o=s?.cells||{},i=s?.caseSensitive??!1,a=t?L[t]:null,u=a&&"object"==typeof a&&null!==a&&"answers"in a?a.answers??{}:{};return e(r,{partNumber:B,partName:v.name,instruction:v.instructions||n.instruction,questionCount:Q,grid:n.grid,rows:n.rows,columns:n.columns,cellAnswers:o,answers:u,onAnswerChange:O,isReviewMode:!0,caseSensitive:i,disabledCells:n.disabledCells})}case"ANSWER_THE_QUESTION":{const t=d(G);return e(n,{partNumber:B,partName:v.name,instruction:v.instructions||t.instruction,questionCount:Q,imageUrl:t.imageUrl,audioUrl:v.audioUrl,title:t.title,groupContent:t.groupContent,questions:t.questions,answers:L,onAnswerChange:O,isReviewMode:!0,correctAnswerTextMap:W})}case"TRUE_FALSE":{const n=T(G);return e(c,{partNumber:B,partName:v.name,instruction:v.instructions||n.instruction,questionCount:Q,imageUrl:n.imageUrl,questions:n.questions,answers:L,onAnswerChange:O,isReviewMode:!0,correctAnswerTextMap:W})}case"MATCH_BY_WRITING_ANSWER":{const n=N(G);return e(o,{partNumber:B,partName:v.name,instruction:v.instructions||n.instruction,questionCount:Q,audioUrl:v.audioUrl,options:n.options,questions:n.questions,answers:L,onAnswerChange:O,isReviewMode:!0,viewMode:n.viewMode,groupImageUrl:n.groupImageUrl,correctAnswerTextMap:W,isCorrectMap:x})}case"WORD_ORDERING":{const n=E(G);return e(l,{partNumber:B,partName:v.name,instruction:v.instructions||n.instruction,questionCount:Q,questions:n.questions,answers:L,onAnswerChange:O,isReviewMode:!0,rawCorrectAnswerMap:k,isCorrectMap:x})}case"WORD_FILL_PARAGRAPH":{const n=I(G),t=n.wordBank.filter(e=>e.isExample).length,r=n.paragraph.match(/\{\d+\}/g),s=r?r.length:0,o=Math.max(1,s-t);let i=v.instructions||n.instruction;return i=i.replace(/\{FROM\}/gi,"1").replace(/\{TO\}/gi,String(o)),e(m,{partNumber:B,partName:v.name,questionCount:Q,instruction:i,questionId:n.questionId,paragraph:n.paragraph,title:n.title,imageUrl:n.imageUrl,wordBank:n.wordBank,viewMode:n.viewMode,answers:L,onAnswerChange:O,exampleAnswer:n.exampleAnswer,emailFrom:n.emailFrom,emailTo:n.emailTo,isReviewMode:!0,correctAnswerTextMap:W})}case"WRITE_SENTENCES":{const n=h(G);return e(w,{partNumber:B,partName:v.name,instruction:v.instructions||n.instruction,questionCount:Q,imageUrl:n.imageUrl,title:n.title,groupContent:n.groupContent,questions:n.questions,answers:L,onAnswerChange:O,isReviewMode:!0})}case"WORD_FILL_STRUCTURED_FORM":{const n=U(G);return e(p,{partNumber:B,partName:v.name,instruction:v.instructions||n.instruction,questionCount:Q,audioUrl:n.audioUrl||v.audioUrl,groupTitle:n.groupTitle,groupContent:n.groupContent,groupImageUrl:n.groupImageUrl,questions:n.questions,answers:L,onAnswerChange:O,isReviewMode:!0})}default:return null}}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map review questions into SpeakingReviewItem for ReviewSpeakingRenderer.
|
|
3
|
+
* Source: oe-exam-fe ReviewQuestionRenderer speaking cases.
|
|
4
|
+
*/
|
|
5
|
+
import type { ExamTakingQuestion } from '../../../api/exam-taking';
|
|
6
|
+
import type { SpeakingReviewItem } from './ReviewSpeakingRenderer.types';
|
|
7
|
+
export declare function mapSpeakingReviewItems(questions: ExamTakingQuestion[], questionType: string, answers: Record<string, unknown>, isCorrectMap: Record<string, boolean | null>): SpeakingReviewItem[];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(e){return e&&"object"==typeof e&&!Array.isArray(e)?e:{}}function r(e,r){const t=e[r];return"string"==typeof t?t:""}function t(e,r){const t=e[r];return"number"==typeof t&&Number.isFinite(t)?t:null}function i(t,i,n){if("READ_DISPLAYED_CONTENT"===t)return r(i,"referenceText");if("SPONTANEOUS_QA"===t)return r(i,"questionText");if("SPEAKING_CUE_CARD"===t)return r(i,"cueCardHtml");if("LISTEN_AND_SPEAK_QUESTION_LIST"===t){const r=Array.isArray(n.questions)?n.questions:[],t=i.activeQuestionId,o=r.find(r=>e(r).id===t);return o&&"string"==typeof e(o).text?String(e(o).text):r.map(r=>e(r).text).filter(e=>"string"==typeof e&&e.length>0).join("\n")}return"LISTEN_AND_SPEAK_IMAGE_GROUP"===t||"LISTEN_AND_SPEAK_ANSWER"===t||"LISTEN_AND_SPEAK_COMPARE_IMAGES"===t||"LISTEN_AND_SPEAK_WITH_STORY_IMAGES"===t||"LISTEN_AND_SPEAK_INFO_EXCHANGE"===t||"LISTEN_AND_SPEAK_ODD_ONE_OUT"===t?"":r(i,"question")||r(i,"title")}function n(t,i,n){if("LISTEN_AND_SPEAK_IMAGE_GROUP"===t){const r=Array.isArray(n.images)?n.images:[],t=i.activeImageId,o=r.find(r=>e(r).id===t),a=o?e(o).url:void 0;if("string"==typeof a&&a)return a;const s=r[0]?e(r[0]).url:void 0;return"string"==typeof s?s:""}if("LISTEN_AND_SPEAK_COMPARE_IMAGES"===t)return r(n,"candidateImageUrl")||r(n,"examinerImageUrl");if("LISTEN_AND_SPEAK_WITH_STORY_IMAGES"===t){const e=Array.isArray(i.images)?i.images:[];return"string"==typeof e[0]?e[0]:""}if("LISTEN_AND_SPEAK_ODD_ONE_OUT"===t){const r=Array.isArray(n.images)?n.images:[],t=Array.isArray(i.images)?i.images:[],o=i.activeImageId;if(o&&r.length>0){const t=r.find(r=>e(r).id===o),i=t?e(t).url:void 0;if("string"==typeof i&&i)return i}if(r.length>0){const t=e(r[0]).url;return"string"==typeof t?t:""}return t.length>0&&"string"==typeof t[0]?t[0]:""}return r(i,"backgroundImageUrl")||r(i,"imageUrl")}export function mapSpeakingReviewItems(o,a,s,c){return o.map(o=>{const u=o,y=e(o.content),A=e(s[o.id]),d=e(A.aiAssessment),g=e(d.pronunciation),_=e(A.llmAssessment),f=e(A.classification),l=e(u.correct_answer),p=function(r){const t=r;let i=t.questionGroup?.payload;if("string"==typeof i)try{i=JSON.parse(i)}catch{i={}}return e(i)}(o),S=e(_.feedback),m="string"==typeof S.vi&&S.vi||"string"==typeof S.en&&S.en||"",E="LISTEN_AND_SPEAK_COMPARE_IMAGES"===a?{candidateImageUrl:r(p,"candidateImageUrl"),examinerImageUrl:r(p,"examinerImageUrl")}:void 0,I="LISTEN_AND_SPEAK_WITH_STORY_IMAGES"===a?(Array.isArray(y.images)?y.images:[]).filter(e=>"string"==typeof e):void 0,N="LISTEN_AND_SPEAK_ODD_ONE_OUT"===a?function(r,t){return{groupImages:(Array.isArray(t.images)?t.images:[]).map((r,t)=>{const i=e(r);return{id:"string"==typeof i.id?i.id:String(t),url:"string"==typeof i.url?i.url:"",label:"string"==typeof i.label?i.label:"string"==typeof i.id?i.id:String(t+1)}}),contentImages:(Array.isArray(r.images)?r.images:[]).filter(e=>"string"==typeof e),activeId:"string"==typeof r.activeImageId?r.activeImageId:void 0}}(y,p):void 0,T="LISTEN_AND_SPEAK_INFO_EXCHANGE"===a?function(r){const t=Array.isArray(r.candidateFields)?r.candidateFields.map(r=>{const t=e(r);return{key:"string"==typeof t.key?t.key:"",value:"string"==typeof t.value?t.value:void 0,answer:"string"==typeof t.answer?t.answer:void 0}}):[],i=Array.isArray(r.examinerFields)?r.examinerFields.map(r=>{const t=e(r);return{key:"string"==typeof t.key?t.key:"",value:"string"==typeof t.value?t.value:void 0,answer:"string"==typeof t.answer?t.answer:void 0}}):[];return{title:"string"==typeof r.title?r.title:"",imageUrl:"string"==typeof r.imageUrl?r.imageUrl:"",candidateFields:t,examinerFields:i}}(p):void 0,v=Array.isArray(g.wordErrors)?g.wordErrors.map(r=>{const t=e(r);return{word:"string"==typeof t.word?t.word:"",errorType:"string"==typeof t.errorType?t.errorType:void 0,accuracyScore:"number"==typeof t.accuracyScore?t.accuracyScore:void 0}}):[],O=Array.isArray(l.expectedAnswers)?l.expectedAnswers.filter(e=>"string"==typeof e):[];return{questionId:o.id,questionNumber:o.question_number,questionType:a,promptText:i(a,y,p),promptAudioKey:r(y,"audioUrl")||r(y,"audio")||r(y,"questionAudio"),promptImageKey:n(a,y,p),studentAudioKey:r(A,"audioKey")||r(A,"answer"),score:"number"==typeof u.score?u.score:null,maxScore:Number(u.total_points||0),isCorrect:c[o.id]??null,expectedAnswers:O,recognizedText:r(g,"recognizedText"),pronunciationScore:t(g,"pronunciationScore"),accuracyScore:t(g,"accuracyScore"),fluencyScore:t(g,"fluencyScore"),prosodyScore:t(g,"prosodyScore"),completenessScore:t(g,"completenessScore"),wordErrors:v,weightedScore:t(d,"weightedScore"),contentScore:t(_,"contentScore"),feedbackEn:m,classificationTitle:r(f,"title"),classificationTag:r(f,"tag"),compareImages:E,storyImages:I,oddOneOutImages:N,infoExchange:T}})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";export function BadgeTitle({number:r,title:s}){return t("div",{className:"absolute left-3 top-[-20px] z-10 flex items-center sm:left-5 sm:top-[-22px]",children:[e("div",{className:"relative z-[2] flex h-9 w-9 items-center justify-center rounded-full border-[3px] border-white bg-[radial-gradient(circle,_#2b78c5_0%,_#0054A6_100%)] text-lg font-extrabold text-white shadow-[0_2px_8px_rgba(0,0,0,0.2)] sm:h-11 sm:w-11 sm:text-xl",children:r}),e("div",{className:"ml-[-18px] rounded-r-[20px] bg-[linear-gradient(90deg,#0054A6_0%,#00AEEF_100%)] px-6 py-1.5 pl-7 text-sm font-bold text-white shadow-[2px_2px_5px_rgba(0,0,0,0.1)] [clip-path:polygon(0%_0%,90%_0%,100%_100%,0%_100%)] sm:ml-[-22px] sm:rounded-r-[25px] sm:px-10 sm:py-2 sm:pl-9 sm:text-base",children:s})]})}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ComponentType } from 'react';
|
|
2
|
+
export interface ExamSkillRowProps {
|
|
3
|
+
icon: ComponentType<{
|
|
4
|
+
className?: string;
|
|
5
|
+
}>;
|
|
6
|
+
label: string;
|
|
7
|
+
score: number | null | undefined;
|
|
8
|
+
maxScore: number | null | undefined;
|
|
9
|
+
barClass: string;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Single skill score row for the general exam report (Reading / Listening / …).
|
|
13
|
+
*/
|
|
14
|
+
export declare function ExamSkillRow({ icon: Icon, label, score, maxScore, barClass, }: ExamSkillRowProps): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as t,Fragment as s}from"react/jsx-runtime";import{cn as x}from"../../../shared/lib/utils";import{formatScore as l}from"../utils/review-score.utils";export function ExamSkillRow({icon:r,label:a,score:m,maxScore:d,barClass:i}){if(null==d)return null;const n=d>0?d:0,p=0===n,c=m??0,o=n>0?Math.max(0,Math.min(100,c/n*100)):0,h=l(c,1),b=l(n,0);return t("div",{className:x("mb-2 flex items-center rounded-full border border-[#eef4fb] bg-white px-3 py-2 shadow-[0_1px_0_rgba(255,255,255,0.9)] last:mb-0 sm:mb-3 sm:px-4 sm:py-3",p&&"opacity-70 bg-slate-50 border-slate-200"),children:[e("div",{className:x("flex h-[48px] w-[48px] shrink-0 items-center justify-center rounded-full bg-white xs:h-[52px] xs:w-[52px] sm:h-[58px] sm:w-[58px]",p?"shadow-none border border-slate-100":"shadow-[0_4px_12px_rgba(15,23,42,0.08)]"),children:e("div",{className:x("flex h-[36px] w-[36px] items-center justify-center rounded-full text-white xs:h-[38px] xs:w-[38px] sm:h-[42px] sm:w-[42px]",p?"bg-slate-300":i),children:e(r,{className:"h-5 w-5 text-white"})})}),t("div",{className:"ml-2 mr-2 min-w-0 flex-1 border-r border-dashed border-[#d9e0ea] pr-2 xs:ml-3 xs:mr-3 xs:pr-3 sm:pr-4",children:[e("h4",{className:x("truncate text-[13px] font-extrabold uppercase leading-5 xs:text-[14px] sm:text-[15px]",p?"text-slate-400":"text-[#0054A6]"),children:a}),e("span",{className:x("mt-0.5 block truncate text-[10px] xs:text-[10.5px] sm:mt-1 sm:text-[11px]",p?"text-slate-400":"text-[#8b97a8]"),children:p?"Không áp dụng":"Kỹ năng"})]}),e("div",{className:x("w-[60px] shrink-0 text-[16px] font-extrabold xs:w-[70px] xs:text-[17px] sm:w-20 sm:text-xl",p?"text-slate-400":"text-[#0054A6]"),children:p?"—":t(s,{children:[h," ",t("small",{className:"text-[11px] font-semibold text-[#c5ced8] xs:text-[11.5px] sm:text-[13px]",children:["/",b]})]})}),e("div",{className:"ml-2 h-[10px] flex-1 overflow-hidden rounded-full bg-[#edf0f4] xs:ml-2.5 sm:ml-3 sm:h-[11px]",children:!p&&e("div",{className:x("h-full rounded-full",i),style:{width:`${o}%`}})})]})}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
export interface OceanEduExamReportProps {
|
|
3
|
+
studentName: string;
|
|
4
|
+
studentCode?: string | null;
|
|
5
|
+
branchName?: string | null;
|
|
6
|
+
examDateText: string;
|
|
7
|
+
totalScore: number | null;
|
|
8
|
+
maxScore: number;
|
|
9
|
+
isPendingGrading: boolean;
|
|
10
|
+
examSubTitle?: string | null;
|
|
11
|
+
examTitle?: string | null;
|
|
12
|
+
readingScore?: number | null;
|
|
13
|
+
maxReadingScore?: number | null;
|
|
14
|
+
listeningScore?: number | null;
|
|
15
|
+
maxListeningScore?: number | null;
|
|
16
|
+
writingScore?: number | null;
|
|
17
|
+
maxWritingScore?: number | null;
|
|
18
|
+
speakingScore?: number | null;
|
|
19
|
+
maxSpeakingScore?: number | null;
|
|
20
|
+
overallAssessmentSlot?: ReactNode;
|
|
21
|
+
/** Canonical review URL (with verification token) encoded into the QR code. */
|
|
22
|
+
reviewUrl?: string;
|
|
23
|
+
/** Optional host public path prefix for report assets. Falls back to bundled imports. */
|
|
24
|
+
assetBaseUrl?: string;
|
|
25
|
+
children?: ReactNode;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* General (non-speaking) Ocean Edu exam result report shell.
|
|
29
|
+
* Source: oe-exam-fe/.../review/components/OceanEduExamReport.tsx
|
|
30
|
+
*/
|
|
31
|
+
export declare function OceanEduExamReport({ studentName, studentCode, branchName, examDateText, totalScore, maxScore, isPendingGrading, examSubTitle, examTitle, readingScore, maxReadingScore, listeningScore, maxListeningScore, writingScore, maxWritingScore, speakingScore, maxSpeakingScore, overallAssessmentSlot, reviewUrl, assetBaseUrl, children, }: OceanEduExamReportProps): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,Fragment as t,jsxs as s}from"react/jsx-runtime";import{useEffect as a,useState as l}from"react";import{BookOpen as n,CalendarDays as x,Headphones as r,IdCard as i,MapPin as m,Mic as c,PenTool as d,Sparkles as o,User as p}from"lucide-react";import{formatScore as h,formatTotalScore as g,getScoreLabel as f,getScoreMessage as b,getScoreStroke as u}from"../utils/review-score.utils";import w from"./assets/logo-ocean.png";import N from"./assets/penguin-main.png";import v from"./assets/penguin-small.png";import F from"./assets/dofu2.png";import{BadgeTitle as E}from"./BadgeTitle";import{ExamSkillRow as y}from"./ExamSkillRow";import{InfoRow as A}from"./SpeakingReportShared";import{resolveAssetUrl as S}from"./speaking-report.utils";export function OceanEduExamReport({studentName:k,studentCode:j,branchName:_,examDateText:T,totalScore:C,maxScore:R,isPendingGrading:H,examSubTitle:M,examTitle:Q,readingScore:I,maxReadingScore:L,listeningScore:U,maxListeningScore:D,writingScore:P,maxWritingScore:G,speakingScore:O,maxSpeakingScore:B,overallAssessmentSlot:K,reviewUrl:q,assetBaseUrl:W,children:z}){const V=f(C,R,H),X=b(C,R,H),J=u(C,R,H),[Y,Z]=l(null),[$,ee]=l(null),te=Q?.toLowerCase().includes("placement test")||M?.toLowerCase().includes("placement test"),se=q??("undefined"!=typeof window?window.location.href:""),ae=S(w,W,"logo-ocean.png"),le=S(N,W,"penguin-main.png"),ne=S(v,W,"penguin-small.png"),xe=S(F,W,"dofu2.png");a(()=>{"undefined"!=typeof window&&import("qrcode").then(e=>{e.default.toDataURL(se,{margin:1,color:{dark:"#0054A6",light:"#ffffff"}}).then(Z).catch(()=>{}),e.default.toDataURL(window.location.origin,{margin:1,color:{dark:"#0054A6",light:"#ffffff"}}).then(ee).catch(()=>{})}).catch(()=>{})},[se]);const re=null!=L||null!=D||null!=G||null!=B;return e("div",{className:"mx-auto w-full max-w-[1120px] py-4 sm:px-4 sm:py-6 md:px-6",children:s("div",{className:"result-exam-container space-y-6",children:[s("div",{className:"overflow-hidden rounded-[12px] bg-white px-3 py-3 shadow-[0_20px_50px_rgba(0,0,0,0.15)] xs:px-4 xs:py-4 sm:rounded-[25px] sm:px-5 sm:py-5 md:rounded-[30px] md:px-8 md:py-6",children:[e("header",{className:"mb-6 md:mb-7",children:s("div",{className:"flex flex-col items-center gap-3 sm:gap-4 md:grid md:grid-cols-[auto_1fr_auto] md:items-center",children:[e("div",{className:"flex items-center justify-center md:justify-start",children:e("img",{src:ae,alt:"Ocean Edu",width:180,className:"h-auto w-[90px] object-contain xs:w-[120px] sm:w-[150px] md:w-[160px] lg:w-[180px]"})}),s("div",{className:"min-w-0 text-center",children:[e("h2",{className:"m-0 text-[16px] font-bold leading-tight text-[#0054A6] xs:text-[20px] sm:text-[22px] md:text-[24px] lg:text-[28px]",children:"PHIẾU KẾT QUẢ"}),e("h1",{className:"mt-1 text-[32px] font-black leading-[0.95] tracking-[-0.5px] text-[#0054A6] xs:text-[40px] sm:text-[40px] md:text-[48px] md:tracking-[-1px] lg:text-[64px]",children:s(t,te?{children:["PLACEMENT ",e("span",{className:"text-[#00AEEF]",children:"TEST"})]}:{children:["EXAM ",e("span",{className:"text-[#00AEEF]",children:"REPORT"})]})}),e("p",{className:"mx-auto mt-1.5 max-w-[520px] text-[12px] font-semibold leading-snug text-[#0054A6] xs:text-[16px] sm:text-sm md:mt-2 md:text-[16px]",children:"Đánh giá năng lực tiếng Anh toàn diện"})]}),e("div",{className:"flex items-center justify-center md:justify-end",children:Y?s("div",{className:"flex flex-col items-center",children:[e("a",{href:se||"#",target:"_blank",rel:"noopener noreferrer",className:"block transition-transform hover:scale-105",children:e("img",{src:Y,alt:"QR Code",className:"h-[80px] w-[80px] rounded-lg border-2 border-[#00AEEF] bg-white p-1 shadow-sm xs:h-[100px] xs:w-[100px] md:h-[120px] md:w-[120px]"})}),e("span",{className:"mt-1.5 text-[9px] font-bold text-[#00AEEF] xs:text-[11px]",children:"Quét hoặc nhấn QR để xem online"})]}):e("img",{src:le,alt:"Mascot",width:250,height:220,className:"h-auto w-[80px] object-contain xs:w-[120px] sm:w-[120px] md:w-[160px] lg:w-[220px]"})})]})}),s("section",{className:"relative mb-10 rounded-[20px] border-2 border-[#00AEEF] px-4 pb-5 pt-10 sm:px-5 sm:pb-6 sm:pt-11 md:px-8",children:[e(E,{number:"1",title:"THÔNG TIN HỌC VIÊN"}),s("div",{className:"flex flex-col items-center justify-between gap-6 md:flex-row md:items-center",children:[s("div",{className:"space-y-3 self-stretch sm:space-y-4",children:[e(A,{icon:p,label:"Họ tên học viên",value:k}),e(A,{icon:i,label:"Mã học viên",value:j||"Đang cập nhật"}),e(A,{icon:m,label:"Chi nhánh",value:_||M||"Ocean Edu"}),e(A,{icon:x,label:"Ngày đánh giá",value:T})]}),e("div",{className:"flex h-[100px] w-[200px] items-center justify-center overflow-hidden rounded-[80px] border-[4px] border-white bg-[linear-gradient(135deg,#EAF6FF_0%,#CFEFFF_45%,#9DDCFF_100%)] shadow-[0_10px_24px_rgba(0,174,239,0.18)] outline outline-2 outline-[#00AEEF] xs:h-[120px] xs:w-[240px] sm:h-[130px] sm:w-[260px] md:h-[150px] md:w-[280px] md:rounded-[100px] md:border-[6px]",children:e("img",{src:xe,alt:"Avatar",width:120,height:120,className:"h-auto w-[40%] scale-[1.2] object-contain md:scale-[1.4]"})})]})]}),s("section",{className:"relative rounded-[20px] border-2 border-[#00AEEF] px-5 pb-6 pt-11 md:px-8",children:[e(E,{number:"2",title:"KẾT QUẢ"}),s("div",{className:"grid gap-6 sm:gap-8 md:grid-cols-[1fr_1.3fr] md:gap-10",children:[s("div",{className:"flex flex-col items-center justify-center border-b border-dashed border-[#ccc] pb-6 text-center sm:pb-8 md:border-b-0 md:border-r md:pb-0 md:pr-8",children:[e("div",{className:"text-[13px] font-extrabold text-[#0054A6] xs:text-[14px] sm:text-[15px]",children:"ĐIỂM TỔNG"}),s("div",{className:"relative mx-auto my-4 flex h-[160px] w-[160px] items-center justify-center rounded-full xs:h-[180px] xs:w-[180px] sm:my-5 sm:h-[200px] sm:w-[200px]",style:{background:J},children:[e("div",{className:"absolute h-[136px] w-[136px] rounded-full bg-white xs:h-[153px] xs:w-[153px] sm:h-[170px] sm:w-[170px]"}),s("div",{className:"relative z-[2] text-center",children:[e("div",{className:"text-[44px] font-black leading-none text-[#0054A6] xs:text-[50px] sm:text-[54px] md:text-[80px]",children:H?"—":g(C)}),s("div",{className:"text-[18px] font-semibold text-[#aaa] xs:text-[20px] sm:text-[22px]",children:["/ ",h(R,0)]})]})]}),e("div",{className:"mt-2 text-[20px] font-extrabold text-[#2ecc71] xs:text-[22px] sm:text-[24px] md:text-[26px]",children:V}),s("div",{className:"mx-auto mt-4 flex w-full max-w-[600px] items-end gap-3 rounded-[20px] border border-[#e1efff] bg-[#F4F9FF] p-3 text-left sm:mt-6 sm:gap-4 sm:p-4",children:[e("img",{src:ne,alt:"Mascot Mini",width:95,height:95,className:"h-auto w-[60px] shrink-0 object-contain xs:w-[80px] sm:w-[90px] md:w-[95px]"}),s("p",{className:"m-0 text-[12px] leading-[1.5] text-[#666] xs:text-[13px]",children:[X,e(o,{className:"ml-1 inline h-4 w-4 text-[#FFD700]"})]})]})]}),s("div",{className:"flex flex-col justify-center rounded-[20px] border border-[#ddecff] bg-[#F4F9FF] p-4 sm:p-5",children:[e("div",{className:"mb-4 text-center text-[16px] font-extrabold text-[#0054A6] xs:text-[18px] sm:mb-5 sm:text-[20px]",children:"ĐIỂM THÀNH PHẦN"}),s("div",{className:"flex flex-col",children:[null!=L&&e(y,{icon:n,label:"Reading",score:I,maxScore:L,barClass:"bg-[#FF9800]"}),null!=D&&e(y,{icon:r,label:"Listening",score:U,maxScore:D,barClass:"bg-[#2196F3]"}),null!=G&&e(y,{icon:d,label:"Writing",score:P,maxScore:G,barClass:"bg-[#9C27B0]"}),null!=B&&e(y,{icon:c,label:"Speaking",score:O,maxScore:B,barClass:"bg-[#4CAF50]"}),!re&&e("div",{className:"py-4 text-center text-sm italic text-slate-500",children:"Không có điểm thành phần"})]})]})]})]}),K,s("footer",{className:"mt-5 grid gap-3 sm:mt-6 sm:gap-4 md:grid-cols-[1fr_1.5fr] md:gap-6",children:[s("div",{className:"flex items-center gap-3 rounded-[20px] bg-[#F4F9FF] p-4 xs:gap-4 sm:gap-5 sm:p-[18px]",children:[$&&e("a",{href:"/",target:"_blank",rel:"noopener noreferrer",className:"block shrink-0 transition-transform hover:scale-105",children:e("img",{src:$,alt:"Home QR",width:110,height:110,className:"w-[80px] rounded-[12px] bg-white object-contain p-[6px] shadow-[0_2px_8px_rgba(0,0,0,0.08)] xs:w-[95px] sm:w-[110px]"})}),s("div",{children:[e("h5",{className:"m-0 text-[14px] font-extrabold text-[#0054A6] xs:text-[15px] sm:text-base",children:"GỢI Ý PHÁT TRIỂN"}),e("p",{className:"mt-1 text-[11px] font-medium text-[#666] xs:text-[11.5px] sm:text-xs",children:"Quét mã QR để nhận bài luyện tập phù hợp với kết quả của bạn!"})]})]}),s("div",{className:"relative flex items-center justify-center rounded-[20px] border-l-[5px] border-[#00AEEF] bg-[#F4F9FF] p-4 text-[13px] font-bold italic leading-[1.4] text-[#0054A6] xs:text-[14px] sm:p-[18px] sm:text-[15px]",children:[e("span",{className:"absolute left-2 top-1 text-xl text-[#00AEEF]/20 xs:left-3 xs:top-2 xs:text-2xl",children:'"'}),"Hãy biến mỗi lần luyện tập thành một bước tiến tới thành công!",e("span",{className:"absolute bottom-1 right-2 text-xl text-[#00AEEF]/20 xs:bottom-2 xs:right-3 xs:text-2xl",children:'"'})]})]})]}),z?e("section",{className:"space-y-6",children:z}):null]})})}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { SpeakingRubric } from '../../../api/result-review';
|
|
2
|
+
import type { SpeakingReviewData } from '../utils/transform-speaking-review';
|
|
3
|
+
export interface OceanEduSpeakingReportProps {
|
|
4
|
+
reviewData: SpeakingReviewData;
|
|
5
|
+
studentName: string;
|
|
6
|
+
studentCode?: string | null;
|
|
7
|
+
branchName?: string | null;
|
|
8
|
+
examDateText: string;
|
|
9
|
+
examTitle?: string | null;
|
|
10
|
+
/** When false, hides the detailed per-question breakdown section. Defaults to true. */
|
|
11
|
+
showDetailedResults?: boolean;
|
|
12
|
+
/** Canonical review URL (with verification token) encoded into the QR code. */
|
|
13
|
+
reviewUrl?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Attempt-level speaking rubric breakdown (Preschool C1–C5, KINDY C1–C7,
|
|
16
|
+
* KIDS 18 questions/4 sections, TEENS C1–C4).
|
|
17
|
+
*/
|
|
18
|
+
speakingRubric?: SpeakingRubric | null;
|
|
19
|
+
/** Optional host public path prefix for report assets. Falls back to bundled imports. */
|
|
20
|
+
assetBaseUrl?: string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Speaking result report shell.
|
|
24
|
+
* Source: oe-exam-fe/.../speaking-review/_components/OceanEduSpeakingReport.tsx
|
|
25
|
+
*/
|
|
26
|
+
export declare function OceanEduSpeakingReport({ reviewData, studentName, studentCode, branchName, examDateText, examTitle, showDetailedResults, reviewUrl, speakingRubric, assetBaseUrl, }: OceanEduSpeakingReportProps): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,Fragment as t,jsxs as s}from"react/jsx-runtime";import{useEffect as a,useState as x}from"react";import{CalendarDays as r,GraduationCap as m,IdCard as n,MapPin as i,Sparkles as d,User as l}from"lucide-react";import{cn as p}from"../../../shared/lib/utils";import{buildSkillMetrics as c,deriveFeedback as o,deriveSkillScores as h,deriveSummaryPoints as g,getScoreLabel as f}from"../utils/speaking-summary";import b from"./assets/logo-ocean.png";import u from"./assets/penguin-main.png";import w from"./assets/penguin-small.png";import N from"./assets/dofu2.png";import v from"./assets/dofu4.png";import{BadgeTitle as F}from"./BadgeTitle";import{SpeakingPartSection as A}from"./SpeakingReportParts";import{InfoRow as E,SkillRow as y}from"./SpeakingReportShared";import{SpeakingRubricSection as k}from"./SpeakingRubricSection";import{formatScore as T,formatTotalScore as _,getScoreStroke as j,resolveAssetUrl as S,withUi as R}from"./speaking-report.utils";export function OceanEduSpeakingReport({reviewData:I,studentName:H,studentCode:C,branchName:P,examDateText:Q,examTitle:D,showDetailedResults:G,reviewUrl:U,speakingRubric:M,assetBaseUrl:L}){const[$,K]=x(null),[O,q]=x(null),B=D?.toLowerCase().includes("placement test")||I.examSubTitle?.toLowerCase().includes("placement test"),z=U??("undefined"!=typeof window?window.location.href:"");a(()=>{"undefined"!=typeof window&&import("qrcode").then(e=>{e.default.toDataURL(z,{margin:1,color:{dark:"#0054A6",light:"#ffffff"}}).then(K).catch(()=>{}),e.default.toDataURL(window.location.origin,{margin:1,color:{dark:"#0054A6",light:"#ffffff"}}).then(q).catch(()=>{})}).catch(()=>{})},[z]);const V=c(h(I)).map(R),X=o(I),J=g(I,V),W=f(I.totalScore,I.maxScore,I.isPendingGrading),Y=j(I.totalScore,I.maxScore,I.isPendingGrading),Z=S(b,L,"logo-ocean.png"),ee=S(u,L,"penguin-main.png"),te=S(w,L,"penguin-small.png"),se=S(N,L,"dofu2.png"),ae=S(v,L,"dofu4.png");return e("div",{className:"mx-auto w-full max-w-[1120px] py-4 sm:px-4 sm:py-6 md:px-6",children:s("div",{className:"space-y-6 result-exam-container",children:[s("div",{className:"overflow-hidden rounded-[12px] bg-white px-3 py-3 shadow-[0_20px_50px_rgba(0,0,0,0.15)] xs:px-4 xs:py-4 sm:rounded-[25px] sm:px-5 sm:py-5 md:rounded-[30px] md:px-8 md:py-6",children:[e("header",{className:"mb-6 md:mb-7",children:s("div",{className:"flex flex-col items-center gap-3 sm:gap-4 md:grid md:grid-cols-[auto_1fr_auto] md:items-center",children:[e("div",{className:"flex items-center justify-center md:justify-start",children:e("img",{src:Z,alt:"Ocean Edu",width:180,className:"h-auto w-[90px] object-contain xs:w-[120px] sm:w-[150px] md:w-[160px] lg:w-[180px]"})}),s("div",{className:"min-w-0 text-center",children:[e("h2",{className:"m-0 text-[16px] font-bold leading-tight text-[#0054A6] xs:text-[20px] sm:text-[22px] md:text-[24px] lg:text-[28px]",children:"PHIẾU KẾT QUẢ"}),e("h1",{className:"mt-1 text-[32px] font-black leading-[0.95] tracking-[-0.5px] text-[#0054A6] xs:text-[40px] sm:text-[40px] md:text-[48px] lg:text-[64px] md:tracking-[-1px]",children:s(t,B?{children:["PLACEMENT ",e("span",{className:"text-[#00AEEF]",children:"TEST"})]}:{children:["SPEAKING ",e("span",{className:"text-[#00AEEF]",children:"AI"})]})}),e("p",{className:"mx-auto mt-1.5 max-w-[520px] text-[12px] font-semibold leading-snug text-[#0054A6] xs:text-[16px] sm:text-sm md:text-[16px] md:mt-2",children:"Đánh giá năng lực nói tiếng Anh bằng trí tuệ nhân tạo"})]}),e("div",{className:"flex items-center justify-center md:justify-end",children:$?s("div",{className:"flex flex-col items-center",children:[e("a",{href:z||"#",target:"_blank",rel:"noopener noreferrer",className:"block transition-transform hover:scale-105",children:e("img",{src:$,alt:"QR Code",className:"h-[80px] w-[80px] rounded-lg border-2 border-[#00AEEF] bg-white p-1 shadow-sm xs:h-[100px] xs:w-[100px] md:h-[120px] md:w-[120px]"})}),e("span",{className:"mt-1.5 text-[9px] font-bold text-[#00AEEF] xs:text-[11px]",children:"Quét hoặc nhấn QR để xem online"})]}):e("img",{src:ee,alt:"Mascot",width:250,height:220,className:"h-auto w-[80px] object-contain xs:w-[120px] sm:w-[120px] md:w-[160px] lg:w-[220px]"})})]})}),s("section",{className:"relative mb-10 rounded-[20px] border-2 border-[#00AEEF] px-4 pb-5 pt-10 sm:px-5 sm:pb-6 sm:pt-11 md:px-8",children:[e(F,{number:"1",title:"THÔNG TIN HỌC VIÊN"}),s("div",{className:"flex flex-col items-center justify-between gap-6 md:flex-row md:items-center",children:[s("div",{className:"space-y-3 self-stretch sm:space-y-4",children:[e(E,{icon:l,label:"Họ tên học viên",value:H}),e(E,{icon:n,label:"Mã học viên",value:C||"Đang cập nhật"}),e(E,{icon:i,label:"Chi nhánh",value:P||I.examSubTitle||"Ocean Edu"}),e(E,{icon:r,label:"Ngày đánh giá",value:Q})]}),e("div",{className:"flex h-[100px] w-[200px] items-center justify-center overflow-hidden rounded-[80px] border-[4px] border-white bg-[linear-gradient(135deg,#EAF6FF_0%,#CFEFFF_45%,#9DDCFF_100%)] outline outline-2 outline-[#00AEEF] shadow-[0_10px_24px_rgba(0,174,239,0.18)] xs:h-[120px] xs:w-[240px] sm:h-[130px] sm:w-[260px] md:h-[150px] md:w-[280px] md:border-[6px] md:rounded-[100px]",children:e("img",{src:se,alt:"Avatar",width:120,height:120,className:"h-auto w-[40%] scale-[1.2] object-contain md:scale-[1.4]"})})]})]}),s("section",{className:"relative rounded-[20px] border-2 border-[#00AEEF] px-5 pb-6 pt-11 md:px-8",children:[e(F,{number:"2",title:"KẾT QUẢ"}),s("div",{className:"grid gap-6 sm:gap-8 md:grid-cols-[1fr_1.3fr] md:gap-10",children:[s("div",{className:"border-b border-dashed border-[#ccc] pb-6 text-center sm:pb-8 md:border-b-0 md:border-r md:pb-0 md:pr-8",children:[e("div",{className:"text-[13px] font-extrabold text-[#0054A6] xs:text-[14px] sm:text-[15px]",children:"ĐIỂM TỔNG"}),s("div",{className:"relative mx-auto my-4 flex h-[160px] w-[160px] items-center justify-center rounded-full xs:h-[180px] xs:w-[180px] sm:my-5 sm:h-[200px] sm:w-[200px]",style:{background:Y},children:[e("div",{className:"absolute h-[136px] w-[136px] rounded-full bg-white xs:h-[153px] xs:w-[153px] sm:h-[170px] sm:w-[170px]"}),s("div",{className:"relative z-[2]",children:[e("div",{className:"text-[44px] font-black leading-none text-[#0054A6] xs:text-[50px] sm:text-[54px] md:text-[80px]",children:I.isPendingGrading?"—":_(I.totalScore)}),s("div",{className:"text-center text-[18px] font-semibold text-[#aaa] xs:text-[20px] sm:text-[22px]",children:["/ ",T(I.maxScore,0)]})]})]}),e("div",{className:"mt-2 text-[20px] font-extrabold text-[#2ecc71] xs:text-[22px] sm:text-[24px] md:text-[26px]",children:W}),s("div",{className:"mt-4 flex items-end gap-3 rounded-[20px] border border-[#e1efff] bg-[#F4F9FF] p-3 text-left sm:mt-6 sm:gap-4 sm:p-4",children:[e("img",{src:te,alt:"Mascot Mini",width:95,height:95,className:"h-auto w-[60px] shrink-0 object-contain xs:w-[80px] sm:w-[90px] md:w-[95px]"}),s("p",{className:"m-0 text-[12px] leading-[1.5] text-[#666] xs:text-[13px]",children:[X,e(d,{className:"ml-1 inline h-4 w-4 text-[#FFD700]"})]})]})]}),s("div",{className:"rounded-[20px] border border-[#ddecff] bg-[#F4F9FF] p-4 sm:p-5",children:[e("div",{className:"mb-4 text-center text-[16px] font-extrabold text-[#0054A6] xs:text-[18px] sm:mb-5 sm:text-[20px]",children:"ĐIỂM THÀNH PHẦN"}),V.map(t=>e(y,{metric:t},t.key))]})]}),s("div",{className:"relative mt-6 rounded-[20px] border border-[#e1efff] bg-white p-4 sm:mt-8 sm:p-5 md:p-6",children:[s("div",{className:"mb-4 flex items-center text-[14px] font-extrabold text-[#0054A6] xs:text-[15px] sm:mb-5 sm:text-base",children:[e(m,{className:"mr-2 h-4 w-4 text-[#00AEEF] xs:h-[18px] xs:w-[18px] sm:h-5 sm:w-5"}),"NHẬN XÉT TỔNG QUAN TỪ AI"]}),e("div",{className:"grid gap-2.5 sm:gap-3 md:w-[75%] md:grid-cols-2",children:J.map((t,a)=>s("div",{className:p("flex items-start gap-2 text-[12px] font-medium xs:text-[12.5px] sm:text-[13px]",t.highlight?"font-extrabold text-[#f39c12]":"text-[#333]"),children:[e("span",{className:p("mt-0.5 text-[13px] xs:text-[13.5px] sm:text-sm","star"===t.icon?"text-[#f39c12]":"text-[#2ecc71]"),children:"star"===t.icon?"★":"●"}),e("span",{children:t.text})]},`${t.text}-${a}`))}),e("img",{src:ae,alt:"AI Robot",width:230,height:220,className:"mx-auto mt-5 h-auto w-[150px] object-contain xs:w-[160px] sm:mt-6 sm:w-[180px] md:mx-0 md:absolute md:bottom-[0px] md:right-0 md:mt-0 md:w-[230px]"})]})]}),M?e(k,{rubric:M,badgeNumber:"3"}):null,s("footer",{className:"mt-5 grid gap-3 sm:mt-6 sm:gap-4 md:grid-cols-[1fr_1.5fr] md:gap-6",children:[s("div",{className:"flex items-center gap-3 rounded-[20px] bg-[#F4F9FF] p-4 xs:gap-4 sm:gap-5 sm:p-[18px]",children:[O?e("a",{href:"/",target:"_blank",rel:"noopener noreferrer",className:"block shrink-0 transition-transform hover:scale-105",children:e("img",{src:O,alt:"Home QR",width:110,height:110,className:"w-[80px] rounded-[12px] bg-white p-[6px] object-contain shadow-[0_2px_8px_rgba(0,0,0,0.08)] xs:w-[95px] sm:w-[100px]"})}):null,s("div",{children:[e("h5",{className:"m-0 text-[14px] font-extrabold text-[#0054A6] xs:text-[15px] sm:text-base",children:"GỢI Ý PHÁT TRIỂN"}),e("p",{className:"mt-1 text-[11px] font-medium text-[#666] xs:text-[11.5px] sm:text-xs",children:"Quét mã QR để nhận bài luyện tập phù hợp với kết quả của bạn!"})]})]}),s("div",{className:"relative flex items-center justify-center rounded-[20px] border-l-[5px] border-[#00AEEF] bg-[#F4F9FF] p-4 text-[13px] font-bold italic leading-[1.4] text-[#0054A6] xs:text-[14px] sm:p-[18px] sm:text-[15px]",children:[e("span",{className:"absolute left-2 top-1 text-xl text-[#00AEEF]/20 xs:left-3 xs:top-2 xs:text-2xl",children:'"'}),"Hãy biến mỗi lần luyện tập thành một bước tiến tới thành công!",e("span",{className:"absolute bottom-1 right-2 text-xl text-[#00AEEF]/20 xs:bottom-2 xs:right-3 xs:text-2xl",children:'"'})]})]})]}),!1!==G?e("section",{className:"space-y-6",children:I.parts.map(t=>e(A,{part:t},`${t.partId}-${t.partSectionName}`))}):null]})})}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface LlmSkillAssessmentItem {
|
|
2
|
+
label: string;
|
|
3
|
+
percentage: number;
|
|
4
|
+
feedback: string;
|
|
5
|
+
}
|
|
6
|
+
export interface OverallAssessmentSectionProps {
|
|
7
|
+
/** True while LLM overall/skill assessment is still generating. */
|
|
8
|
+
isPending?: boolean;
|
|
9
|
+
/** True once the assessment failed to arrive within the allowed wait. */
|
|
10
|
+
isFailed?: boolean;
|
|
11
|
+
llmOverallAssessment?: string | null;
|
|
12
|
+
llmSkillAssessment?: Record<string, LlmSkillAssessmentItem | undefined> | null;
|
|
13
|
+
correctAnswers?: number | null;
|
|
14
|
+
totalQuestions?: number | null;
|
|
15
|
+
/** Section badge number. Defaults to "3". */
|
|
16
|
+
sectionNumber?: string;
|
|
17
|
+
/** Retries the assessment wait. The retry button is hidden when omitted. */
|
|
18
|
+
onRetry?: () => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Section 3 — overall LLM assessment table + pending skeleton.
|
|
22
|
+
* Source: oe-exam-fe/.../review/page.tsx overallAssessmentSlot
|
|
23
|
+
*/
|
|
24
|
+
export declare function OverallAssessmentSection({ isPending, isFailed, llmOverallAssessment, llmSkillAssessment, correctAnswers, totalQuestions, sectionNumber, onRetry, }: OverallAssessmentSectionProps): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as l}from"react/jsx-runtime";import{AlertCircle as t,RotateCw as a,Sparkles as n}from"lucide-react";import{BadgeTitle as s}from"./BadgeTitle";export function OverallAssessmentSection({isPending:r=!1,isFailed:i=!1,llmOverallAssessment:d,llmSkillAssessment:c,correctAnswers:o,totalQuestions:m,sectionNumber:h="3",onRetry:p}){const b=c?Object.values(c).filter(e=>Boolean(e)):[],x=Boolean(d)||b.length>0;return l("section",{className:"relative mt-8 rounded-[20px] border-2 border-[#00AEEF] px-5 pb-6 pt-11 md:px-8",children:[e(s,{number:h,title:"PHÂN TÍCH TỔNG QUAN"}),r&&!x&&l("div",{className:"flex flex-col items-center justify-center gap-3 py-10 text-center",children:[e("div",{className:"flex h-14 w-14 items-center justify-center rounded-full bg-[#d0edff]",children:e(n,{className:"h-7 w-7 animate-pulse text-[#00AEEF]"})}),e("p",{className:"text-sm font-bold text-[#0054A6]",children:"AI đang phân tích bài làm của bạn..."}),e("p",{className:"text-xs text-slate-500",children:"Kết quả sẽ tự động cập nhật sau vài giây. Vui lòng chờ."}),l("div",{className:"flex gap-1.5 pt-1",children:[e("span",{className:"h-2 w-2 animate-bounce rounded-full bg-[#00AEEF] [animation-delay:0ms]"}),e("span",{className:"h-2 w-2 animate-bounce rounded-full bg-[#00AEEF] [animation-delay:150ms]"}),e("span",{className:"h-2 w-2 animate-bounce rounded-full bg-[#00AEEF] [animation-delay:300ms]"})]})]}),i&&!x&&l("div",{className:"flex flex-col items-center justify-center gap-3 py-10 text-center",children:[e("div",{className:"flex h-14 w-14 items-center justify-center rounded-full bg-amber-50",children:e(t,{className:"h-7 w-7 text-amber-500"})}),e("p",{className:"text-sm font-bold text-slate-700",children:"Không thể tạo phân tích cho bài làm này."}),e("p",{className:"text-xs text-slate-500",children:"Điểm số của bạn vẫn được ghi nhận đầy đủ. Vui lòng thử lại, hoặc liên hệ giáo viên nếu tình trạng này tiếp diễn."}),p?l("button",{type:"button",onClick:p,className:"mt-1 inline-flex items-center gap-2 rounded-xl border border-[#00AEEF] px-4 py-2 text-sm font-semibold text-[#0054A6] transition hover:bg-[#f0f8ff]",children:[e(a,{className:"h-4 w-4"}),"Thử lại"]}):null]}),x&&e("div",{className:"overflow-hidden rounded-xl border border-[#00AEEF]/20 shadow-sm",children:l("table",{className:"w-full text-left text-sm text-slate-700",children:[e("thead",{className:"border-b border-[#00AEEF]/20 bg-[#f0f8ff] text-[#0054A6]",children:l("tr",{children:[e("th",{className:"w-[25%] border-r border-[#00AEEF]/10 px-4 py-3 font-bold"}),e("th",{className:"w-[25%] border-r border-[#00AEEF]/10 px-4 py-3 font-bold",children:"Câu đúng/Sai"}),e("th",{className:"w-[50%] px-4 py-3 font-bold",children:"Chi tiết"})]})}),l("tbody",{className:"divide-y divide-[#00AEEF]/10",children:[d?l("tr",{className:"hover:bg-slate-50",children:[e("td",{className:"border-r border-[#00AEEF]/10 px-4 py-4 align-top font-bold",children:"Đánh giá tổng quan"}),l("td",{className:"border-r border-[#00AEEF]/10 px-4 py-4 align-top font-medium text-slate-600",children:[o??"—"," / ",m??"—"]}),e("td",{className:"whitespace-pre-wrap px-4 py-4 align-top leading-relaxed text-slate-700",children:d})]}):null,b.length>0?l("tr",{className:"hover:bg-slate-50",children:[e("td",{className:"border-r border-[#00AEEF]/10 px-4 py-4 align-top font-bold",children:"Đánh giá kỹ năng"}),e("td",{className:"px-0 py-0 align-top",colSpan:2,children:e("table",{className:"h-full w-full",children:e("tbody",{className:"divide-y divide-[#00AEEF]/10",children:b.map((t,a)=>l("tr",{className:"group",children:[l("td",{className:"w-1/3 border-r border-[#00AEEF]/10 px-4 py-4 align-top group-hover:bg-slate-50",children:[e("div",{className:"font-semibold text-slate-800",children:t.label}),l("div",{className:"mt-1 text-xs text-slate-500",children:["(Tỷ lệ"," ",null!=t.percentage?t.percentage.toFixed(2):"0.00","%)"]})]}),e("td",{className:"w-2/3 px-4 py-4 align-top leading-relaxed text-slate-700 group-hover:bg-slate-50",children:t.feedback})]},`${t.label}-${a}`))})})})]}):null]})]})})]})}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface ResultErrorStateProps {
|
|
2
|
+
/** Error object or message string. */
|
|
3
|
+
error?: unknown;
|
|
4
|
+
/** Fallback message when error has no message. */
|
|
5
|
+
fallbackMessage?: string;
|
|
6
|
+
/** Back / retry CTA label. */
|
|
7
|
+
backLabel?: string;
|
|
8
|
+
onBack?: () => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Generic full-page error state for the result review screen.
|
|
12
|
+
* Source: oe-exam-fe/.../review/page.tsx generic error branch
|
|
13
|
+
*/
|
|
14
|
+
export declare function ResultErrorState({ error, fallbackMessage, backLabel, onBack, }: ResultErrorStateProps): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{ChevronLeft as s,XCircle as a}from"lucide-react";export function ResultErrorState({error:l,fallbackMessage:r="Đã xảy ra lỗi khi tải kết quả bài thi.",backLabel:i="Quay lại danh sách",onBack:n}){const c=function(e,t){return e instanceof Error&&e.message?e.message:"string"==typeof e&&e.trim()?e:t}(l,r);return e("div",{className:"flex min-h-screen flex-col bg-[#e0e0e0]",children:e("div",{className:"flex flex-1 items-center justify-center px-4 py-10",children:t("div",{className:"w-full max-w-md rounded-[28px] bg-white p-8 text-center shadow-xl",children:[e("div",{className:"mx-auto mb-5 flex h-16 w-16 items-center justify-center rounded-full bg-red-50",children:e(a,{className:"h-8 w-8 text-red-400"})}),e("h2",{className:"text-base font-bold text-slate-800",children:"Không thể tải kết quả"}),e("p",{className:"mt-2 text-sm text-slate-500",children:c}),n?t("button",{type:"button",onClick:n,className:"mt-6 inline-flex items-center gap-2 rounded-xl bg-[#2874A6] px-5 py-2.5 text-sm font-semibold text-white shadow-sm transition-all hover:bg-[#1A5276] hover:shadow-md active:scale-[0.98]",children:[e(s,{className:"h-4 w-4"}),i]}):null]})})})}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface ResultLoadingStateProps {
|
|
2
|
+
/** Primary loading message. */
|
|
3
|
+
message?: string;
|
|
4
|
+
/** Secondary helper text under the message. */
|
|
5
|
+
description?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Full-page loading state for the result review screen.
|
|
9
|
+
* Source: oe-exam-fe/.../review/page.tsx isLoading branch
|
|
10
|
+
*/
|
|
11
|
+
export declare function ResultLoadingState({ message, description, }: ResultLoadingStateProps): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{Loader2 as s}from"lucide-react";export function ResultLoadingState({message:i="Đang tải kết quả bài thi...",description:l="Vui lòng đợi trong giây lát"}){return e("div",{className:"flex min-h-screen flex-col bg-[#e0e0e0]",children:e("div",{className:"flex flex-1 items-center justify-center px-4 py-10",children:t("div",{className:"text-center",children:[t("div",{className:"relative mx-auto h-16 w-16",children:[e("div",{className:"absolute inset-0 animate-ping rounded-full bg-[#2874A6]/20"}),e("div",{className:"relative flex h-16 w-16 items-center justify-center rounded-full bg-white shadow-lg",children:e(s,{className:"h-8 w-8 animate-spin text-[#2874A6]"})})]}),e("p",{className:"mt-5 text-sm font-semibold text-slate-600",children:i}),l?e("p",{className:"mt-1 text-xs text-slate-500",children:l}):null]})})})}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface ResultNotPublicStateProps {
|
|
2
|
+
/**
|
|
3
|
+
* Login destination. When omitted, builds `/auth/login?redirect=...` from the
|
|
4
|
+
* current URL (host can override via prop or `onLogin` navigation).
|
|
5
|
+
*/
|
|
6
|
+
loginHref?: string;
|
|
7
|
+
/** Optional host public path prefix for report assets. Falls back to bundled imports. */
|
|
8
|
+
assetBaseUrl?: string;
|
|
9
|
+
/** Optional custom login handler (e.g. host router). Falls back to anchor navigation. */
|
|
10
|
+
onLogin?: (href: string) => void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Friendly full-page state when the result is private (403 not-public).
|
|
14
|
+
* Source: oe-exam-fe/.../review/page.tsx isResultNotPublic branch
|
|
15
|
+
*/
|
|
16
|
+
export declare function ResultNotPublicState({ loginHref, assetBaseUrl, onLogin, }: ResultNotPublicStateProps): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{Lock as n}from"lucide-react";import a from"./assets/logo-ocean.png";import i from"./assets/penguin-small.png";import{resolveAssetUrl as r}from"./speaking-report.utils";export function ResultNotPublicState({loginHref:o,assetBaseUrl:s,onLogin:l}){const c=o??("undefined"==typeof window?"/auth/login":`/auth/login?redirect=${encodeURIComponent(window.location.href)}`),d=r(a,s,"logo-ocean.png"),m=r(i,s,"penguin-small.png");return e("div",{className:"flex min-h-screen flex-col items-center justify-center bg-gradient-to-br from-[#eaf4ff] via-[#f0f7ff] to-[#e8f0ff] px-4 py-10",children:e("div",{className:"w-full max-w-md",children:t("div",{className:"relative overflow-hidden rounded-[28px] bg-white px-8 py-10 text-center shadow-[0_20px_60px_rgba(0,84,166,0.12)]",children:[e("div",{className:"absolute inset-x-0 top-0 h-1.5 bg-gradient-to-r from-[#00AEEF] via-[#0054A6] to-[#00AEEF]"}),e("div",{className:"mb-6 flex justify-center",children:e("img",{src:d,alt:"Ocean Edu",className:"h-auto w-[120px] object-contain"})}),e("div",{className:"mx-auto mb-5 flex h-20 w-20 items-center justify-center rounded-full bg-gradient-to-br from-[#eaf4ff] to-[#d0e8ff] ring-4 ring-[#00AEEF]/20",children:e(n,{className:"h-9 w-9 text-[#0054A6]",strokeWidth:1.8})}),e("h2",{className:"text-[20px] font-black text-[#0054A6]",children:"Bạn cần đăng nhập để xem kết quả"}),t("p",{className:"mt-3 text-sm leading-relaxed text-slate-500",children:["Kết quả bài thi này chỉ dành cho học viên đã đăng nhập.",e("br",{}),"Vui lòng đăng nhập vào tài khoản Ocean Edu của bạn để tiếp tục."]}),e("div",{className:"my-6 border-t border-dashed border-slate-200"}),e("a",{href:c,onClick:e=>{l&&(e.preventDefault(),l(c))},className:"inline-flex w-full items-center justify-center gap-2 rounded-2xl bg-gradient-to-r from-[#0054A6] to-[#00AEEF] px-6 py-3 text-sm font-bold text-white shadow-md shadow-[#00AEEF]/30 transition-all hover:opacity-90 hover:shadow-lg active:scale-[0.98]",children:"Đăng nhập ngay"}),e("div",{className:"my-5 border-t border-dashed border-slate-200"}),t("div",{className:"flex items-start gap-3 rounded-2xl bg-[#f4f9ff] px-4 py-3 text-left",children:[e("img",{src:m,alt:"",className:"h-auto w-10 shrink-0 object-contain"}),e("p",{className:"text-[12.5px] leading-relaxed text-[#555]",children:"Sau khi đăng nhập, bạn sẽ có thể xem đầy đủ kết quả và chi tiết bài thi của mình! 💙"})]})]})})})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsxs as e,jsx as t}from"react/jsx-runtime";import{BadgeTitle as r}from"./BadgeTitle";import{QuestionCard as i}from"./SpeakingReportShared";import{formatScore as n,getPartScore as a}from"./speaking-report.utils";function s({part:t}){const r=a(t);return r.hasPending||null==r.score?null:e("div",{className:"inline-flex items-center rounded-full bg-[#0054A6] px-4 py-2 text-sm font-bold text-white shadow-sm",children:["Điểm: ",Math.round(r.score)," / ",n(r.maxScore,0)]})}export function SpeakingPartSection({part:n}){return e("section",{className:"relative rounded-[20px] border-2 border-[#00AEEF] bg-white px-5 pb-6 pt-11 md:px-8",children:[t(r,{number:String(n.partNo),title:n.partName||`PHẦN ${n.partNo}`}),e("div",{className:"flex flex-col gap-4 md:flex-row md:items-start md:justify-between",children:[e("div",{className:"space-y-2",children:[t("h3",{className:"text-xl font-black text-[#0054A6]",children:n.partSectionName||"Speaking section"}),n.instructions?t("p",{className:"max-w-3xl text-sm leading-6 text-slate-600",children:n.instructions}):null]}),t(s,{part:n})]}),t("div",{className:"mt-6 grid gap-4",children:n.questions.map(e=>t(i,{question:e},e.questionId))})]})}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ComponentType } from 'react';
|
|
2
|
+
import type { SpeakingReviewQuestion } from '../utils/transform-speaking-review';
|
|
3
|
+
import { type SkillMetric } from './speaking-report.utils';
|
|
4
|
+
export declare function InfoRow({ icon: Icon, label, value, }: {
|
|
5
|
+
icon: ComponentType<{
|
|
6
|
+
className?: string;
|
|
7
|
+
}>;
|
|
8
|
+
label: string;
|
|
9
|
+
value: string;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
11
|
+
export declare function SkillRow({ metric, compact }: {
|
|
12
|
+
metric: SkillMetric;
|
|
13
|
+
compact?: boolean;
|
|
14
|
+
}): import("react").JSX.Element;
|
|
15
|
+
export declare function PendingGradingCard({ gradingStatus }: {
|
|
16
|
+
gradingStatus: string | null | undefined;
|
|
17
|
+
}): import("react").JSX.Element;
|
|
18
|
+
export declare function NotAnsweredCard(): import("react").JSX.Element;
|
|
19
|
+
export declare function QuestionScorePill({ question }: {
|
|
20
|
+
question: SpeakingReviewQuestion;
|
|
21
|
+
}): import("react").JSX.Element;
|
|
22
|
+
export declare function PromptBlock({ question }: {
|
|
23
|
+
question: SpeakingReviewQuestion;
|
|
24
|
+
}): import("react").JSX.Element;
|
|
25
|
+
export declare function AnswerBlock({ question }: {
|
|
26
|
+
question: SpeakingReviewQuestion;
|
|
27
|
+
}): import("react").JSX.Element;
|
|
28
|
+
export declare function QuestionComponentScores({ question }: {
|
|
29
|
+
question: SpeakingReviewQuestion;
|
|
30
|
+
}): import("react").JSX.Element;
|
|
31
|
+
export declare function FeedbackBlock({ question }: {
|
|
32
|
+
question: SpeakingReviewQuestion;
|
|
33
|
+
}): import("react").JSX.Element;
|
|
34
|
+
export declare function QuestionCard({ question }: {
|
|
35
|
+
question: SpeakingReviewQuestion;
|
|
36
|
+
}): import("react").JSX.Element;
|