@tinyweb_dev/oe-exam-sdk 0.1.2 → 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/exam-questions.d.ts +1 -1
- package/dist/api/exam-questions.js +1 -1
- package/dist/api/exam-questions.types.d.ts +45 -1
- package/dist/api/index.d.ts +2 -0
- package/dist/api/index.js +1 -1
- package/dist/components/exams/CompletionDialog.d.ts +8 -0
- package/dist/components/exams/CompletionDialog.js +1 -0
- package/dist/components/exams/CreateExamInfoForm.d.ts +4 -1
- package/dist/components/exams/CreateExamInfoForm.js +1 -1
- package/dist/components/exams/CreateExamPageContainer.d.ts +3 -0
- package/dist/components/exams/CreateExamPageContainer.js +1 -1
- package/dist/components/exams/ExamCreator.d.ts +9 -3
- package/dist/components/exams/ExamCreator.js +1 -1
- package/dist/components/exams/ExamPartTabs.d.ts +1 -1
- package/dist/components/exams/ExamPartTabs.js +1 -1
- package/dist/components/exams/ExamPreviewDialog.d.ts +20 -0
- package/dist/components/exams/ExamPreviewDialog.js +1 -0
- package/dist/components/exams/ExamQuestionGrid.d.ts +1 -1
- package/dist/components/exams/ExamQuestionGrid.js +1 -1
- package/dist/components/exams/ExamQuestionsPageContainer.d.ts +3 -0
- package/dist/components/exams/ExamQuestionsPageContainer.js +1 -1
- package/dist/components/exams/ExamViewDialog.d.ts +24 -0
- package/dist/components/exams/ExamViewDialog.js +1 -0
- package/dist/components/exams/TemplateSelector.js +1 -1
- package/dist/components/exams/UnsavedChangesDialog.d.ts +8 -0
- package/dist/components/exams/UnsavedChangesDialog.js +1 -0
- package/dist/components/exams/api.d.ts +1 -1
- package/dist/components/exams/create-exam.texts.d.ts +58 -0
- package/dist/components/exams/create-exam.texts.js +1 -0
- package/dist/components/exams/create-exam.validation.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/exam-question-status.d.ts +9 -0
- package/dist/components/exams/exam-question-status.js +1 -0
- package/dist/components/exams/index.d.ts +19 -1
- 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/cambridge-yle/CambridgeYleTimer.js +1 -1
- 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/index.d.ts +3 -0
- package/dist/shared/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/dist/shared/lib/utils/question-reverse-transform.d.ts +6 -0
- package/dist/shared/lib/utils/question-reverse-transform.js +1 -0
- package/dist/shared/lib/utils/question-transform-validation.d.ts +6 -0
- package/dist/shared/lib/utils/question-transform-validation.js +1 -0
- package/dist/shared/lib/utils/question-transform.d.ts +6 -2
- package/dist/shared/lib/utils/question-transform.js +1 -1
- package/package.json +19 -4
package/dist/components/exams/take/components/speaking/SpeakingInfoExchangeSummerSky.module.css
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
* Summer Sky theme — LISTEN_AND_SPEAK_INFO_EXCHANGE
|
|
3
|
+
* Bright, sky-blue palette matching SummerSkyLayout (ocean-frame).
|
|
4
|
+
* ============================================================ */
|
|
5
|
+
|
|
6
|
+
.part2Container {
|
|
7
|
+
border-radius: 10px;
|
|
8
|
+
height: 100%;
|
|
9
|
+
width: 100%;
|
|
10
|
+
display: flex;
|
|
11
|
+
align-items: center;
|
|
12
|
+
overflow: visible;
|
|
13
|
+
padding: 0 56px 12px; /* keep content inside the ocean-frame curved edges */
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.questionContainer {
|
|
18
|
+
height: 100%;
|
|
19
|
+
width: 100%;
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: stretch;
|
|
22
|
+
gap: 18px;
|
|
23
|
+
overflow: visible; /* allow floating pill badges to render outside the cards */
|
|
24
|
+
padding: 55px 4px 6px; /* top padding leaves room for the floating CANDIDATE'S COPY badge */
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* ===== Image side ===== */
|
|
28
|
+
.imageWrapper {
|
|
29
|
+
flex: 1 1 0;
|
|
30
|
+
min-width: 0;
|
|
31
|
+
height: 100%;
|
|
32
|
+
display: flex;
|
|
33
|
+
align-items: center;
|
|
34
|
+
justify-content: center;
|
|
35
|
+
position: relative;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.imageFrame {
|
|
39
|
+
width: 100%;
|
|
40
|
+
height: 100%;
|
|
41
|
+
border-radius: 18px;
|
|
42
|
+
background: linear-gradient(180deg, #E3F2FD 0%, #BBDEFB 100%);
|
|
43
|
+
border: 4px solid #FFFFFF;
|
|
44
|
+
box-shadow: 0 6px 20px rgba(13, 71, 161, 0.18);
|
|
45
|
+
overflow: hidden;
|
|
46
|
+
display: flex;
|
|
47
|
+
align-items: center;
|
|
48
|
+
justify-content: center;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.imageFrame img {
|
|
52
|
+
width: 100%;
|
|
53
|
+
height: 100%;
|
|
54
|
+
object-fit: contain;
|
|
55
|
+
border-radius: 14px;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.recordedBadge {
|
|
59
|
+
position: absolute;
|
|
60
|
+
right: 12px;
|
|
61
|
+
top: 12px;
|
|
62
|
+
z-index: 10;
|
|
63
|
+
display: flex;
|
|
64
|
+
align-items: center;
|
|
65
|
+
gap: 8px;
|
|
66
|
+
padding: 6px 14px;
|
|
67
|
+
border-radius: 9999px;
|
|
68
|
+
background: linear-gradient(180deg, #4CAF50 0%, #2E7D32 100%);
|
|
69
|
+
color: #fff;
|
|
70
|
+
font-weight: 700;
|
|
71
|
+
font-size: 13px;
|
|
72
|
+
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.25);
|
|
73
|
+
border: 2px solid #fff;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.recordedBadgeDot {
|
|
77
|
+
height: 9px;
|
|
78
|
+
width: 9px;
|
|
79
|
+
border-radius: 9999px;
|
|
80
|
+
background: #fff;
|
|
81
|
+
animation: ssPulse 1.2s infinite ease-in-out;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@keyframes ssPulse {
|
|
85
|
+
0%, 100% { opacity: 1; }
|
|
86
|
+
50% { opacity: 0.4; }
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/* ===== Cards side ===== */
|
|
90
|
+
.infoContainer {
|
|
91
|
+
flex: 1 1 0;
|
|
92
|
+
min-width: 0;
|
|
93
|
+
max-width: 46%;
|
|
94
|
+
height: 100%;
|
|
95
|
+
display: flex;
|
|
96
|
+
flex-direction: column;
|
|
97
|
+
gap: 18px;
|
|
98
|
+
overflow-y: auto;
|
|
99
|
+
overflow-x: hidden;
|
|
100
|
+
padding: 14px 8px 8px;
|
|
101
|
+
scrollbar-color: #1E88E5 transparent;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.infoContainer::-webkit-scrollbar {
|
|
105
|
+
width: 6px;
|
|
106
|
+
}
|
|
107
|
+
.infoContainer::-webkit-scrollbar-thumb {
|
|
108
|
+
background: #64B5F6;
|
|
109
|
+
border-radius: 9999px;
|
|
110
|
+
}
|
|
111
|
+
.infoContainer::-webkit-scrollbar-track {
|
|
112
|
+
background: transparent;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* ===== Card ===== */
|
|
116
|
+
.infoItem {
|
|
117
|
+
position: relative;
|
|
118
|
+
width: 100%;
|
|
119
|
+
background: #FFFFFF;
|
|
120
|
+
border: 2px solid #BFE0FF;
|
|
121
|
+
border-radius: 20px;
|
|
122
|
+
box-shadow: 0 4px 12px rgba(30, 90, 180, 0.12);
|
|
123
|
+
padding-top: 26px;
|
|
124
|
+
overflow: visible; /* must stay visible so the badge isn't clipped */
|
|
125
|
+
flex-shrink: 0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Inner wrapper that clips striping rows to the card's rounded corners */
|
|
129
|
+
.infoItemInner {
|
|
130
|
+
border-radius: 0 0 18px 18px;
|
|
131
|
+
overflow: hidden;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/* Pill badge floating over the top edge */
|
|
135
|
+
.infoHeader {
|
|
136
|
+
position: absolute;
|
|
137
|
+
top: -14px;
|
|
138
|
+
left: 50%;
|
|
139
|
+
transform: translateX(-50%);
|
|
140
|
+
z-index: 2;
|
|
141
|
+
background: linear-gradient(180deg, #1E88E5 0%, #1565C0 100%);
|
|
142
|
+
color: #FFFFFF;
|
|
143
|
+
text-transform: uppercase;
|
|
144
|
+
font-weight: 800;
|
|
145
|
+
font-size: 13px;
|
|
146
|
+
letter-spacing: 0.06em;
|
|
147
|
+
padding: 7px 22px;
|
|
148
|
+
border-radius: 9999px;
|
|
149
|
+
border: 2px solid #FFFFFF;
|
|
150
|
+
box-shadow: 0 3px 8px rgba(13, 71, 161, 0.35);
|
|
151
|
+
white-space: nowrap;
|
|
152
|
+
font-family: 'Quicksand', 'Open Sans', sans-serif;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.infoName {
|
|
156
|
+
background: #E3F2FD;
|
|
157
|
+
color: #0D47A1;
|
|
158
|
+
font-weight: 800;
|
|
159
|
+
font-size: 18px;
|
|
160
|
+
text-align: center;
|
|
161
|
+
padding: 8px 10px;
|
|
162
|
+
border-bottom: 1px solid #BFE0FF;
|
|
163
|
+
font-family: 'Quicksand', 'Open Sans', sans-serif;
|
|
164
|
+
margin-top: 6px;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
.infoDetailItem {
|
|
168
|
+
display: flex;
|
|
169
|
+
align-items: stretch;
|
|
170
|
+
border-top: 1px solid #BFE0FF;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
.infoDetailItem:nth-child(even) {
|
|
174
|
+
background: #F5FBFF;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.infoDetailItem:nth-child(odd) {
|
|
178
|
+
background: #FFFFFF;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.infoDetailLabel {
|
|
182
|
+
flex: 1 1 50%;
|
|
183
|
+
min-width: 0;
|
|
184
|
+
padding: 10px 14px;
|
|
185
|
+
text-align: right;
|
|
186
|
+
font-weight: 700;
|
|
187
|
+
font-size: 15px;
|
|
188
|
+
color: #1565C0;
|
|
189
|
+
word-break: break-word;
|
|
190
|
+
font-family: 'Quicksand', 'Open Sans', sans-serif;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
.infoDetailDivide {
|
|
194
|
+
width: 1px;
|
|
195
|
+
background: #BFE0FF;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.infoDetailValue {
|
|
199
|
+
flex: 1 1 50%;
|
|
200
|
+
min-width: 0;
|
|
201
|
+
padding: 10px 14px;
|
|
202
|
+
text-align: left;
|
|
203
|
+
font-weight: 700;
|
|
204
|
+
font-size: 15px;
|
|
205
|
+
color: #0D47A1;
|
|
206
|
+
word-break: break-word;
|
|
207
|
+
font-family: 'Quicksand', 'Open Sans', sans-serif;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.infoDetailValueBlank {
|
|
211
|
+
color: #90A4AE;
|
|
212
|
+
font-style: italic;
|
|
213
|
+
font-weight: 500;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* ===== Responsive ===== */
|
|
217
|
+
@media (max-width: 1400px) {
|
|
218
|
+
.infoHeader { font-size: 12px; padding: 6px 18px; top: -14px; }
|
|
219
|
+
.infoName { font-size: 16px; padding: 6px 8px; }
|
|
220
|
+
.infoDetailLabel,
|
|
221
|
+
.infoDetailValue { font-size: 13px; padding: 7px 10px; }
|
|
222
|
+
.infoContainer { gap: 14px; padding-top: 12px; }
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
@media (max-width: 1024px) {
|
|
226
|
+
.infoHeader { font-size: 11px; padding: 5px 14px; }
|
|
227
|
+
.infoName { font-size: 14px; padding: 5px 6px; }
|
|
228
|
+
.infoDetailLabel,
|
|
229
|
+
.infoDetailValue { font-size: 12px; padding: 5px 8px; }
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
@media only screen and (max-width: 768px) {
|
|
233
|
+
.questionContainer { gap: 8px; }
|
|
234
|
+
.infoHeader { font-size: 10px; padding: 4px 12px; top: -12px; }
|
|
235
|
+
.infoName { font-size: 12px; padding: 4px; }
|
|
236
|
+
.infoDetailLabel,
|
|
237
|
+
.infoDetailValue { font-size: 11px; padding: 4px 6px; }
|
|
238
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e}from"react/jsx-runtime";export function SpeakingInstructionBar({text:r}){return e("div",{className:"rounded-lg px-5 py-2.5 text-center",style:{background:"linear-gradient(180deg, #44271a 0%, #2d1a0e 100%)",boxShadow:"0 4px 8px rgba(0,0,0,0.4), inset 0 1px 0 rgba(255,255,255,0.08)",border:"2px solid #1c0e07"},children:e("span",{className:"text-sm font-bold uppercase tracking-widest text-amber-100",children:r})})}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RecordingState } from '../../types/speaking.types';
|
|
2
|
+
interface SpeakingMicButtonProps {
|
|
3
|
+
status: RecordingState['status'];
|
|
4
|
+
durationSeconds: number;
|
|
5
|
+
remainingSeconds?: number;
|
|
6
|
+
isWarning?: boolean;
|
|
7
|
+
onStartRecording: () => void;
|
|
8
|
+
onStopRecording: () => void;
|
|
9
|
+
onRetryUpload?: () => void;
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
hasExistingRecording?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function SpeakingMicButton({ status, durationSeconds: _durationSeconds, remainingSeconds, isWarning, onStartRecording, onStopRecording, onRetryUpload, disabled, hasExistingRecording, }: SpeakingMicButtonProps): import("react").JSX.Element;
|
|
14
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Mic as t,Square as o,CheckCircle as a,AlertCircle as n,RotateCcw as i}from"lucide-react";import{cn as d}from"../../../../../shared/lib/utils";function l(e){const r=Math.floor(e/60),t=e%60;return`${String(r).padStart(1,"0")}:${String(t).padStart(2,"0")}`}export function SpeakingMicButton({status:s,durationSeconds:g,remainingSeconds:c,isWarning:h=!1,onStartRecording:m,onStopRecording:b,onRetryUpload:u,disabled:p=!1,hasExistingRecording:x=!1}){const f="recording"===s,w="uploading"===s,v="uploaded"===s,N="error"===s,y=x&&!N&&!f;return r("div",{className:"flex flex-col items-center gap-0",children:[f&&e("div",{className:d("mb-2 rounded-full px-4 py-1 text-sm font-bold tabular-nums",h?"bg-red-600 text-white":"bg-red-900/80 text-white"),children:void 0!==c?l(c):""}),r("button",{type:"button",onClick:()=>{p||w||y||(N&&u?u():f?b():m())},disabled:p||w||y,"aria-label":f?"Dừng ghi âm":N?"Thử lại upload":"Bắt đầu ghi âm",className:d("relative flex h-20 w-20 items-center justify-center rounded-full","border-4 shadow-[0_6px_20px_rgba(0,0,0,0.4)]","transition-all duration-200","active:scale-95","disabled:cursor-not-allowed disabled:opacity-50",!f&&!v&&!w&&!N&&"border-orange-700 bg-gradient-to-b from-orange-400 to-orange-600 hover:from-orange-500 hover:to-orange-700",f&&!h&&"animate-pulse border-red-700 bg-gradient-to-b from-red-500 to-red-700",f&&h&&"animate-pulse border-red-900 bg-gradient-to-b from-red-600 to-red-900",w&&"border-orange-700 bg-gradient-to-b from-orange-400 to-orange-600",v&&"border-green-700 bg-gradient-to-b from-green-400 to-green-600 hover:from-green-500 hover:to-green-700",N&&"border-orange-700 bg-gradient-to-b from-orange-400 to-orange-600 hover:from-orange-500 hover:to-orange-700"),children:[f&&e(o,{className:"h-8 w-8 fill-white text-white"}),v&&e(a,{className:"h-8 w-8 text-white"}),N&&e(u?i:n,{className:"h-8 w-8 text-white"}),!f&&!v&&!N&&e(t,{className:"h-8 w-8 text-white"}),w&&e("span",{className:"absolute inset-0 flex items-center justify-center rounded-full bg-black/30",children:r("svg",{className:"h-8 w-8 animate-spin text-white",viewBox:"0 0 24 24",fill:"none","aria-hidden":"true",children:[e("circle",{className:"opacity-25",cx:"12",cy:"12",r:"10",stroke:"currentColor",strokeWidth:"4"}),e("path",{className:"opacity-75",fill:"currentColor",d:"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z"})]})})]}),e("div",{className:"mt-3 rounded-lg px-4 py-2 text-center",style:{background:"linear-gradient(180deg, #3d2008 0%, #2a1505 100%)",boxShadow:"0 3px 8px rgba(0,0,0,0.5), inset 0 1px 0 rgba(255,255,255,0.07)",border:"2px solid #1c0a02"},children:e("p",{className:"text-[11px] font-bold uppercase tracking-widest text-amber-200",children:N?u?"Thử lại upload":"Lỗi ghi âm":v?"Đã ghi âm thành công":w?"Đang tải lên...":f?"Nhấn để dừng ghi âm":x?"Câu trả lời đã được ghi nhận":"Thí sinh ấn vào biểu tượng micro để trả lời"})})]})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e}from"react/jsx-runtime";import{ChevronLeft as r,ChevronRight as t}from"lucide-react";import{cn as a}from"../../../../../shared/lib/utils";export function SpeakingNavButton({direction:o,onClick:i,disabled:s=!1}){const d="prev"===o?r:t;return e("button",{onClick:i,disabled:s,className:a("flex h-14 w-14 items-center justify-center rounded-full","border-4 border-amber-800","bg-gradient-to-b from-amber-500 to-amber-700","shadow-[0_4px_12px_rgba(0,0,0,0.4),inset_0_1px_0_rgba(255,255,255,0.2)]","transition-all duration-200","hover:from-amber-600 hover:to-amber-800","active:scale-95 active:shadow-[0_2px_6px_rgba(0,0,0,0.4)]","disabled:cursor-not-allowed disabled:opacity-40","sm:h-16 sm:w-16"),"aria-label":"prev"===o?"Câu trước":"Câu tiếp theo",children:e(d,{className:"h-7 w-7 text-white drop-shadow sm:h-8 sm:w-8",strokeWidth:2.5})})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as e,jsxs as a}from"react/jsx-runtime";import{cn as r}from"../../../../../shared/lib/utils";export function SpeakingPartBadge({partName:t,className:s}){return e("div",{className:r("relative inline-flex items-center justify-center",s),children:a("div",{className:"relative flex items-center gap-2 rounded-full border-4 border-amber-600 bg-gradient-to-b from-amber-300 via-amber-400 to-amber-600 px-6 py-2 shadow-[0_4px_12px_rgba(0,0,0,0.4),inset_0_1px_0_rgba(255,255,255,0.3)]",children:[e("span",{className:"text-sm","aria-hidden":"true",children:"🍃"}),e("span",{className:"font-bold tracking-wide text-amber-900 drop-shadow-sm",style:{fontSize:"1.1rem"},children:t}),e("span",{className:"text-sm","aria-hidden":"true",children:"🍃"})]})})}
|
package/dist/components/exams/take/components/speaking/SpeakingReadDisplayedContent.module.css
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/* ===== Read Displayed Content — Scoped styles ===== */
|
|
2
|
+
|
|
3
|
+
/* Reference text card — prominent, readable display */
|
|
4
|
+
.referenceTextCard {
|
|
5
|
+
background: rgba(255, 255, 255, 0.95);
|
|
6
|
+
border-radius: 16px;
|
|
7
|
+
padding: 32px 40px;
|
|
8
|
+
max-width: 720px;
|
|
9
|
+
width: 100%;
|
|
10
|
+
box-shadow:
|
|
11
|
+
0 4px 24px rgba(0, 0, 0, 0.12),
|
|
12
|
+
0 1px 4px rgba(0, 0, 0, 0.08);
|
|
13
|
+
border: 2px solid rgba(255, 255, 255, 0.6);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
.referenceText {
|
|
17
|
+
font-size: 1.5rem;
|
|
18
|
+
line-height: 2;
|
|
19
|
+
color: #1a1a2e;
|
|
20
|
+
font-family: 'Georgia', 'Times New Roman', serif;
|
|
21
|
+
letter-spacing: 0.02em;
|
|
22
|
+
text-align: center;
|
|
23
|
+
word-spacing: 0.05em;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* Difficulty tag badge */
|
|
27
|
+
.difficultyBadge {
|
|
28
|
+
display: inline-flex;
|
|
29
|
+
align-items: center;
|
|
30
|
+
gap: 6px;
|
|
31
|
+
padding: 4px 12px;
|
|
32
|
+
border-radius: 20px;
|
|
33
|
+
font-size: 0.75rem;
|
|
34
|
+
font-weight: 600;
|
|
35
|
+
text-transform: uppercase;
|
|
36
|
+
letter-spacing: 0.05em;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.difficultyBasicPhonics {
|
|
40
|
+
background: #dbeafe;
|
|
41
|
+
color: #1d4ed8;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.difficultyDailyRoutine {
|
|
45
|
+
background: #fef3c7;
|
|
46
|
+
color: #92400e;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.difficultyDescriptiveEmotional {
|
|
50
|
+
background: #ede9fe;
|
|
51
|
+
color: #6d28d9;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Model audio section */
|
|
55
|
+
.modelAudioSection {
|
|
56
|
+
display: flex;
|
|
57
|
+
align-items: center;
|
|
58
|
+
justify-content: center;
|
|
59
|
+
gap: 8px;
|
|
60
|
+
margin-top: 12px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.modelAudioLabel {
|
|
64
|
+
font-size: 0.85rem;
|
|
65
|
+
color: #64748b;
|
|
66
|
+
font-weight: 500;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/* Task number indicator */
|
|
70
|
+
.taskIndicator {
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: center;
|
|
73
|
+
gap: 8px;
|
|
74
|
+
color: rgba(255, 255, 255, 0.9);
|
|
75
|
+
font-weight: 700;
|
|
76
|
+
font-size: 0.9rem;
|
|
77
|
+
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
.taskDot {
|
|
81
|
+
width: 10px;
|
|
82
|
+
height: 10px;
|
|
83
|
+
border-radius: 50%;
|
|
84
|
+
background: rgba(255, 255, 255, 0.4);
|
|
85
|
+
transition: background 0.2s;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.taskDotActive {
|
|
89
|
+
background: #fbbf24;
|
|
90
|
+
box-shadow: 0 0 8px rgba(251, 191, 36, 0.5);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.taskDotCompleted {
|
|
94
|
+
background: #34d399;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* Responsive adjustments */
|
|
98
|
+
@media (max-width: 768px) {
|
|
99
|
+
.referenceTextCard {
|
|
100
|
+
padding: 20px 24px;
|
|
101
|
+
max-width: 100%;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.referenceText {
|
|
105
|
+
font-size: 1.2rem;
|
|
106
|
+
line-height: 1.8;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
@media (max-width: 480px) {
|
|
111
|
+
.referenceText {
|
|
112
|
+
font-size: 1rem;
|
|
113
|
+
line-height: 1.7;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { RecordingState } from '../../types/speaking.types';
|
|
2
|
+
interface SpeakingRecordingPreviewProps {
|
|
3
|
+
previewUrl: string;
|
|
4
|
+
uploadedKey: string | null;
|
|
5
|
+
status: RecordingState['status'];
|
|
6
|
+
onReRecord: () => void;
|
|
7
|
+
/** Called when audio playback state changes (play/pause/ended) */
|
|
8
|
+
onPlayingChange?: (isPlaying: boolean) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function SpeakingRecordingPreview({ previewUrl, uploadedKey, status, onReRecord, onPlayingChange, }: SpeakingRecordingPreviewProps): import("react").JSX.Element;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useRef as n,useState as r,useEffect as i}from"react";import{Play as a,Pause as l,RotateCcw as s,CheckCircle as d,AlertCircle as o,Loader2 as c}from"lucide-react";import{cn as m}from"../../../../../shared/lib/utils";function u(e){const t=Math.floor(e/60),n=Math.floor(e%60);return`${String(t).padStart(2,"0")}:${String(n).padStart(2,"0")}`}export function SpeakingRecordingPreview({previewUrl:p,uploadedKey:h,status:b,onReRecord:x,onPlayingChange:f}){const g=n(null),[w,v]=r(!1),[N,k]=r(0),[y,L]=r(0),E=!h&&"uploading"!==b&&"uploaded"!==b;i(()=>{const e=g.current;if(!e)return;const t=()=>{e.duration&&!isNaN(e.duration)&&isFinite(e.duration)&&L(e.duration)},n=()=>{k(e.currentTime),t()},r=()=>{v(!1),k(0),f?.(!1)};return t(),e.addEventListener("loadedmetadata",t),e.addEventListener("timeupdate",n),e.addEventListener("ended",r),()=>{e.removeEventListener("loadedmetadata",t),e.removeEventListener("timeupdate",n),e.removeEventListener("ended",r)}},[p]);const j=y>0?N/y*100:0;return t("div",{className:"mx-auto flex w-fit min-w-[320px] max-w-full items-center gap-3 rounded-full bg-white/70 backdrop-blur-md px-4 py-2.5 shadow-[0_8px_30px_rgb(0,0,0,0.12)] border border-white/60 ring-2 ring-white/40",children:[e("audio",{ref:g,src:p,preload:"metadata"}),e("button",{type:"button",onClick:()=>{const e=g.current;e&&(w?(e.pause(),v(!1),f?.(!1)):(e.play(),v(!0),f?.(!0)))},className:"flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-blue-500 text-white shadow-md transition-all hover:bg-blue-600 hover:scale-105 active:scale-95","aria-label":w?"Tạm dừng":"Nghe lại",children:w?e(l,{className:"h-4 w-4 fill-current"}):e(a,{className:"ml-0.5 h-4 w-4 fill-current"})}),e("div",{className:"flex-1 min-w-[100px] relative flex items-center",children:e("input",{type:"range",min:"0",max:y||0,step:"0.1",value:N,onChange:e=>{const t=g.current;if(!t)return;const n=parseFloat(e.target.value);t.currentTime=n,k(n)},className:m("w-full h-1.5 cursor-pointer appearance-none rounded-full bg-stone-200","[&::-webkit-slider-thumb]:appearance-none [&::-webkit-slider-thumb]:h-3.5 [&::-webkit-slider-thumb]:w-3.5","[&::-webkit-slider-thumb]:rounded-full [&::-webkit-slider-thumb]:bg-white [&::-webkit-slider-thumb]:shadow-md","[&::-webkit-slider-thumb]:border-[3px] [&::-webkit-slider-thumb]:border-blue-500","[&::-webkit-slider-thumb]:transition-transform [&::-webkit-slider-thumb]:hover:scale-125"),style:{background:`linear-gradient(to right, #3b82f6 0%, #3b82f6 ${j}%, #e5e7eb ${j}%, #e5e7eb 100%)`}})}),t("span",{className:"min-w-[72px] text-right text-[11px] tabular-nums text-stone-600 font-bold shrink-0 tracking-wide",children:[u(N)," / ",u(y)]}),e("div",{className:"w-px h-6 bg-stone-200 shrink-0 mx-1"}),t("div",{className:"shrink-0 flex items-center justify-center min-w-[70px]",children:["uploading"===b&&t("div",{className:"flex items-center gap-1.5 rounded-full bg-amber-100 px-2.5 py-1 text-[11px] font-bold text-amber-700",children:[e(c,{className:"h-3 w-3 animate-spin"}),e("span",{className:"hidden sm:inline",children:"Đang lưu"})]}),"uploaded"===b&&t("div",{className:"flex items-center gap-1.5 rounded-full bg-green-100 px-2.5 py-1 text-[11px] font-bold text-green-700",children:[e(d,{className:"h-3.5 w-3.5"}),e("span",{className:"hidden sm:inline",children:"Đã lưu"})]}),"error"===b&&t("div",{className:"flex items-center gap-1.5 rounded-full bg-red-100 px-2.5 py-1 text-[11px] font-bold text-red-700",children:[e(o,{className:"h-3.5 w-3.5"}),e("span",{className:"hidden sm:inline",children:"Lỗi"})]}),"recorded"===b&&e("div",{className:"flex items-center gap-1 text-[11px] text-stone-400 font-medium",children:e(c,{className:"h-3 w-3 animate-spin"})})]}),E&&t("button",{type:"button",onClick:x,className:"flex items-center gap-1.5 rounded-full bg-stone-100 px-3 py-1.5 text-[11px] font-bold text-stone-700 transition-all hover:bg-stone-200 hover:text-stone-900 active:bg-stone-300 disabled:cursor-not-allowed disabled:opacity-50 shrink-0 ml-1",children:[e(s,{className:"h-3.5 w-3.5"}),e("span",{className:"hidden sm:inline",children:"Ghi lại"})]})]})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function SpeakingReportErrorButton(): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{jsx as o}from"react/jsx-runtime";import{toast as e}from"../../../../../shared/lib/toast";export function SpeakingReportErrorButton(){return o("button",{type:"button",onClick:()=>{e.info("Tính năng báo cáo lỗi sẽ sớm có")},className:"flex flex-col items-center justify-center gap-0.5 rounded-lg border-2 border-yellow-600 bg-gradient-to-b from-yellow-400 to-yellow-600 px-2 py-3 shadow-md transition-all hover:from-yellow-500 hover:to-yellow-700 active:scale-95","aria-label":"Báo cáo lỗi",title:"Báo cáo lỗi",children:"BÁO CÁO LỖI".split("").map((e,t)=>o("span",{className:"text-[9px] font-extrabold leading-tight text-yellow-900",children:" "===e?" ":e},t))})}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
* SpeakingStoryImages — 2-row grid layout
|
|
3
|
+
*
|
|
4
|
+
* Goals:
|
|
5
|
+
* - Larger image frames (acceptable to scroll vertically).
|
|
6
|
+
* - 2 rows by default (e.g. 3+3, 3+2, 2+2). For ≤3 images,
|
|
7
|
+
* a single row still renders naturally via auto-fit.
|
|
8
|
+
* - Never overlap the floating audio bar at the top of the
|
|
9
|
+
* Summer Sky board, nor the mic label / mic button at the
|
|
10
|
+
* bottom — handled via fixed safe-zone padding.
|
|
11
|
+
* - Numbered frames so the story order remains obvious.
|
|
12
|
+
* ============================================================ */
|
|
13
|
+
|
|
14
|
+
/* Outer wrapper takes the full content area and reserves
|
|
15
|
+
* vertical "safe zones". Vertical scroll is allowed; horizontal
|
|
16
|
+
* is not. */
|
|
17
|
+
.part3Container {
|
|
18
|
+
height: 100%;
|
|
19
|
+
width: 100%;
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: flex-start;
|
|
22
|
+
justify-content: center;
|
|
23
|
+
position: relative;
|
|
24
|
+
/* Top: clears the floating audio pill (top:50px + ~36px tall). */
|
|
25
|
+
/* Bottom: clears mic label that sits inside the board. */
|
|
26
|
+
padding: 96px 110px 140px;
|
|
27
|
+
box-sizing: border-box;
|
|
28
|
+
overflow-x: hidden;
|
|
29
|
+
overflow-y: auto;
|
|
30
|
+
scrollbar-color: rgba(255, 255, 255, 0.8) transparent;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.part3Container::-webkit-scrollbar {
|
|
34
|
+
width: 8px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.part3Container::-webkit-scrollbar-track {
|
|
38
|
+
background: transparent;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.part3Container::-webkit-scrollbar-thumb {
|
|
42
|
+
background: rgba(255, 255, 255, 0.8);
|
|
43
|
+
border-radius: 10px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/* Grid: auto-fit so 1–6 images naturally lay out. With minmax
|
|
47
|
+
* around 320px and a max board width of ~1180px (after padding),
|
|
48
|
+
* 4 columns fit comfortably; ≥5 wraps to a second row, giving
|
|
49
|
+
* the requested 3+2 / 3+3 style without manual breakpoints. */
|
|
50
|
+
.storyGrid {
|
|
51
|
+
width: 100%;
|
|
52
|
+
display: grid;
|
|
53
|
+
gap: 24px;
|
|
54
|
+
justify-items: center;
|
|
55
|
+
align-content: start;
|
|
56
|
+
/* Default: 3 columns (covers 3, 5→3+2, 6→3+3). Renderer overrides
|
|
57
|
+
* via data-count for cases where a different split looks better. */
|
|
58
|
+
grid-template-columns: repeat(3, minmax(0, 1fr));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.storyGrid[data-count="1"] {
|
|
62
|
+
grid-template-columns: minmax(0, 520px);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.storyGrid[data-count="2"],
|
|
66
|
+
.storyGrid[data-count="4"] {
|
|
67
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/* Each frame keeps a fixed landscape aspect ratio so all images
|
|
71
|
+
* render at the same size regardless of source dimensions. */
|
|
72
|
+
.storyFrame {
|
|
73
|
+
position: relative;
|
|
74
|
+
width: 100%;
|
|
75
|
+
max-width: 380px;
|
|
76
|
+
aspect-ratio: 4 / 3;
|
|
77
|
+
display: flex;
|
|
78
|
+
align-items: center;
|
|
79
|
+
justify-content: center;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/* The clickable image surface — themed border + shadow that
|
|
83
|
+
* matches Summer Sky cards. */
|
|
84
|
+
.imageBorder {
|
|
85
|
+
position: relative;
|
|
86
|
+
width: 100%;
|
|
87
|
+
height: 100%;
|
|
88
|
+
border-radius: 16px;
|
|
89
|
+
/* Soft fill behind contained images so any letterboxing blends in. */
|
|
90
|
+
background: #f4f8ff;
|
|
91
|
+
border: 4px solid #ffffff;
|
|
92
|
+
box-shadow:
|
|
93
|
+
0 6px 14px rgba(11, 76, 142, 0.22),
|
|
94
|
+
0 0 0 2px rgba(74, 144, 217, 0.25);
|
|
95
|
+
overflow: hidden;
|
|
96
|
+
cursor: zoom-in;
|
|
97
|
+
transition: transform 0.18s ease, box-shadow 0.18s ease;
|
|
98
|
+
padding: 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
.imageBorder:hover {
|
|
102
|
+
transform: translateY(-2px) scale(1.02);
|
|
103
|
+
box-shadow:
|
|
104
|
+
0 10px 22px rgba(11, 76, 142, 0.32),
|
|
105
|
+
0 0 0 2px rgba(74, 144, 217, 0.45);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.imageBorder img {
|
|
109
|
+
width: 100%;
|
|
110
|
+
height: 100%;
|
|
111
|
+
/* `contain` ensures the full story illustration is always visible —
|
|
112
|
+
* never crop characters/objects out at the edges. Minor letterboxing
|
|
113
|
+
* is acceptable and blends with the background fill. */
|
|
114
|
+
object-fit: contain;
|
|
115
|
+
object-position: center;
|
|
116
|
+
display: block;
|
|
117
|
+
pointer-events: none;
|
|
118
|
+
user-select: none;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/* Numbered sequence badge (top-left). */
|
|
122
|
+
.storyNumber {
|
|
123
|
+
position: absolute;
|
|
124
|
+
top: -12px;
|
|
125
|
+
left: -12px;
|
|
126
|
+
width: 40px;
|
|
127
|
+
height: 40px;
|
|
128
|
+
border-radius: 50%;
|
|
129
|
+
background: linear-gradient(180deg, #ffe341 0%, #f7ab16 100%);
|
|
130
|
+
color: #0b4c8e;
|
|
131
|
+
font-family: 'Nunito', 'Open Sans', system-ui, sans-serif;
|
|
132
|
+
font-weight: 900;
|
|
133
|
+
font-size: 20px;
|
|
134
|
+
line-height: 1;
|
|
135
|
+
display: flex;
|
|
136
|
+
align-items: center;
|
|
137
|
+
justify-content: center;
|
|
138
|
+
border: 3px solid #ffffff;
|
|
139
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.25);
|
|
140
|
+
z-index: 2;
|
|
141
|
+
pointer-events: none;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* Empty state */
|
|
145
|
+
.emptyState {
|
|
146
|
+
display: flex;
|
|
147
|
+
height: 100%;
|
|
148
|
+
width: 100%;
|
|
149
|
+
align-items: center;
|
|
150
|
+
justify-content: center;
|
|
151
|
+
color: #cbd5e1;
|
|
152
|
+
font-size: 14px;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* ===== Lightbox / enlarged preview ===== */
|
|
156
|
+
.lightboxBackdrop {
|
|
157
|
+
position: fixed;
|
|
158
|
+
inset: 0;
|
|
159
|
+
background: rgba(0, 0, 0, 0.78);
|
|
160
|
+
z-index: 1000;
|
|
161
|
+
display: flex;
|
|
162
|
+
align-items: center;
|
|
163
|
+
justify-content: center;
|
|
164
|
+
padding: 40px;
|
|
165
|
+
animation: storyFadeIn 0.18s ease-out;
|
|
166
|
+
cursor: zoom-out;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.lightboxImage {
|
|
170
|
+
max-width: min(90vw, 1100px);
|
|
171
|
+
max-height: 90vh;
|
|
172
|
+
border-radius: 18px;
|
|
173
|
+
border: 6px solid #ffffff;
|
|
174
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.55);
|
|
175
|
+
object-fit: contain;
|
|
176
|
+
background: #fff;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.lightboxClose {
|
|
180
|
+
position: fixed;
|
|
181
|
+
top: 24px;
|
|
182
|
+
right: 32px;
|
|
183
|
+
width: 44px;
|
|
184
|
+
height: 44px;
|
|
185
|
+
border-radius: 50%;
|
|
186
|
+
background: #ffffff;
|
|
187
|
+
color: #0b4c8e;
|
|
188
|
+
font-size: 24px;
|
|
189
|
+
font-weight: 900;
|
|
190
|
+
border: none;
|
|
191
|
+
cursor: pointer;
|
|
192
|
+
display: flex;
|
|
193
|
+
align-items: center;
|
|
194
|
+
justify-content: center;
|
|
195
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.35);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
@keyframes storyFadeIn {
|
|
199
|
+
from { opacity: 0; }
|
|
200
|
+
to { opacity: 1; }
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/* ===== Responsive tightening ===== */
|
|
204
|
+
@media (max-width: 1530px) {
|
|
205
|
+
.part3Container {
|
|
206
|
+
padding: 90px 90px 130px;
|
|
207
|
+
}
|
|
208
|
+
.storyGrid {
|
|
209
|
+
gap: 20px;
|
|
210
|
+
}
|
|
211
|
+
.storyFrame {
|
|
212
|
+
max-width: 340px;
|
|
213
|
+
}
|
|
214
|
+
.storyNumber {
|
|
215
|
+
width: 36px;
|
|
216
|
+
height: 36px;
|
|
217
|
+
font-size: 18px;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
@media (max-width: 1280px) {
|
|
222
|
+
.part3Container {
|
|
223
|
+
padding: 84px 70px 120px;
|
|
224
|
+
}
|
|
225
|
+
.storyGrid {
|
|
226
|
+
gap: 16px;
|
|
227
|
+
}
|
|
228
|
+
.storyFrame {
|
|
229
|
+
max-width: 300px;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* speakingVideoMap — Maps speaking question types to their AI teacher intro video URLs.
|
|
3
|
+
*
|
|
4
|
+
* These videos play muted in the floating AI teacher widget within the speaking exam layout,
|
|
5
|
+
* corresponding to the current question type being answered.
|
|
6
|
+
*/
|
|
7
|
+
import type { QuestionType } from '../../utils/question-transformers';
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the AI teacher video URL for the given question type.
|
|
10
|
+
* Returns the type-specific video or undefined if no video exists.
|
|
11
|
+
*/
|
|
12
|
+
export declare function getTeacherVideoUrl(questionType: QuestionType): string | undefined;
|
|
13
|
+
export declare function shouldWaitForTeacherVideo(questionType: QuestionType): boolean;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e={READ_DISPLAYED_CONTENT:"/speaking/video/read_displayed_content.webm",SPEAKING_DESCRIBE_IMAGE:"/speaking/video/speaking_describe_image.webm",SPONTANEOUS_QA:"/speaking/video/spontaneous_qa.webm"};export function getTeacherVideoUrl(n){return e[n]}const n=new Set(["READ_DISPLAYED_CONTENT","SPEAKING_DESCRIBE_IMAGE","SPONTANEOUS_QA"]);export function shouldWaitForTeacherVideo(e){return n.has(e)}
|