@tinyweb_dev/oe-exam-sdk 0.1.3 → 0.1.4
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 +58 -4
- 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/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 +34 -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 +3 -1
- package/dist/components/results/ResultReviewPageContainer.js +1 -1
- package/dist/components/results/hooks/useResultReviewPolling.d.ts +27 -0
- package/dist/components/results/hooks/useResultReviewPolling.js +1 -0
- package/dist/components/results/index.d.ts +3 -0
- package/dist/components/results/index.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/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 +10 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function groupQuestionsByPart(e,t){const n=new Map,o=new Map;for(const t of e){const e=o.get(t.part_id)||[];e.push(t),o.set(t.part_id,e)}for(const[e,r]of o.entries()){const o=[...r].sort((e,t)=>e.question_number-t.question_number),i=o[0];if(!i)continue;const s=t?.find(t=>String(t.id)===String(e)),a=void 0!==s?.id?Number(s.id):void 0,u=s?.name?.match(/\d+/),c=u?parseInt(u[0],10):i.part_no,l=s?.group,m=Boolean(s?.isGroup)&&"DOCUMENT"===l?.type,p=s?.sections??[],d=new Set(p.map(e=>e.questionType).filter(Boolean)),g=p.length>1&&d.size>1;if(m||g){const t=`${e}_part`;n.set(t,{partId:e,partNo:c,questionType:i.question_type,questions:o,name:s?.name,instructions:s?.instructions,audioUrl:s?.audioUrl,templateOrder:a,category:s?.category,isGroup:!0,group:l,sections:s?.sections,startIndex:s?.startIndex,endIndex:s?.endIndex});continue}if(!s?.sections?.length){const t=`${e}_part`;n.set(t,{partId:e,partNo:c,questionType:i.question_type,questions:o,name:s?.name,instructions:s?.instructions,audioUrl:s?.audioUrl,templateOrder:a,category:s?.category,isGroup:s?.isGroup,optionMode:s?.optionMode,optionCount:s?.optionCount,viewMode:s?.viewMode,defaultValue:s?.defaultValue,multipleAnswers:s?.multipleAnswers,questionConfig:s?.questionConfig,wordBankConfig:s?.wordBankConfig,group:s?.group,startIndex:s?.startIndex,endIndex:s?.endIndex});continue}for(const[t,r]of s.sections.entries()){const i=o.filter(e=>{const t=e.question_number-1;return t>=r.startIndex&&t<=r.endIndex});if(!i.length)continue;const u=`${e}_section_${t}`,l=Boolean(r.isGroup||r.group?.type);n.set(u,{partId:e,partNo:c,questionType:r.questionType||i[0].question_type,questions:i,name:s.name,instructions:r.instructions||r.name||s.instructions,audioUrl:s.audioUrl,templateOrder:a,category:s.category,sectionId:r.sectionId,isGroup:l,optionMode:r.optionMode,optionCount:r.optionCount,viewMode:r.viewMode,defaultValue:r.defaultValue,multipleAnswers:r.multipleAnswers,questionConfig:r.questionConfig,wordBankConfig:r.wordBankConfig,group:r.group,startIndex:r.startIndex,endIndex:r.endIndex})}}return Array.from(n.values()).sort((e,t)=>{const n=void 0===e.templateOrder||isNaN(e.templateOrder)?1/0:e.templateOrder,o=void 0===t.templateOrder||isNaN(t.templateOrder)?1/0:t.templateOrder;return n!==o?n-o:(e.questions[0]?.question_number||0)-(t.questions[0]?.question_number||0)})}export function transformFillInBlank(e){const t=e[0],n=t?.content,o=t?.questionGroup?.payload,r=[];for(const t of e){const e=t.content,n=t.is_example||!1,o=e.question||e.blanks?.[0]?.text||"";if([...o.matchAll(/\{(\d+)\}/g)].length>1){const i=o.split("\n").filter(e=>e.trim());for(const o of i){const i=o.match(/\{(\d+)\}/);if(!i)continue;const s=parseInt(i[1],10),a=o.split(/\{\d+\}/),u=a[0]?.trim()||"",c=a[1]?.trim()||"";r.push({id:`${t.id}-blank-${s}`,questionNumber:t.question_number,textBefore:u,textAfter:c,imageUrl:e.imageUrl,hints:e.hints,isExample:n,correctAnswer:n?t.correct_answer?.answers?.[s]:void 0})}}else{const i=o.match(/^(.*?)\{0\}(.*)$/),s=i?i[1].trim():o,a=i?i[2].trim():"";r.push({id:t.id,questionNumber:t.question_number,textBefore:s,textAfter:a,imageUrl:e.imageUrl,hints:e.hints,isExample:n,correctAnswer:n?t.correct_answer?.answers?.[0]:void 0})}}return{questions:r,title:n?.title||o?.title||"FILL IN THE BLANK",imageUrl:n?.imageUrl,audioUrl:n?.audioUrl,exampleText:n?.exampleText,exampleHighlight:n?.exampleHighlight,instruction:n?.instruction||"LISTEN AND WRITE",showHints:n?.showHints,groupContent:o?.content||o?.groupContent,groupTitle:o?.title,groupImageUrl:o?.mainImageUrl||o?.imageUrl}}export function transformWriteCorrectVerbForm(e){const t=e[0],n=t?.content;return{questions:e.map(e=>{const t=e.content,n=t.sentence||"",o=n.match(/^(.*?)\{0\}(.*)$/),r=o?o[1].trim():n,i=o?o[2].trim():"";return{id:e.id,questionNumber:e.question_number,textBefore:r,textAfter:i,verbHint:t.verbHint}}),title:"WRITE THE CORRECT VERB FORM",instruction:n?.instruction||"Write the correct form of the verb in parentheses."}}const e=["A","B","C","D","E","F"];export function transformChooseCorrectAnswer(t){const n=t[0]?.content,o=t[0]?.questionGroup?.payload,r=t.find(e=>e.is_example),i=t.filter(e=>!e.is_example);let s;if(r){const t=r.content,n=(t.options||[]).map((t,n)=>({id:`${r.id}-opt-${n}`,label:e[n]||String(n+1),text:t})),o="number"==typeof r.correct_answer?.answer?r.correct_answer.answer:-1;s={questionNumber:r.question_number,questionText:t.question||"",options:n,correctOptionId:o>=0?n[o]?.id||"":n[0]?.id||"",optionType:t.optionType||"text",imageUrl:t.imageUrl}}return{questions:i.map(t=>{const n=t.content,o=n.optionType||"text",r=(n.options||[]).map((n,o)=>({id:`${t.id}-opt-${o}`,label:e[o]||String(o+1),text:n})),i=t.correct_answer?.answer,s=Array.isArray(i)?i.filter(e=>"number"==typeof e).map(e=>r[e]?.id).filter(e=>Boolean(e)):"number"==typeof i?[r[i]?.id].filter(e=>Boolean(e)):[];return{id:t.id,questionNumber:t.question_number,questionText:n.question||"",options:r,correctOptionId:s[0],correctOptionIds:s,imageUrl:n.imageUrl,optionType:o,multipleAnswers:Boolean(n.multipleAnswers||Array.isArray(i)),explanation:t.explanation}}),example:s,imageUrl:n?.imageUrl,groupImageUrl:o?.mainImageUrl||o?.imageUrl,groupTitle:o?.title,groupContent:o?.content,articles:o?.articles,audioUrl:n?.audioUrl,instruction:n?.instruction||"CHOOSE THE CORRECT ANSWER"}}export function transformChooseCorrectAdjective(t){return{questions:t.map(t=>{const n=t.content,o=n.sentence||"",r=o.match(/^(.*?)\{0\}(.*)$/),i=r?r[1]:o,s=r?r[2]:"";return{id:t.id,questionNumber:t.question_number,sentenceBefore:i,sentenceAfter:s,options:(n.options||[]).map((n,o)=>({id:`${t.id}-opt-${o}`,label:e[o]||String(o+1),text:n}))}}),instruction:"CHOOSE THE CORRECT ADJECTIVE"}}export function transformWriteShortLetter(e){const t=e.content;if(t.instructions?.mainInstruction||t.informations?.length)return{prompt:t.instructions?.mainInstruction||"",requirements:t.informations,minWords:t.minWords,maxWords:t.maxWords,guideImages:t.guideImages};return{prompt:t.contexts?.filter(e=>"text"===e.type).map(e=>e.text).join("\n")||"",minWords:t.minWords,maxWords:t.maxWords,guideImages:t.guideImages}}export function transformColorObjects(e){const t=e[0],n=t?.content,o=n?.colorNodes?.map(e=>({id:e.id,color:e.color,label:e.label}))||n?.hintItems||[],r=n?.regionNodes?.map(e=>({id:e.id,x:e.x,y:e.y,width:e.width,height:e.height,correctHintId:e.linkedColorNodeId}))||n?.regions||[],i=n?.instructions;return{instruction:(i&&i.length>0?i.join(" "):n?.instruction)||"READ AND COLOR THE OBJECTS",instructions:i,imageUrl:n?.backgroundImageUrl||n?.imageUrl||"",audioUrl:n?.audioUrl,hintItems:o,regions:r}}export function transformPictureChoose(t){const n=t[0]?.content;return{subQuestions:t.map(t=>{const n=t.content;return{id:t.id,questionNumber:t.question_number,imageUrl:n.imageUrl||"",questionText:n.questionText,options:(n.options||[]).map((n,o)=>({id:n.id||`${t.id}-opt-${o}`,label:e[o]||String(o+1),text:"string"==typeof n?n:n.text}))}}),instruction:n?.instruction||"LOOK AT THE PICTURE AND CHOOSE THE CORRECT ANSWER"}}export function transformPictureFillBlankChoose(t){const n=t[0]?.content;return{subQuestions:t.map(t=>{const n=t.content;return{id:t.id,questionNumber:t.question_number,imageUrl:n.imageUrl||"",question:n.question||"",options:(n.options||[]).map((n,o)=>({id:n.id||`${t.id}-opt-${o}`,label:e[o]||String(o+1),text:"string"==typeof n?n:n.text}))}}),instruction:n?.instruction||"LOOK AT THE PICTURE, FILL IN THE BLANK AND CHOOSE THE ANSWER"}}export function transformReadingPassage(t){if(function(e){const t=e[0];if(!t)return!1;const n=t.content;return void 0!==n?.question&&!n?.questions&&void 0!==t.questionGroup?.payload?.passage}(t)){const n=t[0],o=n.questionGroup?.payload||{},r=t.map(t=>{const n=t.content;return{id:t.id,questionNumber:t.question_number,questionText:n?.question||"",options:(n?.options||[]).map((n,o)=>({id:`${t.id}-opt-${o}`,label:e[o]||String(o+1),text:"string"==typeof n?n:String(n)}))}});return{passage:o.passage||"",passageTitle:o.passageTitle,passageImageUrl:o.passageImageUrl||o.mainImageUrl,instruction:o.instructions||"READ THE PASSAGE AND ANSWER QUESTIONS",questions:r}}const n=t[0],o=n?.content,r=o?.questions?.map((t,o)=>({id:t.id||`${n.id}-sq-${o}`,questionNumber:o+1,questionText:t.question,options:(t.options||[]).map((o,r)=>({id:`${t.id||n.id}-opt-${r}`,label:e[r]||String(r+1),text:o}))}))||[];return{passage:o?.passage||"",passageTitle:o?.passageTitle,passageImageUrl:o?.passageImageUrl||o?.mainImageUrl,instruction:o?.instructions?.mainInstruction||"READ THE PASSAGE AND ANSWER QUESTIONS",questions:r}}export function transformLabelPicture(e){const t=e[0],n=t?.content;return{imageUrl:n?.imageUrl||"",wordBank:n?.wordBank||[],labels:n?.labels?.map(e=>({id:e.id,number:e.number,isPreFilled:e.isPreFilled||!1,correctAnswer:e.correctAnswer}))||[],instruction:n?.instruction||"LABEL THE PICTURE"}}function t(e){return e&&"string"==typeof e.word&&!e.subQuestions}export function transformFillWordHint(e){const n=e[0],o=n?.content;if(e.length>1&&e.every(e=>t(e.content))){const t=n.questionGroup?.payload?.hintLetters||[];return{subQuestions:e.map(e=>{const t=e.content,n=t.word.replace(/\{\d+\}/,"_");return{id:e.id,questionNumber:e.question_number,imageUrl:t.imageUrl||"",word:n}}),hintLetters:t,instruction:o?.instruction||"LOOK AT THE PICTURE AND FILL IN THE MISSING LETTER"}}if(t(o)){const e=o,t=n.questionGroup?.payload?.hintLetters||[];return{subQuestions:function(e){const t=[],n=e.split(",").map(e=>e.trim());for(const e of n){let n;const o=/\{(\d+)\}/g;for(;null!==(n=o.exec(e));){const o=parseInt(n[1],10),r=e.replace(n[0],"_");t.push({index:o,displayWord:r})}}return t.sort((e,t)=>e.index-t.index),t}(e.word).map(t=>({id:`${n.id}-blank-${t.index}`,questionNumber:n.question_number,imageUrl:e.imageUrl||"",word:t.displayWord,blankIndex:t.index})),hintLetters:t,instruction:e.instruction||"LOOK AT THE PICTURE AND FILL IN THE MISSING LETTER"}}const r=o;return{subQuestions:r?.subQuestions?.map((e,t)=>({id:e.id,questionNumber:n.question_number+t,imageUrl:e.imageUrl||"",word:e.word||""}))||[],hintLetters:r?.hintLetters||n.questionGroup?.payload?.hintLetters||[],instruction:r?.instruction||"LOOK AT THE PICTURE AND FILL IN THE MISSING LETTER"}}export function transformGridFill(e){const t=e[0],n=t?.content;return{grid:n?.grid||[],rows:n?.rows||0,columns:n?.columns||0,instruction:n?.instruction||"FILL IN THE MISSING WORDS",disabledCells:n?.disabledCells||[]}}export function transformAnswerTheQuestion(e){const t=e[0],n=t?.questionGroup?.payload,o=t?.content;return{questions:e.map(e=>{const t=e.content,n=e.is_example||t.isExample||!1;return{id:e.id,questionNumber:e.question_number,question:(t.question||"").replace(/\{\d+\}/g,"______"),imageUrl:t.imageUrl,prefixText:t.prefixText,suffixText:t.suffixText,suggestedAnswers:t.suggestedAnswers,isExample:n,exampleAnswer:n?e.correct_answer?.answer:void 0}}),instruction:"ANSWER THE QUESTION",imageUrl:n?.mainImageUrl||n?.imageUrl||o?.imageUrl,title:o?.title,groupContent:n?.groupContent||o?.groupContent}}export function transformTrueFalse(e){const t=e[0]?.questionGroup?.payload;return{questions:e.map(e=>{const t=e.content,n=e.is_example||t.isExample||!1,o=n&&"boolean"==typeof e.correct_answer?.answer?e.correct_answer.answer:void 0;return{id:e.id,questionNumber:e.question_number,question:t.question||"",imageUrl:t.imageUrl,isExample:n,exampleCorrectAnswer:o}}),instruction:"READ AND CHOOSE TRUE OR FALSE",imageUrl:t?.mainImageUrl||t?.imageUrl,groupContent:t?.content||t?.groupContent}}export function transformWordOrdering(e){return{questions:e.map(e=>{const t=e.content,n=e.is_example||t.isExample||!1,o=Array.isArray(e.correct_answer?.answer)?e.correct_answer.answer:Array.isArray(t.answer)?t.answer:void 0;return{id:e.id,questionNumber:e.question_number,words:t.words||[],imageUrl:t.imageUrl,isExample:n,exampleAnswer:n?o:void 0}}),instruction:"ARRANGE THE LETTERS TO MAKE A WORD"}}export function transformWordFillParagraph(e){const t=e[0],n=t?.questionGroup?.payload,o=t?.content,r=o?.title||n?.title||e.find(e=>e.content?.title)?.content?.title,i=o?.wordBank||[],s=i.map(e=>"string"==typeof e?{word:e}:e),a=o?.viewMode,u=void 0!==o?.wordBank?.[0]?.options;let c;c="EMAIL"===a?"EMAIL":a||!o?.emailFrom&&!o?.emailTo?"DEFAULT"===a?"DEFAULT":"WITH_OPTIONS"===a||u?"WITH_OPTIONS":a||0!==i.length?"WITH_WORD_BANK":"DEFAULT":"EMAIL";const l=o?.emailFrom||o?.metadata?.emailFrom,m=o?.emailTo||o?.metadata?.emailTo;return{questionId:t?.id||"",paragraph:o?.paragraph||"",title:r,imageUrl:o?.imageUrl,wordBank:s,viewMode:c,instruction:"READ AND FILL IN THE BLANKS",exampleAnswer:o?.exampleAnswer,emailFrom:l,emailTo:m}}export function transformMatchByWritingAnswer(e){const t=e[0]?.questionGroup?.payload,n=e.some(e=>!!e.content?.askerName),o=e.map(e=>{const t=e.content,n=e.is_example||t.isExample||!1;return{id:e.id,questionNumber:e.question_number,question:t.question||"",imageUrl:t.imageUrl,isExample:n,exampleAnswer:n?e.correct_answer?.answer:void 0,askerName:t.askerName,answererName:t.answererName}}),r=t?.options?.map(e=>({answer:e.answer,imageUrl:e.imageUrl||"",content:e.content||""}))||[],i=n?e[0]?.content:void 0;return{questions:o,options:r,instruction:"LOOK AND WRITE THE ANSWER",viewMode:n?"CONVERSATION":"DEFAULT",groupImageUrl:t?.groupImageUrl||"",askerName:i?.askerName,answererName:i?.answererName}}export function transformWriteSentences(e){const t=e[0],n=t?.content,o=t?.questionGroup?.payload;return{questions:e.map(e=>({id:e.id,questionNumber:e.question_number})),instruction:n?.instruction||"WRITE SENTENCES ABOUT THE PICTURE",imageUrl:o?.mainImageUrl||o?.imageUrl||n?.imageUrl,title:n?.title,groupContent:o?.groupContent||n?.groupContent}}export function transformWordFillStructuredForm(e){const t=e[0],n=t?.questionGroup?.payload;return{questions:e.map(e=>{const t=e.content,n=e.correct_answer||{};let o=t?.content||"";const r=t?.blocks;return!o&&r?.length&&(o=r.map(e=>(e.heading?`<h3>${e.heading}</h3>`:"")+(e.content||"")).join("")),{id:e.id,questionNumber:e.question_number,title:t?.title||"",imageUrl:t?.imageUrl,content:o,answers:n?.answers||{},caseSensitive:n?.caseSensitive??t?.caseSensitive??!1}}),instruction:n?.instructions||"COMPLETE THE FORM BELOW",groupTitle:n?.title,groupContent:n?.content,groupImageUrl:n?.imageUrl,audioUrl:t?.audio_url||n?.audioUrl}}export function transformListenChooseObjects(e){const t=e[0],n=t?.content,o=t?.questionGroup?.payload||{};return{audioUrl:n?.audioUrl||"",backgroundImageUrl:o.backgroundImageUrl||"",objectNodes:o.objectNodes||[],correctObjectId:t?.correctAnswer?.objectId||t?.correct_answer?.objectId||"",isBackup:n?.isBackup||!1,instruction:"LISTEN AND CHOOSE THE CORRECT OBJECT"}}export function transformListenSpeakAnswer(e){return{questions:e.map(e=>{const t=e.content,n=e.questionGroup?.payload||{};return{id:e.id,questionNumber:e.question_number,audioUrl:t?.audioUrl||"",arrowNode:t?.arrowNode,backgroundImageUrl:n.backgroundImageUrl||n.imageUrl||"",isBackup:t?.isBackup||!1}}),instruction:"LISTEN AND SPEAK YOUR ANSWER"}}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Attempt } from '../../../shared/types/student.types';
|
|
2
|
+
interface BeforeStartCardProps {
|
|
3
|
+
countdown: number;
|
|
4
|
+
formatCountdown: (seconds: number) => string;
|
|
5
|
+
}
|
|
6
|
+
export declare function BeforeStartCard({ countdown, formatCountdown }: BeforeStartCardProps): import("react").JSX.Element;
|
|
7
|
+
interface WithinWindowCardProps {
|
|
8
|
+
autoStartCountdown: number;
|
|
9
|
+
isStarting: boolean;
|
|
10
|
+
showCountdown: boolean;
|
|
11
|
+
onStartClick: () => void;
|
|
12
|
+
}
|
|
13
|
+
export declare function WithinWindowCard({ autoStartCountdown, isStarting, showCountdown, onStartClick, }: WithinWindowCardProps): import("react").JSX.Element;
|
|
14
|
+
export declare function ExpiredCard(): import("react").JSX.Element;
|
|
15
|
+
interface AlreadySubmittedCardProps {
|
|
16
|
+
attempt?: Attempt | null;
|
|
17
|
+
}
|
|
18
|
+
export declare function AlreadySubmittedCard({ attempt }: AlreadySubmittedCardProps): import("react").JSX.Element;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{ROUTES as n}from"../../../shared/constants/routes";import{Clock as r,CheckCircle as i,XCircle as l,Loader2 as a,Play as s,Trophy as c}from"lucide-react";export function BeforeStartCard({countdown:n,formatCountdown:i}){return t("div",{className:"flex flex-col items-center justify-center rounded-2xl bg-black/20 px-6 py-8 text-center ring-1 ring-white/10 backdrop-blur-sm",children:[e(r,{className:"mb-3 h-10 w-10 text-amber-400"}),e("h2",{className:"mb-1 text-xl font-extrabold text-white",children:"Chưa đến giờ thi"}),e("p",{className:"mb-4 text-sm font-medium text-white/80",children:"Bài thi sẽ bắt đầu sau:"}),e("div",{className:"rounded-xl bg-black/30 px-6 py-3 ring-1 ring-white/5",children:e("p",{className:"font-mono text-2xl font-extrabold tracking-wider text-amber-300",children:i(n)})})]})}export function WithinWindowCard({autoStartCountdown:n,isStarting:r,showCountdown:i,onStartClick:l}){return t("div",i?{className:"flex flex-col items-center justify-center rounded-2xl bg-black/20 px-6 py-8 text-center ring-1 ring-white/10 backdrop-blur-sm",children:[e("h2",{className:"mb-4 text-xl font-extrabold text-white",children:"Chuẩn bị vào thi"}),e("div",{className:"flex h-16 w-16 items-center justify-center rounded-full bg-emerald-500/20 ring-2 ring-emerald-400/50",children:e("span",{className:"text-3xl font-extrabold text-emerald-300",children:n})}),t("p",{className:"mt-4 text-sm font-medium text-white/80",children:["Bài thi sẽ tự động bắt đầu sau ",n," giây"]}),r&&t("div",{className:"mt-3 flex items-center gap-2 text-sm font-medium text-emerald-300",children:[e(a,{className:"h-4 w-4 animate-spin"}),e("span",{children:"Đang bắt đầu bài thi..."})]})]}:{className:"flex flex-col items-center justify-center rounded-2xl bg-black/20 px-6 py-8 text-center ring-1 ring-white/10 backdrop-blur-sm",children:[e("h2",{className:"mb-6 text-xl font-extrabold text-white",children:"Sẵn sàng vào thi"}),e("button",{onClick:l,className:"group flex h-20 w-20 cursor-pointer items-center justify-center rounded-full bg-emerald-500 shadow-lg ring-4 ring-emerald-500/30 transition-all duration-200 hover:scale-105 hover:bg-emerald-400 active:scale-95",children:e(s,{className:"h-8 w-8 text-white transition-transform group-hover:scale-110",fill:"white"})}),e("p",{className:"mt-5 text-sm font-medium text-white/80",children:"Nhấn nút trên để bắt đầu bài thi"})]})}export function ExpiredCard(){return t("div",{className:"flex flex-col items-center justify-center rounded-2xl bg-black/20 px-6 py-8 text-center ring-1 ring-white/10 backdrop-blur-sm",children:[e(l,{className:"mb-3 h-10 w-10 text-red-400"}),e("h2",{className:"text-xl font-extrabold text-white",children:"Đã hết thời gian"}),e("p",{className:"mt-2 text-sm font-medium text-white/80",children:"Thời gian làm bài đã kết thúc"})]})}export function AlreadySubmittedCard({attempt:r}){return t("div",{className:"flex flex-col items-center justify-center rounded-2xl bg-black/20 px-6 py-8 text-center ring-1 ring-white/10 backdrop-blur-sm",children:[e(i,{className:"mb-3 h-10 w-10 text-emerald-400"}),e("h2",{className:"mb-4 text-xl font-extrabold text-white",children:"Đã hoàn thành bài thi"}),null!=r?.totalScore&&t("div",{className:"mb-5 flex items-center gap-3 rounded-xl bg-black/30 px-5 py-3 ring-1 ring-white/5",children:[e(c,{className:"h-5 w-5 text-yellow-400"}),e("span",{className:"text-sm font-semibold text-white/90",children:"Điểm số:"}),t("span",{className:"text-xl font-extrabold text-yellow-400",children:[r.totalScore,t("span",{className:"text-sm font-bold text-white/50",children:["/",r.maxScore]})]})]}),r?e("button",{className:"inline-flex cursor-pointer items-center gap-2 rounded-full bg-emerald-600 px-6 py-2.5 text-sm font-bold text-white shadow-lg transition-all duration-200 hover:-translate-y-0.5 hover:bg-emerald-500 active:translate-y-0 active:scale-95",onClick:()=>window.location.href=n.STUDENT.RESULT_DETAIL(r.id),children:"Xem chi tiết kết quả"}):e("p",{className:"text-sm font-medium text-white/80",children:"Phòng thi này không cho phép thi nhiều lần."})]})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{ThemedWaitingRoom}from"./themed";export{BeforeStartCard,WithinWindowCard,ExpiredCard,AlreadySubmittedCard}from"./entry-state-cards";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { StudentRoom } from '../../../../shared/types/student.types';
|
|
2
|
+
interface CambridgeYleWaitingRoomProps {
|
|
3
|
+
room: StudentRoom;
|
|
4
|
+
onStart: () => void;
|
|
5
|
+
isStarting?: boolean;
|
|
6
|
+
showCountdown?: boolean;
|
|
7
|
+
autoStartCountdown?: number;
|
|
8
|
+
closeRedirectUrl?: string;
|
|
9
|
+
onClose?: () => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function CambridgeYleWaitingRoom({ room, onStart, isStarting, showCountdown, autoStartCountdown, closeRedirectUrl, onClose, }: CambridgeYleWaitingRoomProps): import("react").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useState as n}from"react";import a from"next/image";import{Loader2 as i,FileText as l,Clock as s,Trophy as r,Layers as c,Target as h,HelpCircle as o,ChevronDown as m,X as d}from"lucide-react";import x from"../../../../components/themes/cambridge-yle/assets/images/background-ocean-edu-2.png";import g from"../../../../components/themes/cambridge-yle/assets/images/frame.png";import p from"../../../../components/themes/cambridge-yle/assets/images/bg-title.png";import u from"../../../../components/themes/cambridge-yle/assets/images/close.png";import b from"../../../../components/themes/cambridge-yle/assets/images/rocket.png";const f=[{q:"Bài test này kéo dài bao lâu?",a:"Thời gian làm bài sẽ được hiển thị trên màn hình. Hãy theo dõi đồng hồ để phân bổ thời gian hợp lý."},{q:"Em có cần hoàn thành tất cả câu hỏi không?",a:"Để kết quả phản ánh chính xác nhất năng lực hiện tại, em cố gắng hoàn thành toàn bộ câu hỏi trong thời gian quy định."},{q:"Nếu mất mạng giữa chừng thì sao?",a:"Trong một số trường hợp, hệ thống có thể tự động lưu tiến trình làm bài. Hãy kết nối lại internet sớm nhất có thể. Tuy nhiên để tránh gián đoạn, em nên sử dụng kết nối internet ổn định trong suốt quá trình làm bài."},{q:"Em không nghe được audio thì phải làm sao?",a:"Em hãy kiểm tra loa, tai nghe và âm lượng thiết bị trước. Nếu vẫn gặp lỗi, hãy thử tải lại trang hoặc đổi sang trình duyệt khác để tiếp tục làm bài."},{q:"Trang web bị lag hoặc không tải được câu hỏi?",a:"Em có thể thử làm mới trang, kiểm tra lại kết nối internet hoặc đổi trình duyệt/thiết bị khác. Nếu tình trạng vẫn tiếp diễn, hãy liên hệ trung tâm để được hỗ trợ nhanh nhất."},{q:"Em có thể xem lại đáp án sau khi nộp bài không?",a:"Sau khi nộp bài, sẽ có phiếu kết quả AI chấm điểm trả về, phân tích từng câu hỏi em đã làm."},{q:"Nếu em nộp nhầm thì sao?",a:"Sau khi nộp bài, em sẽ không thể chỉnh sửa câu trả lời. Em có thể liên hệ thầy cô ở trung tâm để hỗ trợ làm lại bài khác."},{q:"Em có được dùng từ điển hoặc tài liệu không?",a:"Để kết quả phản ánh đúng năng lực hiện tại, em nên làm bài trong điều kiện tương tự một bài kiểm tra thực tế và hạn chế sử dụng tài liệu hỗ trợ."},{q:"Em cần chuẩn bị gì trước khi bắt đầu?",a:"Em nên chuẩn bị thiết bị có kết nối internet ổn định, tai nghe (nếu có phần Listening), không gian yên tĩnh và dành đủ thời gian để hoàn thành bài test liên tục."},{q:"Em nên làm gì nếu gặp lỗi hệ thống?",a:"Nếu gặp lỗi trong quá trình làm bài, em hãy chụp lại màn hình lỗi (nếu có) và liên hệ trung tâm để được hỗ trợ xử lý nhanh nhất."}];function w({className:t="",size:n=18}){return e("svg",{width:n,height:n,viewBox:"0 0 24 24",className:t,fill:"currentColor",children:e("path",{d:"M12 0l2.4 9.6L24 12l-9.6 2.4L12 24l-2.4-9.6L0 12l9.6-2.4L12 0z"})})}export function CambridgeYleWaitingRoom({room:i,onStart:b,isStarting:y,showCountdown:k,autoStartCountdown:_,closeRedirectUrl:C,onClose:z}){const j=i.exam?.name||"",q=i.contest?.name||"",S=i.exam?.numberOfParts||1,E=i.exam?.durationMinutes||45,B=i.exam?.totalScore||100,L=i.exam?.passScore||0,[T,W]=n(!1),[O,P]=n(0);return t("div",{className:"fixed inset-0 z-50 flex items-center justify-center overflow-hidden",children:[e("div",{className:"absolute inset-0 scale-105 bg-cover bg-center",style:{backgroundImage:`url(${x.src})`,filter:"blur(2px) brightness(0.8) saturate(0.9)"}}),e("div",{className:"absolute inset-0 bg-gradient-to-b from-blue-950/50 via-blue-900/40 to-blue-950/60"}),e(w,{className:"absolute left-[8%] top-[14%] text-yellow-300 drop-shadow-[0_0_8px_rgba(255,255,150,0.8)]",size:22}),e(w,{className:"absolute right-[10%] top-[10%] text-yellow-200 drop-shadow-[0_0_8px_rgba(255,255,150,0.8)]",size:16}),e(w,{className:"absolute left-[15%] bottom-[18%] text-white/80",size:14}),e(w,{className:"absolute right-[6%] bottom-[24%] text-yellow-300 drop-shadow-[0_0_8px_rgba(255,255,150,0.8)]",size:20}),e(w,{className:"absolute left-[45%] top-[8%] text-white/70",size:12}),e("div",{className:"relative z-10",style:{width:"min(98vw, calc(98vh * 1552 / 672), 1800px)"},children:t("div",{className:"relative aspect-[1552/672] w-full",children:[e(a,{src:g,alt:"",fill:!0,className:"object-contain",priority:!0,unoptimized:!0}),e("div",{className:"absolute left-1/2 top-0 z-20 -translate-x-1/2 -translate-y-1/2",children:t("div",{className:"relative",children:[e(a,{src:p,alt:"",width:320,height:72,className:"drop-shadow-xl",priority:!0,unoptimized:!0}),e("div",{className:"absolute inset-0 flex items-center justify-center px-10 pt-1",title:q,children:e("span",{className:"block truncate text-center text-base font-extrabold uppercase tracking-[0.15em] text-white drop-shadow",children:q})})]})}),e("button",{onClick:()=>{z?z():C&&(window.location.href=C)},className:"absolute right-[7%] top-[-2%] z-30 cursor-pointer transition-transform hover:scale-110 active:scale-95","aria-label":"Close",children:e(a,{src:u,alt:"Close",width:56,height:56,unoptimized:!0})}),e("div",{className:"absolute inset-0 z-10 flex items-stretch overflow-hidden px-[9%] py-[10%]",children:t("div",{className:"flex h-full w-full items-stretch gap-4",children:[t("div",{className:"mx-auto flex w-full max-w-[560px] flex-[0_0_46%] flex-col justify-center gap-1",children:[e("h1",{className:"whitespace-nowrap text-center text-3xl font-black uppercase tracking-wide text-white md:text-4xl lg:text-5xl 2xl:text-6xl",style:{fontWeight:900,margin:"0.2em 0",textShadow:["0 1px 0 #cfe3ff","0 2px 0 #a8c8ee","0 3px 0 #6f9cd4","0 4px 0 #4a7cb8","0 5px 0 #2f5e9c","0 6px 0 #1a4480","0 8px 12px rgba(0,0,0,0.45)","0 0 22px rgba(120,180,255,0.55)"].join(", ")},children:i.name}),e("p",{className:"mt-1 hidden text-center text-sm font-bold text-[#21cffb] md:text-base 2xl:block 2xl:text-lg",children:"Đánh giá năng lực • Xác định lộ trình • Bứt phá tương lai"}),e("p",{className:"mx-auto mt-1 hidden max-w-2xl text-center text-xs text-white/95 md:text-sm 2xl:block",children:"Bài thi giúp bạn biết chính xác trình độ hiện tại và được Ocean Edu tư vấn lộ trình học tập phù hợp nhất."}),t("div",{className:"mt-4 flex items-center justify-center gap-2 2xl:mt-6",children:[e("span",{className:"h-px w-10 bg-white"}),e(w,{className:"text-white",size:12}),e("span",{className:"text-xs font-extrabold uppercase tracking-[0.3em] text-white md:text-sm",children:"Bài thi bao gồm"}),e(w,{className:"text-white",size:12}),e("span",{className:"h-px w-10 bg-white"})]}),t("div",{className:"mt-4 grid grid-cols-3 gap-2.5",children:[e(v,{icon:e(c,{className:"h-6 w-6"}),value:S,label:"Phần thi"}),e(v,{icon:e(s,{className:"h-6 w-6"}),value:E,label:"Phút"}),e(v,{icon:e(r,{className:"h-6 w-6"}),value:B,label:"Thang điểm"})]}),t("div",{className:"mt-4 space-y-2.5",children:[t("div",{className:"flex items-center gap-3 rounded-xl bg-[#0000001c] px-3 py-2.5",children:[e("span",{className:"flex h-9 w-9 flex-none items-center justify-center rounded-full bg-[#00000021] text-white",children:e(l,{className:"h-4 w-4"})}),e("span",{className:"text-sm font-bold text-white md:text-base",children:"Đề thi"}),e("span",{className:"ml-auto truncate text-sm font-bold text-white md:text-base",children:j})]}),L>0&&t("div",{className:"flex items-center gap-3 rounded-xl bg-[#0000001c] px-3 py-2.5",children:[e("span",{className:"flex h-9 w-9 flex-none items-center justify-center rounded-full bg-[#00000021] text-white",children:e(h,{className:"h-4 w-4"})}),e("span",{className:"text-sm font-bold text-white md:text-base",children:"Điểm tối thiểu cần đạt"}),t("span",{className:"ml-auto text-sm font-black text-yellow-300 md:text-lg",children:[L,"/",B]})]})]}),e("div",{className:"mt-5 flex justify-center",children:e(N,{onClick:b,isStarting:y,showCountdown:k,autoStartCountdown:_})})]}),e("div",{className:"relative hidden flex-[0_0_42%] md:block",children:e("div",{className:"absolute left-0 top-[4%] z-20 max-w-[60%]",children:t("div",{className:"relative rounded-2xl bg-white px-3 py-2 text-center text-[11px] font-semibold text-sky-900 shadow-xl ring-2 ring-sky-200 md:text-xs",children:["Cùng mình khám phá trình độ của bạn nhé!",e("span",{className:"absolute -bottom-1.5 right-6 h-3 w-3 rotate-45 bg-white ring-2 ring-sky-200"})]})})})]})})]})}),t("button",{type:"button",onClick:()=>W(!0),"aria-label":"Câu hỏi thường gặp",className:"group fixed bottom-5 right-5 z-40 inline-flex cursor-pointer items-center gap-2 rounded-full bg-gradient-to-b from-cyan-300 via-sky-500 to-sky-700 px-4 py-3 text-sm font-bold text-white shadow-[0_6px_0_0_#0c4a6e,0_10px_22px_rgba(14,165,233,0.45)] ring-2 ring-cyan-200/50 transition-all duration-150 hover:-translate-y-0.5 hover:brightness-110 active:translate-y-1 active:shadow-[0_1px_0_0_#0c4a6e,0_3px_8px_rgba(14,165,233,0.3)]",children:[e(o,{className:"h-5 w-5",strokeWidth:2.5}),e("span",{className:"hidden sm:inline",style:{textShadow:"0 1px 0 rgba(12,74,110,0.5)"},children:"Câu hỏi thường gặp"})]}),T&&t("div",{className:"fixed inset-0 z-[60] flex items-center justify-center p-4",role:"dialog","aria-modal":"true","aria-label":"Câu hỏi thường gặp",children:[e("div",{className:"absolute inset-0 bg-blue-950/70 backdrop-blur-sm",onClick:()=>W(!1)}),t("div",{className:"relative z-10 flex max-h-[85vh] w-full max-w-2xl flex-col overflow-hidden rounded-3xl bg-gradient-to-br from-cyan-300/20 via-white/5 to-white/0 p-px shadow-[0_20px_60px_-15px_rgba(10,75,140,0.7)] backdrop-blur-xl",children:[e("div",{className:"pointer-events-none absolute -left-12 -top-12 h-40 w-40 rounded-full bg-cyan-400/30 blur-3xl"}),e("div",{className:"pointer-events-none absolute -right-12 -bottom-12 h-40 w-40 rounded-full bg-sky-500/25 blur-3xl"}),t("div",{className:"relative flex max-h-[85vh] flex-col overflow-hidden rounded-[calc(1.5rem-1px)] bg-gradient-to-b from-[#0e3d70]/95 via-[#0a4b8c]/95 to-[#0a3866]/95 ring-1 ring-cyan-300/20",children:[t("div",{className:"flex items-center gap-3 px-6 py-5",children:[e("div",{className:"flex h-12 w-12 flex-none items-center justify-center rounded-full bg-gradient-to-br from-cyan-300 to-sky-600 shadow-[0_6px_18px_-4px_rgba(34,211,238,0.6)] ring-4 ring-cyan-300/20",children:e(o,{className:"h-6 w-6 text-white",strokeWidth:2.5})}),t("div",{className:"flex-1",children:[e("h2",{className:"text-lg font-extrabold tracking-tight text-white drop-shadow-md",children:"Câu hỏi thường gặp"}),e("p",{className:"mt-0.5 text-xs font-medium text-cyan-100/70",children:"Những điều cần biết trước khi làm bài"})]}),e("button",{type:"button",onClick:()=>W(!1),"aria-label":"Đóng",className:"flex h-9 w-9 flex-none cursor-pointer items-center justify-center rounded-full bg-white/10 text-white/80 ring-1 ring-white/15 transition-all duration-150 hover:bg-white/20 hover:text-white",children:e(d,{className:"h-5 w-5"})})]}),e("div",{className:"mx-6 h-px bg-gradient-to-r from-transparent via-cyan-300/25 to-transparent"}),e("div",{className:"flex-1 overflow-y-auto px-4 py-4 sm:px-6",children:e("ul",{className:"space-y-2",children:f.map((n,a)=>{const i=O===a;return t("li",{className:"overflow-hidden rounded-2xl ring-1 transition-colors duration-200 "+(i?"bg-white/[0.08] ring-cyan-300/30":"bg-white/[0.04] ring-white/10 hover:bg-white/[0.06]"),children:[t("button",{type:"button",onClick:()=>P(i?null:a),className:"flex w-full cursor-pointer items-center gap-3 px-4 py-3 text-left","aria-expanded":i,children:[e("span",{className:"flex h-7 w-7 flex-none items-center justify-center rounded-full text-xs font-black transition-colors "+(i?"bg-gradient-to-br from-yellow-300 to-orange-500 text-white":"bg-white/10 text-cyan-100"),children:a+1}),e("span",{className:"flex-1 text-sm font-bold text-white",children:n.q}),e(m,{className:"h-5 w-5 flex-none text-cyan-200 transition-transform duration-200 "+(i?"rotate-180":"")})]}),e("div",{className:"grid transition-all duration-200 "+(i?"grid-rows-[1fr] opacity-100":"grid-rows-[0fr] opacity-0"),children:e("div",{className:"overflow-hidden",children:e("p",{className:"px-4 pb-4 pl-14 text-sm leading-relaxed text-cyan-50/90",children:n.a})})})]},a)})})}),e("div",{className:"border-t border-white/10 bg-white/[0.03] px-6 py-3 text-center text-xs text-cyan-100/60",children:"Cần hỗ trợ thêm? Liên hệ trung tâm Ocean Edu để được giải đáp."})]})]})]})]})}function v({icon:n,value:a,label:i}){return t("div",{className:"flex items-center justify-center gap-3 rounded-2xl bg-[#0000001c] px-2 py-4",children:[e("span",{className:"flex h-12 w-12 flex-none items-center justify-center rounded-full bg-[#00bdff1a] text-white",children:n}),t("div",{className:"flex flex-col items-start leading-tight",children:[e("div",{className:"text-2xl font-black text-white md:text-3xl",children:a}),e("div",{className:"text-xs font-bold text-white/90 md:text-sm",children:i})]})]})}function N({onClick:n,isStarting:l,showCountdown:s,autoStartCountdown:r}){return s?e("div",{className:"inline-flex h-14 w-48 items-center justify-center rounded-full bg-yellow-400 text-2xl font-black text-[#1a3a6e]",children:r}):t("button",{onClick:n,disabled:l,className:"group relative mt-5 inline-flex cursor-pointer items-center gap-3 rounded-full bg-gradient-to-b from-[#ffd54a] via-[#ffb733] to-[#ff8a1f] py-2.5 pl-8 pr-10 shadow-[0_6px_0_0_#c46410,0_10px_18px_rgba(0,0,0,0.4)] ring-2 ring-orange-300/60 transition-all duration-150 hover:-translate-y-0.5 hover:brightness-110 hover:shadow-[0_8px_0_0_#c46410,0_14px_22px_rgba(0,0,0,0.45)] active:translate-y-1.5 active:shadow-[0_1px_0_0_#c46410,0_2px_6px_rgba(0,0,0,0.3)] disabled:cursor-not-allowed disabled:opacity-70",children:[l?e(i,{className:"h-7 w-7 animate-spin text-white"}):e(a,{src:b,alt:"",width:36,height:36,unoptimized:!0}),e("span",{className:"text-xl font-black text-white",style:{color:"#685030"},children:"Bắt đầu"})]})}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { ThemeConfig } from '../../../../shared/lib/themes';
|
|
2
|
+
import type { StudentRoom } from '../../../../shared/types/student.types';
|
|
3
|
+
interface ThemedWaitingRoomProps {
|
|
4
|
+
room: StudentRoom;
|
|
5
|
+
theme: ThemeConfig;
|
|
6
|
+
onStart: () => void;
|
|
7
|
+
isStarting: boolean;
|
|
8
|
+
showCountdown: boolean;
|
|
9
|
+
autoStartCountdown: number;
|
|
10
|
+
/** Custom redirect URL for close button (default: student exams page) */
|
|
11
|
+
closeRedirectUrl?: string;
|
|
12
|
+
}
|
|
13
|
+
export declare function ThemedWaitingRoom({ room, theme, onStart, isStarting, showCountdown, autoStartCountdown, closeRedirectUrl, }: ThemedWaitingRoomProps): import("react").JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import a from"next/image";import{ROUTES as s}from"../../../../shared/constants/routes";import{CloseButton as i,ModeSelector as o,ExamInfoPanel as r,StartButton as n}from"./components";export function ThemedWaitingRoom({room:l,theme:c,onStart:d,isStarting:m,showCountdown:u,autoStartCountdown:h,closeRedirectUrl:p}){const b=l.name,g={parts:l.exam?.numberOfParts||1,duration:l.exam?.durationMinutes||15,maxScore:l.exam?.totalScore||8,passScore:l.exam?.passScore||0,examName:l.exam?.name,contestClassification:l.contest?.classification,level:l.exam?.level},f=c.visuals?.backgroundFilter??"grayscale(1) blur(10px) saturate(0.85) brightness(0.9)",v=c.visuals?.backgroundOverlay??"linear-gradient(to bottom, rgba(0, 0, 0, 0.2), transparent, rgba(0, 0, 0, 0.3))",x=c.visuals?.cardBackground??"radial-gradient(60% 60% at 50% 40%, rgba(184, 229, 157, 0.85) 0%, rgba(115, 213, 133, 0.85) 100%)";return t("div",{className:"fixed inset-0 z-50 flex items-center justify-center overflow-hidden",children:[e("div",{className:"absolute inset-0 scale-110 bg-cover bg-center",style:{backgroundImage:`url(${c.assets.background})`,filter:f}}),e("div",{className:"absolute inset-0",style:{background:v}}),t("div",{className:"relative z-10 w-[88%] max-w-2xl sm:w-[70%] md:w-[50%] lg:w-[42%]",children:[e(i,{imageSrc:c.assets.closeButton,onClick:()=>{"undefined"!=typeof window&&(window.location.href=p||s.STUDENT.CONTESTS_DISCOVER)},className:"absolute -right-3 -top-7 z-30",size:72}),t("div",{className:"absolute left-1/2 top-0 z-10 -translate-x-1/2 -translate-y-1/2",children:[e(a,{src:c.assets.titleBar,alt:"",width:260,height:56,className:"drop-shadow-lg",style:void 0,priority:!0,unoptimized:!0}),e("span",{className:"absolute mt-2.5 inset-0 flex items-center justify-center pb-1 text-sm font-extrabold tracking-wider text-white drop-shadow",children:c.displayName})]}),e("div",{className:"relative overflow-hidden rounded-3xl p-[3px] shadow-2xl",style:{background:"linear-gradient(180deg, #38bdf8 0%, #0ea5e9 35%, #0473f9 70%, #1d4ed8 100%)"},children:e("div",{className:"overflow-hidden rounded-[calc(1.5rem-3px)] ring-1 ring-white/30",children:t("div",{className:"relative p-6 pt-12 md:p-8 md:pt-14 lg:p-10 lg:pt-16",style:{background:x},children:[e("div",{className:"mb-5",children:e(o,{label:b,theme:c})}),e("div",{className:"mb-6",children:e(r,{parts:g.parts,duration:g.duration,maxScore:g.maxScore,passScore:g.passScore,examName:g.examName,contestClassification:g.contestClassification,level:g.level,theme:c})}),e("div",{className:"flex justify-center pb-2",children:e(n,{onClick:d,isLoading:m,showCountdown:u,countdown:h,rocketSrc:c.assets.startIcon,theme:c})}),e("div",{className:"pointer-events-none absolute -bottom-1 -left-1 opacity-60",children:e(a,{src:c.assets.bottomLeft,alt:"",width:80,height:80,unoptimized:!0})}),e("div",{className:"pointer-events-none absolute -bottom-1 -right-1 opacity-60",children:e(a,{src:c.assets.bottomRight,alt:"",width:80,height:80,unoptimized:!0})})]})})})]}),c.assets.feedbackButton&&e("button",{className:"absolute bottom-4 right-0 z-20 transition-transform hover:scale-105","aria-label":"Báo cáo lỗi",children:e(a,{src:c.assets.feedbackButton,alt:"Báo cáo lỗi",width:35,height:100,unoptimized:!0})})]})}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { StudentRoom } from '../../../../shared/types/student.types';
|
|
2
|
+
interface ToeicWaitingRoomProps {
|
|
3
|
+
room: StudentRoom;
|
|
4
|
+
onStart: () => void;
|
|
5
|
+
isStarting: boolean;
|
|
6
|
+
showCountdown: boolean;
|
|
7
|
+
autoStartCountdown: number;
|
|
8
|
+
closeRedirectUrl?: string;
|
|
9
|
+
onClose?: () => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Professional waiting room for TOEIC exam entry.
|
|
13
|
+
* Neutral, business-appropriate design (no cartoon elements).
|
|
14
|
+
*/
|
|
15
|
+
export declare function ToeicWaitingRoom({ room, onStart, isStarting, showCountdown, autoStartCountdown, onClose, }: ToeicWaitingRoomProps): import("react").JSX.Element;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as a,Fragment as t}from"react/jsx-runtime";import{Loader2 as s}from"lucide-react";import{Button as l}from"../../../../components/ui/button";export function ToeicWaitingRoom({room:n,onStart:r,isStarting:i,showCountdown:d,autoStartCountdown:c,onClose:m}){return a("div",{className:"fixed inset-0 z-50 flex flex-col items-center justify-center overflow-auto bg-gradient-to-b from-slate-50 to-slate-100 px-4 py-8",children:[a("div",{className:"mb-8 text-center",children:[a("div",{className:"mb-2 inline-flex items-center gap-2 rounded-full border border-blue-200 bg-blue-50 px-4 py-1.5",children:[e("span",{className:"h-2 w-2 rounded-full bg-blue-500"}),e("span",{className:"text-xs font-semibold uppercase tracking-wider text-blue-700",children:"Ocean Edu"})]}),e("h1",{className:"text-2xl font-bold text-gray-900 sm:text-3xl",children:"TOEIC Listening and Reading Test"}),e("p",{className:"mt-2 text-gray-500",children:n.name})]}),a("div",{className:"w-full max-w-lg rounded-2xl border border-gray-200 bg-white p-6 shadow-sm sm:p-8",children:[a("div",{className:"mb-6",children:[e("h2",{className:"mb-4 text-sm font-semibold uppercase tracking-wider text-gray-400",children:"Cấu trúc bài thi"}),a("div",{className:"space-y-3",children:[a("div",{className:"rounded-lg border border-gray-100 bg-gray-50 p-4",children:[a("div",{className:"mb-2 flex items-center justify-between",children:[e("span",{className:"text-sm font-semibold text-gray-700",children:"Listening"}),e("span",{className:"text-xs font-medium text-gray-400",children:"~45 phút"})]}),e("div",{className:"flex flex-wrap gap-1.5",children:["Part 1: Photographs","Part 2: Question-Response","Part 3: Conversations"].map(a=>e("span",{className:"rounded bg-blue-100 px-2 py-0.5 text-xs text-blue-700",children:a},a))}),e("p",{className:"mt-2 text-xs text-gray-500",children:"100 câu hỏi · Nghe và chọn đáp án đúng"})]}),a("div",{className:"rounded-lg border border-gray-100 bg-gray-50 p-4",children:[a("div",{className:"mb-2 flex items-center justify-between",children:[e("span",{className:"text-sm font-semibold text-gray-700",children:"Reading"}),e("span",{className:"text-xs font-medium text-gray-400",children:"~75 phút"})]}),e("div",{className:"flex flex-wrap gap-1.5",children:["Part 5: Incomplete Sentences","Part 6: Text Completion","Part 7: Reading Comprehension"].map(a=>e("span",{className:"rounded bg-green-100 px-2 py-0.5 text-xs text-green-700",children:a},a))}),e("p",{className:"mt-2 text-xs text-gray-500",children:"100 câu hỏi · Đọc hiểu và chọn đáp án đúng"})]})]})]}),a("div",{className:"mb-6 rounded-lg border border-amber-100 bg-amber-50 p-4",children:[e("h3",{className:"mb-2 text-sm font-semibold text-amber-700",children:"Lưu ý quan trọng"}),a("ul",{className:"space-y-1 text-xs text-amber-800",children:[a("li",{className:"flex items-start gap-1.5",children:[e("span",{className:"mt-0.5 h-1.5 w-1.5 shrink-0 rounded-full bg-amber-500"}),"Không thể tạm dừng bài thi sau khi bắt đầu"]}),a("li",{className:"flex items-start gap-1.5",children:[e("span",{className:"mt-0.5 h-1.5 w-1.5 shrink-0 rounded-full bg-amber-500"}),"Không thể quay lại câu trước sau khi chuyển câu"]}),a("li",{className:"flex items-start gap-1.5",children:[e("span",{className:"mt-0.5 h-1.5 w-1.5 shrink-0 rounded-full bg-amber-500"}),"Bài thi sẽ tự động nộp khi hết thời gian 120 phút"]}),a("li",{className:"flex items-start gap-1.5",children:[e("span",{className:"mt-0.5 h-1.5 w-1.5 shrink-0 rounded-full bg-amber-500"}),"Điểm số: Listening (5–495) + Reading (5–495) = Total (10–990)"]})]})]}),e("div",{className:"mb-6 grid grid-cols-3 gap-3",children:[{label:"Tổng câu hỏi",value:"200"},{label:"Thời gian",value:"120 phút"},{label:"Thang điểm",value:"990"}].map(({label:t,value:s})=>a("div",{className:"rounded-lg bg-slate-50 p-3 text-center",children:[e("div",{className:"text-lg font-bold text-gray-900",children:s}),e("div",{className:"text-xs text-gray-400",children:t})]},t))}),e(l,{onClick:r,disabled:i,className:"h-12 w-full rounded-xl bg-blue-600 text-base font-semibold hover:bg-blue-700 disabled:cursor-not-allowed disabled:opacity-60",children:i?a(t,{children:[e(s,{className:"mr-2 h-4 w-4 animate-spin"}),"Đang khởi tạo..."]}):d?a(t,{children:["Bắt đầu sau ",c,"s..."]}):"Bắt đầu bài thi"})]}),m&&e("button",{onClick:m,className:"mt-6 text-sm text-gray-400 hover:text-gray-600",children:"Quay lại danh sách phòng thi"})]})}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
interface CloseButtonProps {
|
|
2
|
+
imageSrc: string;
|
|
3
|
+
onClick: () => void;
|
|
4
|
+
className?: string;
|
|
5
|
+
size?: number;
|
|
6
|
+
imageFilter?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function CloseButton({ imageSrc, onClick, className, size, imageFilter }: CloseButtonProps): import("react").JSX.Element;
|
|
9
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e}from"react/jsx-runtime";import t from"next/image";export function CloseButton({imageSrc:i,onClick:r,className:a="",size:o=80,imageFilter:s}){return e("button",{onClick:r,className:`cursor-pointer transition-transform hover:scale-105 active:scale-95 ${a}`,"aria-label":"Đóng",children:e(t,{src:i,alt:"Đóng",width:o,height:o,className:"drop-shadow-lg",style:s?{filter:s}:void 0,unoptimized:!0})})}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ThemeConfig } from '../../../../../shared/lib/themes';
|
|
2
|
+
interface ExamInfoPanelProps {
|
|
3
|
+
parts: number;
|
|
4
|
+
duration: number;
|
|
5
|
+
maxScore: number;
|
|
6
|
+
passScore: number;
|
|
7
|
+
examName?: string;
|
|
8
|
+
contestClassification?: string;
|
|
9
|
+
level?: string;
|
|
10
|
+
theme: ThemeConfig;
|
|
11
|
+
}
|
|
12
|
+
export declare function ExamInfoPanel({ parts, duration, maxScore, passScore, examName, level, }: ExamInfoPanelProps): import("react").JSX.Element;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{Clock as l,Trophy as i,Target as n,Layers as a,FileText as r,Sparkles as s}from"lucide-react";const x={A1:{bg:"bg-emerald-100",text:"text-emerald-700",ring:"ring-emerald-300/50"},A2:{bg:"bg-teal-100",text:"text-teal-700",ring:"ring-teal-300/50"},B1:{bg:"bg-blue-100",text:"text-blue-700",ring:"ring-blue-300/50"},B2:{bg:"bg-indigo-100",text:"text-indigo-700",ring:"ring-indigo-300/50"},C1:{bg:"bg-purple-100",text:"text-purple-700",ring:"ring-purple-300/50"},C2:{bg:"bg-rose-100",text:"text-rose-700",ring:"ring-rose-300/50"}};export function ExamInfoPanel({parts:d,duration:o,maxScore:c,passScore:g,examName:m,level:h}){const b=h?x[h]||{bg:"bg-gray-100",text:"text-gray-700",ring:"ring-gray-300/50"}:null;return t("div",{className:"overflow-hidden rounded-2xl bg-[#0473f9] ring-1 ring-white/25",children:[t("div",{className:"relative flex items-center justify-between border-b border-white/15 bg-[#002249]/45 px-5 py-3",children:[e(s,{className:"absolute left-3 top-2 h-3 w-3 text-yellow-300/45"}),e("h2",{className:"text-lg font-extrabold tracking-wide text-white drop-shadow-sm",children:"Chi tiết bài thi"}),h&&b&&e("span",{className:`rounded-full px-3.5 py-1 text-xs font-bold ring-2 ${b.bg} ${b.text} ${b.ring}`,children:h})]}),t("div",{className:"space-y-3 p-4",children:[t("div",{className:"grid grid-cols-3 gap-2.5",children:[t("div",{className:"rounded-xl bg-white/15 px-2 py-3 text-center ring-1 ring-white/20",children:[e(a,{className:"mx-auto mb-1.5 h-5 w-5 text-sky-100"}),e("span",{className:"block text-2xl font-extrabold leading-none text-white",children:d}),e("span",{className:"mt-1 block text-xs font-bold text-sky-100",children:"Phần thi"})]}),t("div",{className:"rounded-xl bg-white/15 px-2 py-3 text-center ring-1 ring-white/20",children:[e(l,{className:"mx-auto mb-1.5 h-5 w-5 text-cyan-100"}),e("span",{className:"block text-2xl font-extrabold leading-none text-white",children:o}),e("span",{className:"mt-1 block text-xs font-bold text-cyan-100",children:"Phút"})]}),t("div",{className:"rounded-xl bg-white/15 px-2 py-3 text-center ring-1 ring-white/20",children:[e(i,{className:"mx-auto mb-1.5 h-5 w-5 text-indigo-100"}),e("span",{className:"block text-2xl font-extrabold leading-none text-white",children:c}),e("span",{className:"mt-1 block text-xs font-bold text-indigo-100",children:"Thang điểm"})]})]}),t("div",{className:"overflow-hidden rounded-xl bg-[#002249]/18 ring-1 ring-white/12",children:[m&&t("div",{className:"flex items-center gap-3 px-3 py-2.5",children:[e(r,{className:"h-4 w-4 shrink-0 text-yellow-300"}),e("span",{className:"text-sm font-bold text-sky-100",children:"Đề thi"}),e("span",{className:"ml-auto text-right text-sm font-extrabold text-white",children:m})]}),m&&e("div",{className:"border-t border-white/10"}),t("div",{className:"flex items-center gap-3 px-3 py-2.5",children:[e(n,{className:"h-4 w-4 shrink-0 text-yellow-300"}),e("span",{className:"text-sm font-bold text-white",children:"Điểm tối thiểu cần đạt"}),t("span",{className:"ml-auto text-base font-extrabold text-yellow-300",children:[g,"/",c]})]})]})]})]})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{User as r}from"lucide-react";export function ModeSelector({label:i}){return e("div",{className:"flex justify-center",children:t("div",{className:"flex items-center gap-3 rounded-2xl bg-white/10 px-6 py-3 ring-1 ring-white/15 backdrop-blur-sm",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-full bg-white/15",children:e(r,{className:"h-4 w-4 text-white/80"})}),e("p",{className:"text-center text-lg font-bold uppercase leading-5 tracking-wider text-white drop-shadow-sm",children:i})]})})}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ThemeConfig } from '../../../../../shared/lib/themes';
|
|
2
|
+
interface StartButtonProps {
|
|
3
|
+
onClick: () => void;
|
|
4
|
+
isLoading: boolean;
|
|
5
|
+
showCountdown: boolean;
|
|
6
|
+
countdown: number;
|
|
7
|
+
rocketSrc: string;
|
|
8
|
+
theme: ThemeConfig;
|
|
9
|
+
}
|
|
10
|
+
export declare function StartButton({ onClick, isLoading, showCountdown, countdown, rocketSrc, }: StartButtonProps): import("react").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import a from"next/image";import{Loader2 as s}from"lucide-react";export function StartButton({onClick:i,isLoading:l,showCountdown:r,countdown:n,rocketSrc:o}){return r?t("div",{className:"flex flex-col items-center gap-4",children:[t("div",{className:"relative flex h-24 w-24 items-center justify-center",children:[e("div",{className:"absolute inset-0 animate-ping rounded-full bg-sky-400/20"}),e("div",{className:"absolute inset-0 animate-pulse rounded-full bg-blue-500/10"}),e("div",{className:"relative flex h-20 w-20 items-center justify-center rounded-full bg-[#0473f9] shadow-[0_4px_0_0_#1d4ed8]",children:e("span",{className:"text-4xl font-extrabold text-white drop-shadow",children:n})})]}),t("p",{className:"text-sm font-medium text-white/90 drop-shadow",children:["Bài thi sẽ tự động bắt đầu sau ",e("strong",{className:"text-yellow-300",children:n})," giây"]}),l&&t("div",{className:"flex items-center gap-2 rounded-full bg-white/10 px-4 py-1.5 text-white/80 backdrop-blur-sm",children:[e(s,{className:"h-4 w-4 animate-spin"}),e("span",{className:"text-sm",children:"Đang bắt đầu bài thi..."})]})]}):e("div",{className:"relative flex flex-col items-center",children:t("button",{onClick:i,disabled:l,className:"group relative flex cursor-pointer items-center gap-4 rounded-full bg-[#0473f9] px-10 py-4 shadow-[0_5px_0_0_#1d4ed8] transition-all duration-150 hover:brightness-110 active:translate-y-[3px] active:shadow-[0_2px_0_0_#1d4ed8] disabled:cursor-not-allowed disabled:opacity-50",children:[e("div",{className:"relative h-11 w-11 transition-transform duration-300 group-hover:-translate-y-1 group-hover:rotate-[-8deg]",children:e(a,{src:o,alt:"",fill:!0,className:"object-contain drop-shadow",unoptimized:!0})}),e("span",{className:"text-xl font-extrabold tracking-wide text-white drop-shadow-sm",children:"Bắt đầu"}),l&&e(s,{className:"h-5 w-5 animate-spin text-white/80"})]})})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{CloseButton}from"./CloseButton";export{ModeSelector}from"./ModeSelector";export{ExamInfoPanel}from"./ExamInfoPanel";export{StartButton}from"./StartButton";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ThemedWaitingRoom } from './ThemedWaitingRoom';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{ThemedWaitingRoom}from"./ThemedWaitingRoom";
|
|
@@ -4,10 +4,12 @@ export interface ResultReviewPageContainerProps {
|
|
|
4
4
|
attemptId: string;
|
|
5
5
|
token?: string | null;
|
|
6
6
|
api: ResultReviewSdkApi;
|
|
7
|
+
/** When true, polling is disabled and PDF readiness is signalled (headless render). */
|
|
8
|
+
isPrintMode?: boolean;
|
|
7
9
|
onBack?: () => void;
|
|
8
10
|
onNavigate?: (href: string) => void;
|
|
9
11
|
topActionsSlot?: ReactNode;
|
|
10
12
|
beforeReportSlot?: ReactNode;
|
|
11
13
|
afterReportSlot?: ReactNode;
|
|
12
14
|
}
|
|
13
|
-
export declare function ResultReviewPageContainer({ attemptId, token, api, onBack, onNavigate, topActionsSlot, beforeReportSlot, afterReportSlot }: ResultReviewPageContainerProps): import("react").JSX.Element;
|
|
15
|
+
export declare function ResultReviewPageContainer({ attemptId, token, api, isPrintMode, onBack, onNavigate, topActionsSlot, beforeReportSlot, afterReportSlot }: ResultReviewPageContainerProps): import("react").JSX.Element;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useCallback as a,useEffect as n,useMemo as l,useState as s}from"react";import{ChevronLeft as i,Download as r,Loader2 as c,Lock as o,Sparkles as m,XCircle as d}from"lucide-react";import{Button as u}from"../../components/ui/button";import{Card as h,CardContent as p,CardHeader as f,CardTitle as x}from"../../components/ui/card";import{useToast as b}from"../../shared/lib/hooks/useToast";import{transformResultAnswersForReview as g}from"./review-data";import{ReviewQuestionRenderer as N}from"./ReviewQuestionRenderer";import{unwrap as w}from"./result-review.utils";import{useResultReviewPolling as v}from"./hooks/useResultReviewPolling";import{isResultNotPublic as y,setPdfReady as k}from"./review-status.utils";export function ResultReviewPageContainer({attemptId:m,token:N,api:S,isPrintMode:F=!1,onBack:j,onNavigate:$,topActionsSlot:C,beforeReportSlot:D,afterReportSlot:q}){const{error:M,success:O}=b(),[K,L]=s(null),[Q,U]=s(null),[B,T]=s(!0),[X,H]=s(null),[I,G]=s(!1),z=a(async e=>{e&&T(!0);try{const[e,t]=await Promise.all([S.getDetailedResult(m,N),S.getAttempt?.(m,N)]);L(w(e)),U(t?w(t):null),H(null)}catch(e){H(e),y(e,Boolean(N))||M({message:"Không thể tải kết quả",description:e instanceof Error?e.message:void 0})}finally{e&&T(!1)}},[S,m,N,M]);n(()=>{let e=!1;return z(!0).then(()=>{}),()=>{e=!0}},[z]);const J=a(()=>{z(!1)},[z]);v({detail:K,isPrintMode:F,isLoading:B,onRefetch:J}),n(()=>{if(!F||B)return;const e=requestAnimationFrame(()=>k(!0));return()=>{cancelAnimationFrame(e),k(!1)}},[F,B]);const V=l(()=>g(K?.answers??[],Q?.exam?.template?.parts),[K?.answers,Q?.exam?.template?.parts]),W=Q?.studentName??"Học sinh",Y=Q?.studentUsername,Z=Q?.branchName??"Chưa tham gia chi nhánh",_=Q?.exam?.template?.title??K?.examName??"Kết quả bài thi",ee=()=>{j?j():$?$("/student/results"):"undefined"!=typeof window&&window.history.back()},te=async()=>{if(K&&S.exportPdf){G(!0);try{const{blob:e,filename:t}=await S.exportPdf(m,Q?.exam?.template?.type);!function(e,t){const a=URL.createObjectURL(e),n=document.createElement("a");n.href=a,n.download=t,document.body.appendChild(n),n.click(),document.body.removeChild(n),URL.revokeObjectURL(a)}(e,t),O({message:"Xuất PDF thành công"})}catch(e){M({message:"Không xuất được PDF",description:e instanceof Error?e.message:void 0})}finally{G(!1)}}};return B?e(E,{icon:e(c,{className:"h-8 w-8 animate-spin text-[#2874A6]"}),title:"Đang tải kết quả bài thi..."}):y(X,Boolean(N))?e(E,{icon:e(o,{className:"h-8 w-8 text-amber-500"}),title:"Kết quả này chưa được công khai",action:e(u,{onClick:ee,children:"Quay lại"})}):K?e("div",{className:"min-h-screen bg-[#e0e0e0] py-4 md:py-6",children:t("div",{className:"mx-auto flex w-full max-w-[1160px] flex-col gap-4 px-3 sm:px-4",children:[t("div",{className:"flex items-center justify-between gap-3 print:hidden",children:[t(u,{variant:"outline",onClick:ee,children:[e(i,{className:"mr-2 h-4 w-4"}),"Quay lại"]}),t("div",{className:"flex items-center gap-2",children:[S.exportPdf&&t(u,{onClick:()=>{te()},disabled:I,className:"bg-[#00AEEF] hover:bg-[#0099D6]",children:[I?e(c,{className:"mr-2 h-4 w-4 animate-spin"}):e(r,{className:"mr-2 h-4 w-4"}),"Xuất PDF"]}),C]})]}),D,t(h,{className:"overflow-hidden rounded-[28px] border-0 shadow-xl",children:[t(f,{className:"bg-gradient-to-r from-[#0054A6] to-[#00AEEF] text-white",children:[e(x,{className:"text-2xl",children:_}),t("p",{className:"text-sm text-white/80",children:[W,Y?` • ${Y}`:""," • ",Z]})]}),t(p,{className:"space-y-6 p-5 md:p-8",children:[e(A,{detail:K}),e(R,{detail:K}),!1!==K.showDetailedResults&&e(P,{reviewData:V})]})]}),q]})}):e(E,{icon:e(d,{className:"h-8 w-8 text-red-400"}),title:"Không thể tải kết quả",action:e(u,{onClick:ee,children:"Quay lại"})})}function A({detail:a}){return t("div",{className:"grid gap-3 md:grid-cols-4",children:[e(S,{label:"Tổng điểm",value:`${F(a.totalScore)} / ${F(a.maxScore)}`}),e(S,{label:"Câu đúng",value:`${a.correctAnswers} / ${a.totalQuestions}`}),e(S,{label:"Xếp hạng",value:a.rank?`${a.rank}/${a.totalStudents??"-"}`:"-"}),e(S,{label:"Trạng thái",value:a.isPendingGrading?"Đang chấm":"Hoàn tất"})]})}function R({detail:a}){if(!a.llmOverallAssessment&&!a.llmSkillAssessment)return null;const n=Object.entries(a.llmSkillAssessment??{}).filter(e=>null!=e[1]);return t("section",{className:"rounded-2xl border border-[#00AEEF]/30 bg-[#f0f8ff] p-4",children:[t("div",{className:"mb-2 flex items-center gap-2 font-bold text-[#0054A6]",children:[e(m,{className:"h-5 w-5"}),"Phân tích tổng quan"]}),a.llmOverallAssessment&&e("p",{className:"whitespace-pre-wrap text-sm text-slate-700",children:a.llmOverallAssessment}),n.length>0&&e("div",{className:"mt-3 grid gap-2 md:grid-cols-2",children:n.map(([a,n])=>t("div",{className:"rounded-xl bg-white p-3 text-sm",children:[e("strong",{children:n.label}),e("p",{className:"mt-1 text-slate-600",children:n.feedback})]},a))})]})}function P({reviewData:a}){return e("div",{className:"space-y-6",children:a.parts.map((n,l)=>t("section",{className:"rounded-2xl border border-[#00AEEF] bg-white p-5",children:[t("div",{className:"mb-4 flex items-center justify-between",children:[e("h3",{className:"text-lg font-bold text-[#0054A6]",children:n.name||`Phần ${n.partNo}`}),a.partScoresMap[l]&&t("span",{className:"rounded-full bg-[#0054A6] px-3 py-1 text-sm font-bold text-white",children:[F(a.partScoresMap[l].score)," / ",F(a.partScoresMap[l].maxScore)]})]}),n.instructions&&e("p",{className:"mb-4 font-semibold text-slate-700",children:n.instructions}),e(N,{part:n,reviewData:a})]},`${n.partId}-${l}`))})}function S({label:a,value:n}){return t("div",{className:"rounded-2xl bg-slate-50 p-4",children:[e("p",{className:"text-xs font-semibold uppercase text-slate-500",children:a}),e("p",{className:"mt-1 text-xl font-black text-[#0054A6]",children:n})]})}function E({icon:a,title:n,action:l}){return e("div",{className:"flex min-h-screen items-center justify-center bg-[#e0e0e0]",children:t("div",{className:"rounded-[28px] bg-white p-8 text-center shadow-xl",children:[e("div",{className:"mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-slate-50",children:a}),e("p",{className:"font-semibold text-slate-700",children:n}),l&&e("div",{className:"mt-5",children:l})]})})}function F(e){return null!=e&&Number.isFinite(e)?e<10?e.toFixed(1):String(Math.round(e)):"0"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ResultReviewDetail } from '../../../api/result-review';
|
|
2
|
+
export interface UseResultReviewPollingOptions {
|
|
3
|
+
/** Latest loaded result detail (null while first load is in flight). */
|
|
4
|
+
detail: ResultReviewDetail | null;
|
|
5
|
+
/** Disable polling entirely (e.g. headless PDF print mode). */
|
|
6
|
+
isPrintMode: boolean;
|
|
7
|
+
/** Whether the initial load has finished. */
|
|
8
|
+
isLoading: boolean;
|
|
9
|
+
/** Poll interval in milliseconds. */
|
|
10
|
+
intervalMs?: number;
|
|
11
|
+
/** Called on each tick to refetch the result; should be stable. */
|
|
12
|
+
onRefetch: () => void;
|
|
13
|
+
}
|
|
14
|
+
export interface UseResultReviewPollingResult {
|
|
15
|
+
/** `true` while some answers are still being auto/LLM graded. */
|
|
16
|
+
hasPendingQuestions: boolean;
|
|
17
|
+
/** `true` while the LLM overall assessment hasn't arrived yet. */
|
|
18
|
+
hasPendingLlm: boolean;
|
|
19
|
+
/** `true` while polling is active. */
|
|
20
|
+
isPolling: boolean;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Polls the result endpoint while any answer is still being graded or the LLM
|
|
24
|
+
* assessment hasn't been generated yet. Ports the host app's behaviour: polling
|
|
25
|
+
* is suppressed in print mode so the headless PDF renderer reaches an idle state.
|
|
26
|
+
*/
|
|
27
|
+
export declare function useResultReviewPolling({ detail, isPrintMode, isLoading, intervalMs, onRefetch, }: UseResultReviewPollingOptions): UseResultReviewPollingResult;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{useEffect as e,useRef as n}from"react";const s=new Set(["PENDING","PROCESSING","QUEUED","IN_PROGRESS"]);export function useResultReviewPolling({detail:t,isPrintMode:l,isLoading:r,intervalMs:i=5e3,onRefetch:a}){const o=(u=t?.answers,!!u&&u.some(e=>null!=e.gradingStatus&&s.has(e.gradingStatus)));var u;const c=Boolean(t?.llmOverallAssessment||t?.llmSkillAssessment),g=Boolean(t)&&!c,m=!l&&!r&&(o||g),P=n(a);return P.current=a,e(()=>{if(!m)return;const e=setInterval(()=>P.current(),i);return()=>clearInterval(e)},[i,m]),{hasPendingQuestions:o,hasPendingLlm:g,isPolling:m}}
|
|
@@ -2,3 +2,6 @@ export { ResultReviewPageContainer } from './ResultReviewPageContainer';
|
|
|
2
2
|
export type { ResultReviewPageContainerProps } from './ResultReviewPageContainer';
|
|
3
3
|
export { ReviewQuestionRenderer } from './ReviewQuestionRenderer';
|
|
4
4
|
export type { ReviewQuestionRendererProps } from './ReviewQuestionRenderer';
|
|
5
|
+
export { useResultReviewPolling } from './hooks/useResultReviewPolling';
|
|
6
|
+
export type { UseResultReviewPollingOptions, UseResultReviewPollingResult } from './hooks/useResultReviewPolling';
|
|
7
|
+
export { isResultNotPublic, setPdfReady } from './review-status.utils';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export{ResultReviewPageContainer}from"./ResultReviewPageContainer";export{ReviewQuestionRenderer}from"./ReviewQuestionRenderer";
|
|
1
|
+
export{ResultReviewPageContainer}from"./ResultReviewPageContainer";export{ReviewQuestionRenderer}from"./ReviewQuestionRenderer";export{useResultReviewPolling}from"./hooks/useResultReviewPolling";export{isResultNotPublic,setPdfReady}from"./review-status.utils";
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/** Status & access helpers for the result review screen. */
|
|
2
|
+
/**
|
|
3
|
+
* Determine whether a load error means "this result is not public". Ports the
|
|
4
|
+
* host app's logic: a 403 with an explicit `RESULT_NOT_PUBLIC`/`FORBIDDEN` code,
|
|
5
|
+
* or any 403 reached via a token link (the only anonymous-access path).
|
|
6
|
+
*/
|
|
7
|
+
export declare function isResultNotPublic(error: unknown, hasToken: boolean): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Toggle the `data-pdf-ready` body attribute the headless PDF renderer waits on.
|
|
10
|
+
* Call with `true` once the result has finished loading in print mode.
|
|
11
|
+
*/
|
|
12
|
+
export declare function setPdfReady(ready: boolean): void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const t=new Set(["RESULT_NOT_PUBLIC","FORBIDDEN"]);export function isResultNotPublic(e,n){const o=function(t){return"object"!=typeof t||null===t?null:t}(e);if(403!==o?.response?.status)return!1;const r=o.response.data?.errorCode;return r?t.has(r):n}export function setPdfReady(t){"undefined"!=typeof document&&(t?document.body.setAttribute("data-pdf-ready","true"):document.body.removeAttribute("data-pdf-ready"))}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
interface SpeakingAudioPlayerProps {
|
|
2
|
+
audioUrl: string;
|
|
3
|
+
onPlayingChange?: (isPlaying: boolean) => void;
|
|
4
|
+
/**
|
|
5
|
+
* Auto-play the audio a few seconds (3s) after it loads or changes — e.g.
|
|
6
|
+
* when navigating to the next question. Defaults to false so other usages
|
|
7
|
+
* (such as model-pronunciation playback) are not auto-triggered.
|
|
8
|
+
*/
|
|
9
|
+
autoPlay?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function SpeakingAudioPlayer({ audioUrl, onPlayingChange, autoPlay }: SpeakingAudioPlayerProps): import("react").JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import{useRef as t,useState as n,useEffect as r}from"react";import{Pause as i,Play as o}from"lucide-react";import{usePresignedFileUrl as d}from"../../../shared/lib/hooks/usePresignedFileUrl";import{useDelayedAutoPlay as l}from"../../../shared/lib/hooks/useDelayedAutoPlay";import s from"./SpeakingAudioPlayer.module.css";const u=[.4,.6,.85,.55,.75,1,.65,.45,.7,.9,.6,.5,.8,.65,.95,.7,.5,.6,.8,.45];export function SpeakingAudioPlayer({audioUrl:c,onPlayingChange:m,autoPlay:p=!1}){const v=t(null),[h,y]=n(!1),[g,f]=n(0),[L,E]=n(0),{previewUrl:N}=d(c||void 0);r(()=>{const e=v.current;if(!e)return;const a=()=>{e.duration&&!isNaN(e.duration)&&isFinite(e.duration)&&E(e.duration)},t=()=>{f(e.currentTime),a()},n=()=>{y(!0),m?.(!0)},r=()=>{y(!1),m?.(!1)},i=()=>{y(!1),f(0),m?.(!1)};return a(),e.addEventListener("loadedmetadata",a),e.addEventListener("durationchange",a),e.addEventListener("canplay",a),e.addEventListener("timeupdate",t),e.addEventListener("play",n),e.addEventListener("pause",r),e.addEventListener("ended",i),()=>{e.removeEventListener("loadedmetadata",a),e.removeEventListener("durationchange",a),e.removeEventListener("canplay",a),e.removeEventListener("timeupdate",t),e.removeEventListener("play",n),e.removeEventListener("pause",r),e.removeEventListener("ended",i)}},[N]),r(()=>{y(!1),f(0),E(0)},[N]),l({audioRef:v,audioUrl:N,enabled:p});const b=L>0?g/L:0,P=!N;return a("div",{className:s.audioPill,children:[e("audio",{ref:v,...N?{src:N}:{},preload:"metadata"}),e("button",{type:"button",className:`${s.playButton} ${h?s.playButtonPlaying:""} ${P?s.playButtonDisabled:""}`,onClick:P?void 0:()=>{const e=v.current;e&&(h?e.pause():e.play().catch(e=>{console.warn("Audio play request was interrupted:",e)}))},disabled:P,"aria-label":P?"Không có âm thanh đính kèm":h?"Tạm dừng":"Nghe câu hỏi",title:P?"Không có âm thanh đính kèm":h?"Tạm dừng":"Nghe câu hỏi",children:h?e(i,{className:s.playIcon,fill:"currentColor"}):e(o,{className:s.playIcon,fill:"currentColor",style:{marginLeft:"2px"}})}),e("div",{className:s.waveform,"aria-hidden":"true",children:u.map((a,t)=>{const n=(t+.5)/u.length<=b;return e("span",{className:`${s.waveBar} ${h&&n?s.waveBarPlaying:""}`,style:{height:100*a+"%",background:n?"linear-gradient(180deg, #ffc93c, #ff9900)":"#d8e0ea",animationDelay:h&&n?t%6*.1+"s":void 0}},t)})})]})}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/* ===== Speaking Audio Player — "Listen to the question" ===== */
|
|
2
|
+
/* Small, simple, kid-friendly player for light backgrounds (Summer Sky theme):
|
|
3
|
+
a sky-blue play/replay button + a sunny-amber waveform progress indicator.
|
|
4
|
+
No time digits, no seek — compact and tap-friendly. */
|
|
5
|
+
|
|
6
|
+
.audioPill {
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
gap: 10px;
|
|
10
|
+
width: 100%;
|
|
11
|
+
padding: 5px 14px 5px 5px;
|
|
12
|
+
background: #ffffff;
|
|
13
|
+
border: 2px solid #dcebff;
|
|
14
|
+
border-radius: 999px;
|
|
15
|
+
box-shadow: 0 3px 10px rgba(0, 0, 0, 0.08);
|
|
16
|
+
box-sizing: border-box;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/* ===== Play / Pause button ===== */
|
|
20
|
+
.playButton {
|
|
21
|
+
flex-shrink: 0;
|
|
22
|
+
height: 42px;
|
|
23
|
+
width: 42px;
|
|
24
|
+
min-width: 42px;
|
|
25
|
+
border: none;
|
|
26
|
+
border-radius: 50%;
|
|
27
|
+
background: linear-gradient(135deg, #5ba8e8, #357abd);
|
|
28
|
+
color: #ffffff;
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
justify-content: center;
|
|
32
|
+
cursor: pointer;
|
|
33
|
+
padding: 0;
|
|
34
|
+
box-shadow: 0 3px 8px rgba(53, 122, 189, 0.35);
|
|
35
|
+
transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.2s ease;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.playButton:hover {
|
|
39
|
+
transform: scale(1.08);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.playButton:active {
|
|
43
|
+
transform: scale(0.95);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.playIcon {
|
|
47
|
+
width: 20px;
|
|
48
|
+
height: 20px;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/* Playing — coral with a gentle pulsing ring */
|
|
52
|
+
.playButtonPlaying {
|
|
53
|
+
background: linear-gradient(135deg, #ff8a8a, #ff6b6b);
|
|
54
|
+
box-shadow: 0 0 0 0 rgba(255, 107, 107, 0.5);
|
|
55
|
+
animation: playPulse 1.5s infinite;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@keyframes playPulse {
|
|
59
|
+
0% {
|
|
60
|
+
box-shadow: 0 0 0 0 rgba(255, 107, 107, 0.5);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
70% {
|
|
64
|
+
box-shadow: 0 0 0 8px rgba(255, 107, 107, 0);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
100% {
|
|
68
|
+
box-shadow: 0 0 0 0 rgba(255, 107, 107, 0);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/* Disabled — no audio attached */
|
|
73
|
+
.playButtonDisabled {
|
|
74
|
+
background: #cbd5e1;
|
|
75
|
+
box-shadow: none;
|
|
76
|
+
cursor: not-allowed;
|
|
77
|
+
opacity: 0.7;
|
|
78
|
+
animation: none;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.playButtonDisabled:hover {
|
|
82
|
+
transform: none;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* ===== Waveform progress indicator (display-only, fills L→R with progress) ===== */
|
|
86
|
+
.waveform {
|
|
87
|
+
flex: 1;
|
|
88
|
+
min-width: 0;
|
|
89
|
+
height: 20px;
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
gap: 2px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
.waveBar {
|
|
96
|
+
flex: 1;
|
|
97
|
+
min-width: 0;
|
|
98
|
+
border-radius: 999px;
|
|
99
|
+
transform-origin: center;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/* Subtle liveliness on the already-played region while playing */
|
|
103
|
+
.waveBarPlaying {
|
|
104
|
+
animation: waveBounce 0.9s ease-in-out infinite alternate;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
@keyframes waveBounce {
|
|
108
|
+
0% {
|
|
109
|
+
transform: scaleY(0.7);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
100% {
|
|
113
|
+
transform: scaleY(1);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
Binary file
|
|
Binary file
|
|
@@ -2,12 +2,20 @@ interface EnglishCertificationAudioPlayerProps {
|
|
|
2
2
|
audioUrl: string;
|
|
3
3
|
partNumber?: number;
|
|
4
4
|
isLoading?: boolean;
|
|
5
|
+
/**
|
|
6
|
+
* TOEIC play-once rule: once true, playback is permanently disabled for
|
|
7
|
+
* this part (persisted so a page reload can't be used to re-listen).
|
|
8
|
+
*/
|
|
9
|
+
isLocked?: boolean;
|
|
10
|
+
/** Fired the first time the student starts playback in this part. */
|
|
11
|
+
onFirstPlay?: () => void;
|
|
5
12
|
}
|
|
6
13
|
/**
|
|
7
14
|
* Branded Ocean Edu audio bar for English Certification Listening sections.
|
|
8
15
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
16
|
+
* Enforces the TOEIC "play once" rule: no seeking/scrubbing and no replay —
|
|
17
|
+
* once playback starts, `onFirstPlay` locks the part permanently (survives
|
|
18
|
+
* reload via the caller's persisted state).
|
|
11
19
|
*/
|
|
12
|
-
export declare function EnglishCertificationAudioPlayer({ audioUrl, partNumber, isLoading, }: EnglishCertificationAudioPlayerProps): import("react").JSX.Element;
|
|
20
|
+
export declare function EnglishCertificationAudioPlayer({ audioUrl, partNumber, isLoading, isLocked, onFirstPlay, }: EnglishCertificationAudioPlayerProps): import("react").JSX.Element;
|
|
13
21
|
export {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{useRef as t,useState as a,useEffect as
|
|
1
|
+
"use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{useRef as t,useState as a,useEffect as i}from"react";import{Play as d,Pause as l,RefreshCw as n,Lock as o}from"lucide-react";import{usePresignedFileUrl as s}from"../../../shared/lib/hooks/usePresignedFileUrl";import{cn as c}from"../../../shared/lib/utils";export function EnglishCertificationAudioPlayer({audioUrl:u,partNumber:m=0,isLoading:p=!1,isLocked:f=!1,onFirstPlay:b}){const g=t(null),{previewUrl:x}=s(u||void 0),[h,v]=a(!1),[y,w]=a(0),[N,L]=a(0),[E,_]=a(null),k=t(!1),P=p||!u||!!E||f,A=e=>{if(!e||isNaN(e))return"00:00";const r=Math.floor(e/60),t=Math.floor(e%60);return`${r.toString().padStart(2,"0")}:${t.toString().padStart(2,"0")}`};i(()=>{const e=g.current;if(!e)return;const r=()=>w(e.currentTime),t=()=>{L(e.duration),_(null)},a=()=>v(!1),i=()=>{_("Failed to load audio file"),v(!1)};return e.addEventListener("timeupdate",r),e.addEventListener("loadedmetadata",t),e.addEventListener("ended",a),e.addEventListener("error",i),()=>{e.removeEventListener("timeupdate",r),e.removeEventListener("loadedmetadata",t),e.removeEventListener("ended",a),e.removeEventListener("error",i)}},[]),i(()=>{g.current&&x&&(g.current.src=x,w(0),v(!1),_(null))},[x]);const $=N>0?y/N*100:0,j=f?"Already played":E?"Audio unavailable":p?"Loading...":h?"Playing...":y>0?"Paused":"Ready to play";return r("div",{className:"grid w-full grid-cols-[64px_1fr] items-center gap-4 rounded-[10px] border border-[#d9e4f2] bg-white/90 p-4 shadow-[0_5px_16px_rgba(13,71,161,0.08)] sm:gap-[18px] sm:px-5",children:[e("audio",{ref:g}),e("button",{type:"button",onClick:()=>{g.current&&!f&&(h?(g.current.pause(),v(!1)):(k.current||(k.current=!0,b?.()),g.current.play().catch(e=>{_(`Failed to play audio: ${e.message}`)}),v(!0)))},disabled:P,className:"grid h-14 w-14 place-items-center rounded-full text-2xl text-white shadow-[0_12px_22px_rgba(11,87,208,0.22)] transition-transform hover:scale-[1.03] disabled:cursor-not-allowed disabled:opacity-50",style:{background:"linear-gradient(180deg, #1f7cff, #0b57d0)"},"aria-label":f?"Audio locked, already played":h?"Pause audio":"Play audio",children:f?e(o,{size:22}):h?e(l,{size:24}):e(d,{size:24,className:"ml-0.5"})}),r("div",{className:"min-w-0",children:[e("div",{className:"flex items-center justify-between gap-3",children:r("span",{className:"text-[18px] font-black text-[#0f1730]",children:["Listening Audio",m>0&&` · Part ${m}`]})}),r("div",{className:"mt-1 flex items-center gap-2.5",children:[e("span",{className:c("text-sm font-bold",E?"text-red-500":"text-[#64748b]"),children:E??j}),E&&r("button",{type:"button",onClick:()=>{const e=g.current;e&&x&&(_(null),w(0),v(!1),e.src=x,e.load())},disabled:!u,className:"inline-flex items-center gap-1.5 rounded-full border border-red-200 bg-red-50 px-3 py-1 text-xs font-bold text-red-600 transition-colors hover:bg-red-100 disabled:cursor-not-allowed disabled:opacity-50","aria-label":"Retry loading audio",children:[e(n,{size:13}),"Retry"]})]}),r("div",{className:"mt-2 grid grid-cols-[46px_1fr_46px] items-center gap-2.5 text-sm font-bold tabular-nums text-[#0f1730]",children:[e("span",{children:A(y)}),e("div",{className:"relative h-2 w-full overflow-hidden rounded-full bg-[#dfe9f6]",role:"progressbar","aria-label":"Audio progress","aria-valuemin":0,"aria-valuemax":N||0,"aria-valuenow":y,children:e("div",{className:"absolute inset-y-0 left-0 rounded-full",style:{width:`${$}%`,background:"linear-gradient(90deg, #1f7cff, #0b57d0)"}})}),e("span",{children:N>0?A(N):"00:00"})]})]})]})}
|