@tinyweb_dev/oe-exam-sdk 0.1.1 → 0.1.2
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/dist/api/exam-create.js +1 -179
- package/dist/api/exam-create.types.js +1 -1
- package/dist/api/exam-questions.js +1 -98
- package/dist/api/exam-questions.types.js +1 -1
- package/dist/api/exam-taking.js +1 -73
- package/dist/api/exam-taking.types.js +1 -1
- package/dist/api/index.js +1 -6
- package/dist/api/result-review.js +1 -50
- package/dist/api/result-review.types.js +1 -1
- package/dist/components/CueCardEditor.js +1 -58
- package/dist/components/common/ResolvedImage.js +1 -15
- package/dist/components/exams/CreateExamInfoForm.js +1 -35
- package/dist/components/exams/CreateExamPageContainer.js +1 -103
- package/dist/components/exams/ExamCreator.js +1 -89
- package/dist/components/exams/ExamPartTabs.js +1 -40
- package/dist/components/exams/ExamQuestionGrid.js +1 -18
- package/dist/components/exams/ExamQuestionsPageContainer.js +1 -90
- package/dist/components/exams/PartAudioUploader.js +1 -9
- package/dist/components/exams/TemplateSelector.js +1 -66
- package/dist/components/exams/api.js +1 -3
- package/dist/components/exams/create-exam.validation.js +1 -18
- package/dist/components/exams/exam-template.utils.js +1 -18
- package/dist/components/exams/index.js +1 -10
- package/dist/components/exams/take/ExamTakingPageContainer.js +1 -153
- package/dist/components/exams/take/GenericQuestionRenderer.js +1 -14
- package/dist/components/exams/take/exam-taking.utils.js +1 -88
- package/dist/components/exams/take/hooks/useSpeakingRecorder.js +1 -24
- package/dist/components/exams/take/index.js +1 -2
- package/dist/components/exams/take/types.js +1 -1
- package/dist/components/index.js +1 -42
- package/dist/components/questions/_shared/config/question-types.config.js +1 -333
- package/dist/components/questions/_shared/constants/color-palette.js +1 -21
- package/dist/components/questions/_shared/hooks/useAnswerGroupQuestionGroup.js +1 -105
- package/dist/components/questions/_shared/hooks/useBasicQuestionGroup.js +1 -222
- package/dist/components/questions/_shared/hooks/useCompareImagesGroup.js +1 -140
- package/dist/components/questions/_shared/hooks/useImageQuestionGroup.js +1 -123
- package/dist/components/questions/_shared/hooks/useInfoExchangeGroup.js +1 -50
- package/dist/components/questions/_shared/hooks/useQuestionFormState.js +1 -54
- package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +1 -123
- package/dist/components/questions/_shared/hooks/useQuestionListGroup.js +1 -121
- package/dist/components/questions/_shared/hooks/useQuestionOnChange.js +1 -70
- package/dist/components/questions/_shared/hooks/useSceneQuestionGroup.js +1 -194
- package/dist/components/questions/_shared/index.js +1 -7
- package/dist/components/questions/_shared/types/answer-the-question.type.js +1 -1
- package/dist/components/questions/_shared/types/arrange-letters-into-words.type.js +1 -1
- package/dist/components/questions/_shared/types/choose-correct-adjective.type.js +1 -3
- package/dist/components/questions/_shared/types/color-region.config.js +1 -1
- package/dist/components/questions/_shared/types/fill-in-blank.type.js +1 -1
- package/dist/components/questions/_shared/types/fill-missing-words-in-grid.type.js +1 -1
- package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +1 -1
- package/dist/components/questions/_shared/types/index.js +1 -12
- package/dist/components/questions/_shared/types/label-the-picture.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-choose-from-answer-group.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-choose-objects-in-scene.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-drag-objects-into-scene.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-answer.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-compare-images.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-image-group.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-info-exchange.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-odd-one-out.type.js +1 -3
- package/dist/components/questions/_shared/types/listen-and-speak-question-list.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-with-story-images.type.js +1 -3
- package/dist/components/questions/_shared/types/listen-and-tick-answer.type.js +1 -1
- package/dist/components/questions/_shared/types/listening-drawline.type.js +1 -1
- package/dist/components/questions/_shared/types/look-and-choose-answer.type.js +1 -1
- package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +1 -1
- package/dist/components/questions/_shared/types/match-by-writing-answer.type.js +1 -1
- package/dist/components/questions/_shared/types/multiple-choice.type.js +1 -1
- package/dist/components/questions/_shared/types/question-group.type.js +1 -11
- package/dist/components/questions/_shared/types/read-and-color-objects.type.js +1 -1
- package/dist/components/questions/_shared/types/read-displayed-content.type.js +1 -1
- package/dist/components/questions/_shared/types/read-passage-and-answer-questions.type.js +1 -1
- package/dist/components/questions/_shared/types/speaking-describe-image.type.js +1 -1
- package/dist/components/questions/_shared/types/spontaneous-qa.type.js +1 -1
- package/dist/components/questions/_shared/types/word-fill-paragraph-with-options.type.js +1 -1
- package/dist/components/questions/_shared/types/word-fill-paragraph.type.js +1 -1
- package/dist/components/questions/_shared/types/word-fill-structured-form.type.js +1 -1
- package/dist/components/questions/_shared/types/word-ordering.type.js +1 -1
- package/dist/components/questions/_shared/types/write-a-short-letter.type.js +1 -1
- package/dist/components/questions/_shared/types/write-correct-verb-form.type.js +1 -3
- package/dist/components/questions/_shared/types/write-sentences.type.js +1 -1
- package/dist/components/questions/_shared/types/write-short-paragraph.type.js +1 -1
- package/dist/components/questions/_shared/utils/answer-builder.js +1 -79
- package/dist/components/questions/_shared/utils/createColorRegionRegistration.js +1 -39
- package/dist/components/questions/_shared/utils/id-generator.js +1 -3
- package/dist/components/questions/_shared/utils/question-validation.js +1 -21
- package/dist/components/questions/components/ColorNode.js +1 -85
- package/dist/components/questions/components/ColorRegionClient.js +1 -545
- package/dist/components/questions/components/ColorRegionCreator.js +1 -420
- package/dist/components/questions/components/ColorRegionNode.js +1 -59
- package/dist/components/questions/components/CreatorGuide.js +1 -103
- package/dist/components/questions/components/NameNode.js +1 -26
- package/dist/components/questions/components/QuestionBankOption.js +1 -46
- package/dist/components/questions/components/QuestionBasicInfoForm.js +1 -9
- package/dist/components/questions/components/QuestionCommonFields.js +1 -33
- package/dist/components/questions/components/QuestionSearchDropdown.js +1 -258
- package/dist/components/questions/components/QuestionTypeSelector.js +1 -32
- package/dist/components/questions/components/QuestionTypeSpecificForms.js +1 -6
- package/dist/components/questions/components/RegionNode.js +1 -94
- package/dist/components/questions/components/index.js +1 -14
- package/dist/components/questions/creator/QuestionCreator.js +1 -90
- package/dist/components/questions/creator/QuestionGroupCreator.js +1 -119
- package/dist/components/questions/creator/dedicated-component-router.js +1 -79
- package/dist/components/questions/creator/index.js +1 -3
- package/dist/components/questions/creator/question-type-registry.js +1 -80
- package/dist/components/questions/groups/ArticlesGroupCard.js +1 -121
- package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -50
- package/dist/components/questions/groups/DocumentGroupCard.js +1 -114
- package/dist/components/questions/groups/MatchAnswerGroupCard.js +1 -25
- package/dist/components/questions/index.js +1 -10
- package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +1 -37
- package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +1 -351
- package/dist/components/questions/types/answer-the-question/register.js +1 -67
- package/dist/components/questions/types/answer-the-question/transform.js +1 -36
- package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +1 -141
- package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +1 -118
- package/dist/components/questions/types/arrange-letters-into-words/index.js +1 -2
- package/dist/components/questions/types/arrange-letters-into-words/register.js +1 -4
- package/dist/components/questions/types/arrange-letters-into-words/transform.js +1 -24
- package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -64
- package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +1 -169
- package/dist/components/questions/types/choose-correct-adjective/index.js +1 -2
- package/dist/components/questions/types/choose-correct-adjective/register.js +1 -55
- package/dist/components/questions/types/choose-correct-adjective/transform.js +1 -16
- package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +1 -131
- package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +1 -581
- package/dist/components/questions/types/choose-the-correct-answer/index.js +1 -2
- package/dist/components/questions/types/choose-the-correct-answer/register.js +1 -101
- package/dist/components/questions/types/choose-the-correct-answer/transform.js +1 -43
- package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +1 -275
- package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +1 -448
- package/dist/components/questions/types/fill-in-blank/index.js +1 -2
- package/dist/components/questions/types/fill-in-blank/register.js +1 -138
- package/dist/components/questions/types/fill-in-blank/transform.js +1 -59
- package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +1 -117
- package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +1 -671
- package/dist/components/questions/types/fill-missing-words-in-grid/index.js +1 -2
- package/dist/components/questions/types/fill-missing-words-in-grid/register.js +1 -37
- package/dist/components/questions/types/fill-missing-words-in-grid/transform.js +1 -15
- package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +1 -262
- package/dist/components/questions/types/gn-speaking-interview/register.js +1 -121
- package/dist/components/questions/types/gn-speaking-interview/transform.js +1 -48
- package/dist/components/questions/types/image-object-matching/ImageObjectMatchingClient.js +1 -16
- package/dist/components/questions/types/image-object-matching/ImageObjectMatchingCreator.js +1 -27
- package/dist/components/questions/types/image-object-matching/index.js +1 -2
- package/dist/components/questions/types/image-object-matching/register.js +1 -3
- package/dist/components/questions/types/image-object-matching/transform.js +1 -2
- package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +1 -102
- package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +1 -344
- package/dist/components/questions/types/label-the-picture/index.js +1 -2
- package/dist/components/questions/types/label-the-picture/register.js +1 -54
- package/dist/components/questions/types/label-the-picture/transform.js +1 -28
- package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +1 -78
- package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +1 -232
- package/dist/components/questions/types/listen-and-choose-from-answer-group/index.js +1 -2
- package/dist/components/questions/types/listen-and-choose-from-answer-group/register.js +1 -82
- package/dist/components/questions/types/listen-and-choose-from-answer-group/transform.js +1 -27
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +1 -70
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +1 -386
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/index.js +1 -2
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/register.js +1 -100
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/transform.js +1 -33
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +1 -106
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +1 -457
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/index.js +1 -2
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/register.js +1 -110
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/transform.js +1 -35
- package/dist/components/questions/types/listen-and-speak-answer/ArrowIndicator.js +1 -8
- package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -22
- package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +1 -291
- package/dist/components/questions/types/listen-and-speak-answer/index.js +1 -2
- package/dist/components/questions/types/listen-and-speak-answer/register.js +1 -84
- package/dist/components/questions/types/listen-and-speak-answer/transform.js +1 -27
- package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -32
- package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +1 -295
- package/dist/components/questions/types/listen-and-speak-compare-images/register.js +1 -79
- package/dist/components/questions/types/listen-and-speak-compare-images/transform.js +1 -27
- package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -34
- package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +1 -285
- package/dist/components/questions/types/listen-and-speak-image-group/register.js +1 -81
- package/dist/components/questions/types/listen-and-speak-image-group/transform.js +1 -26
- package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -38
- package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +1 -385
- package/dist/components/questions/types/listen-and-speak-info-exchange/register.js +1 -87
- package/dist/components/questions/types/listen-and-speak-info-exchange/transform.js +1 -32
- package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -26
- package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +1 -247
- package/dist/components/questions/types/listen-and-speak-odd-one-out/register.js +1 -59
- package/dist/components/questions/types/listen-and-speak-odd-one-out/transform.js +1 -18
- package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -26
- package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +1 -256
- package/dist/components/questions/types/listen-and-speak-question-list/register.js +1 -81
- package/dist/components/questions/types/listen-and-speak-question-list/transform.js +1 -26
- package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -26
- package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +1 -230
- package/dist/components/questions/types/listen-and-speak-with-story-images/register.js +1 -56
- package/dist/components/questions/types/listen-and-speak-with-story-images/transform.js +1 -16
- package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +1 -72
- package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +1 -230
- package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +1 -1
- package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +1 -66
- package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +1 -42
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +1 -96
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +1 -238
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +1 -2
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +1 -76
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +1 -19
- package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +1 -134
- package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +1 -617
- package/dist/components/questions/types/look-picture-fill-word-hint/index.js +1 -2
- package/dist/components/questions/types/look-picture-fill-word-hint/register.js +1 -82
- package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +1 -29
- package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +1 -38
- package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +1 -359
- package/dist/components/questions/types/match-by-writing-answer/index.js +1 -3
- package/dist/components/questions/types/match-by-writing-answer/register.js +1 -58
- package/dist/components/questions/types/match-by-writing-answer/transform.js +1 -61
- package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsClient.js +1 -16
- package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsCreator.js +1 -23
- package/dist/components/questions/types/read-and-color-objects/index.js +1 -2
- package/dist/components/questions/types/read-and-color-objects/register.js +1 -3
- package/dist/components/questions/types/read-and-color-objects/transform.js +1 -36
- package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +1 -125
- package/dist/components/questions/types/read-displayed-content/register.js +1 -64
- package/dist/components/questions/types/read-displayed-content/transform.js +1 -12
- package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -64
- package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +1 -511
- package/dist/components/questions/types/read-passage-and-answer-questions/index.js +1 -2
- package/dist/components/questions/types/read-passage-and-answer-questions/register.js +1 -58
- package/dist/components/questions/types/read-passage-and-answer-questions/transform.js +1 -28
- package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +1 -373
- package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +1 -41
- package/dist/components/questions/types/speaking-conversation/register.js +1 -110
- package/dist/components/questions/types/speaking-conversation/transform.js +1 -34
- package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +1 -74
- package/dist/components/questions/types/speaking-cue-card/register.js +1 -52
- package/dist/components/questions/types/speaking-cue-card/transform.js +1 -12
- package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -10
- package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +1 -161
- package/dist/components/questions/types/speaking-describe-image/index.js +1 -2
- package/dist/components/questions/types/speaking-describe-image/register.js +1 -69
- package/dist/components/questions/types/speaking-describe-image/transform.js +1 -16
- package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +1 -234
- package/dist/components/questions/types/spontaneous-qa/register.js +1 -93
- package/dist/components/questions/types/spontaneous-qa/transform.js +1 -34
- package/dist/components/questions/types/true-false/TrueFalseClient.js +1 -75
- package/dist/components/questions/types/true-false/TrueFalseCreator.js +1 -244
- package/dist/components/questions/types/true-false/index.js +1 -2
- package/dist/components/questions/types/true-false/register.js +1 -77
- package/dist/components/questions/types/true-false/transform.js +1 -32
- package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +1 -233
- package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +1 -631
- package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphWrapper.js +1 -28
- package/dist/components/questions/types/word-fill-paragraph/index.js +1 -2
- package/dist/components/questions/types/word-fill-paragraph/register.js +1 -136
- package/dist/components/questions/types/word-fill-paragraph/transform.js +1 -56
- package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -138
- package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +1 -261
- package/dist/components/questions/types/word-fill-structured-form/index.js +1 -2
- package/dist/components/questions/types/word-fill-structured-form/register.js +1 -67
- package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -40
- package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -84
- package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +1 -192
- package/dist/components/questions/types/word-ordering/index.js +1 -2
- package/dist/components/questions/types/word-ordering/register.js +1 -46
- package/dist/components/questions/types/word-ordering/transform.js +1 -24
- package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +1 -98
- package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +1 -320
- package/dist/components/questions/types/write-a-short-letter/index.js +1 -2
- package/dist/components/questions/types/write-a-short-letter/register.js +1 -68
- package/dist/components/questions/types/write-a-short-letter/transform.js +1 -25
- package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +1 -61
- package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +1 -196
- package/dist/components/questions/types/write-correct-verb-form/index.js +1 -2
- package/dist/components/questions/types/write-correct-verb-form/register.js +1 -62
- package/dist/components/questions/types/write-correct-verb-form/transform.js +1 -17
- package/dist/components/questions/types/write-sentences/WriteSentencesClient.js +1 -22
- package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +1 -174
- package/dist/components/questions/types/write-sentences/index.js +1 -2
- package/dist/components/questions/types/write-sentences/register.js +1 -58
- package/dist/components/questions/types/write-sentences/transform.js +1 -28
- package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -96
- package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +1 -161
- package/dist/components/questions/types/write-short-paragraph/index.js +1 -2
- package/dist/components/questions/types/write-short-paragraph/register.js +1 -4
- package/dist/components/questions/types/write-short-paragraph/transform.js +1 -21
- package/dist/components/questions/viewer/QuestionGroupViewer.js +1 -36
- package/dist/components/questions/viewer/QuestionViewer.js +1 -446
- package/dist/components/questions/viewer/index.js +1 -2
- package/dist/components/results/ResultReviewPageContainer.js +1 -98
- package/dist/components/results/ReviewQuestionRenderer.js +1 -72
- package/dist/components/results/index.js +1 -2
- package/dist/components/results/result-review.utils.js +1 -6
- package/dist/components/results/review-data.js +1 -150
- package/dist/components/results/review-renderer.utils.js +1 -97
- package/dist/components/results/review-types.js +1 -1
- package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +1 -138
- package/dist/components/themes/cambridge-yle/CambridgeYleBottomNav.js +1 -12
- package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.js +1 -18
- package/dist/components/themes/cambridge-yle/CambridgeYleExampleText.js +1 -11
- package/dist/components/themes/cambridge-yle/CambridgeYleHeader.js +1 -11
- package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.js +1 -9
- package/dist/components/themes/cambridge-yle/CambridgeYleNavButton.js +1 -13
- package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.js +1 -8
- package/dist/components/themes/cambridge-yle/CambridgeYleProgressBar.js +1 -6
- package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCard.js +1 -5
- package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCardFullWidth.js +1 -9
- package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.js +1 -41
- package/dist/components/themes/cambridge-yle/CambridgeYleTimer.js +1 -25
- package/dist/components/themes/cambridge-yle/index.js +1 -13
- package/dist/components/themes/english-certification/EcAnswerTheQuestion.js +1 -24
- package/dist/components/themes/english-certification/EcMatchByWritingAnswer.js +1 -88
- package/dist/components/themes/english-certification/EcWordFillParagraph.js +1 -55
- package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -156
- package/dist/components/themes/english-certification/EcWritingTask.js +1 -34
- package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +1 -114
- package/dist/components/themes/english-certification/EnglishCertificationBrand.js +1 -11
- package/dist/components/themes/english-certification/EnglishCertificationExamLayout.js +1 -21
- package/dist/components/themes/english-certification/EnglishCertificationFlagButton.js +1 -15
- package/dist/components/themes/english-certification/EnglishCertificationFooterBar.js +1 -19
- package/dist/components/themes/english-certification/EnglishCertificationGroupedQuestion.js +1 -148
- package/dist/components/themes/english-certification/EnglishCertificationHeader.js +1 -31
- package/dist/components/themes/english-certification/EnglishCertificationNavigationPanel.js +1 -71
- package/dist/components/themes/english-certification/EnglishCertificationOptionSelector.js +1 -33
- package/dist/components/themes/english-certification/EnglishCertificationPassagePanel.js +1 -16
- package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.js +1 -22
- package/dist/components/themes/english-certification/EnglishCertificationQuestionItem.js +1 -37
- package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +1 -72
- package/dist/components/themes/english-certification/EnglishCertificationReadingSection.js +1 -90
- package/dist/components/themes/english-certification/EnglishCertificationSingleQuestion.js +1 -18
- package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js +1 -29
- package/dist/components/themes/english-certification/english-certification-theme.js +1 -58
- package/dist/components/themes/english-certification/grouped-question-utils.js +1 -218
- package/dist/components/themes/english-certification/index.js +1 -21
- package/dist/components/themes/index.js +1 -6
- package/dist/components/themes/summer-sky/SummerSkyAudioPlayer.js +1 -170
- package/dist/components/themes/summer-sky/SummerSkyLayout.js +1 -221
- package/dist/components/themes/summer-sky/SummerSkySubmitModal.js +1 -18
- package/dist/components/themes/summer-sky/SummerSkyWaitingRoom.js +1 -194
- package/dist/components/themes/types.js +1 -1
- package/dist/components/ui/alert-dialog.js +1 -26
- package/dist/components/ui/alert.js +1 -22
- package/dist/components/ui/avatar.js +1 -12
- package/dist/components/ui/badge.js +1 -38
- package/dist/components/ui/button.js +1 -35
- package/dist/components/ui/calendar.js +1 -76
- package/dist/components/ui/card.js +1 -16
- package/dist/components/ui/carousel.js +1 -91
- package/dist/components/ui/checkbox.js +1 -9
- package/dist/components/ui/collapsible.js +1 -13
- package/dist/components/ui/command.js +1 -34
- package/dist/components/ui/confirm-dialog.js +1 -49
- package/dist/components/ui/dialog.js +1 -23
- package/dist/components/ui/drawer.js +1 -35
- package/dist/components/ui/dropdown-menu.js +1 -33
- package/dist/components/ui/file-upload.js +1 -155
- package/dist/components/ui/form.js +1 -62
- package/dist/components/ui/hover-card.js +1 -10
- package/dist/components/ui/image-preview.js +1 -29
- package/dist/components/ui/input.js +1 -8
- package/dist/components/ui/label.js +1 -9
- package/dist/components/ui/language-switcher.js +1 -15
- package/dist/components/ui/multi-select.js +1 -90
- package/dist/components/ui/points-input.js +1 -33
- package/dist/components/ui/popover.js +1 -26
- package/dist/components/ui/progress.js +1 -16
- package/dist/components/ui/radio-group.js +1 -12
- package/dist/components/ui/scroll-area.js +1 -11
- package/dist/components/ui/select.js +1 -38
- package/dist/components/ui/separator.js +1 -8
- package/dist/components/ui/sheet.js +1 -40
- package/dist/components/ui/single-select.js +1 -76
- package/dist/components/ui/skeleton.js +1 -6
- package/dist/components/ui/switch.js +1 -8
- package/dist/components/ui/table.js +1 -20
- package/dist/components/ui/tabs.js +1 -17
- package/dist/components/ui/textarea.js +1 -8
- package/dist/components/ui/tooltip.js +1 -11
- package/dist/index.js +1 -19
- package/dist/mcp/config.js +1 -32
- package/dist/mcp/http-client.js +1 -48
- package/dist/mcp/index.js +1 -29
- package/dist/mcp/tools/_helpers.js +1 -25
- package/dist/mcp/tools/exams.js +1 -439
- package/dist/mcp/tools/index.js +1 -6
- package/dist/mcp/tools/results.js +1 -19
- package/dist/shared/constants/ApiConstant.js +1 -276
- package/dist/shared/constants/QueryKeyConstant.js +1 -112
- package/dist/shared/constants/exam-level.enum.js +1 -41
- package/dist/shared/constants/landings/placement-test.constants.js +1 -431
- package/dist/shared/constants/question-skills.js +1 -267
- package/dist/shared/constants/routes.js +1 -81
- package/dist/shared/index.js +1 -7
- package/dist/shared/lib/hooks/index.js +1 -10
- package/dist/shared/lib/hooks/useAlertDialog.js +1 -57
- package/dist/shared/lib/hooks/useAudioPlayCounter.js +1 -30
- package/dist/shared/lib/hooks/useAudioPreview.js +1 -81
- package/dist/shared/lib/hooks/useAudioUpload.js +1 -24
- package/dist/shared/lib/hooks/useConfirmDialog.js +1 -89
- package/dist/shared/lib/hooks/useDebouncedCallback.js +1 -32
- package/dist/shared/lib/hooks/useDelayedAutoPlay.js +1 -12
- package/dist/shared/lib/hooks/useFileUpload.js +1 -24
- package/dist/shared/lib/hooks/useImageUrl.js +1 -5
- package/dist/shared/lib/hooks/usePresignedFileUrl.js +1 -10
- package/dist/shared/lib/hooks/useReactHookForm.js +1 -31
- package/dist/shared/lib/hooks/useToast.js +1 -62
- package/dist/shared/lib/i18n/index.js +1 -54
- package/dist/shared/lib/i18n/messages/en/admin.js +1 -2104
- package/dist/shared/lib/i18n/messages/en/auth.js +1 -86
- package/dist/shared/lib/i18n/messages/en/common.js +1 -140
- package/dist/shared/lib/i18n/messages/en/file-upload.js +1 -18
- package/dist/shared/lib/i18n/messages/en/student.js +1 -133
- package/dist/shared/lib/i18n/messages/en/terms.js +1 -37
- package/dist/shared/lib/i18n/messages/en.js +1 -15
- package/dist/shared/lib/i18n/messages/vi/admin.js +1 -2104
- package/dist/shared/lib/i18n/messages/vi/auth.js +1 -86
- package/dist/shared/lib/i18n/messages/vi/common.js +1 -140
- package/dist/shared/lib/i18n/messages/vi/file-upload.js +1 -18
- package/dist/shared/lib/i18n/messages/vi/student.js +1 -133
- package/dist/shared/lib/i18n/messages/vi/terms.js +1 -37
- package/dist/shared/lib/i18n/messages/vi.js +1 -15
- package/dist/shared/lib/i18n/types.js +1 -1
- package/dist/shared/lib/stores/examQuestionStore.js +1 -212
- package/dist/shared/lib/stores/localeStore.js +1 -10
- package/dist/shared/lib/utils/public-page.js +1 -44
- package/dist/shared/lib/utils/question-transform-types.js +1 -1
- package/dist/shared/lib/utils/question-transform.js +1 -174
- package/dist/shared/lib/utils.js +1 -168
- package/dist/shared/lib/validation/choose-correct-answer.validation.js +1 -111
- package/dist/shared/lib/validation/label-the-picture.validation.js +1 -19
- package/dist/shared/lib/validation/listen-and-choose-from-answer-group.validation.js +1 -46
- package/dist/shared/lib/validation/listen-and-choose-objects-in-scene.validation.js +1 -21
- package/dist/shared/lib/validation/listen-and-drag-objects-into-scene.validation.js +1 -34
- package/dist/shared/lib/validation/listen-and-speak-answer.validation.js +1 -40
- package/dist/shared/lib/validation/listen-and-speak-compare-images.validation.js +1 -45
- package/dist/shared/lib/validation/listen-and-speak-image-group.validation.js +1 -19
- package/dist/shared/lib/validation/listen-and-speak-info-exchange.validation.js +1 -39
- package/dist/shared/lib/validation/listen-and-speak-odd-one-out.validation.js +1 -32
- package/dist/shared/lib/validation/listen-and-speak-question-list.validation.js +1 -19
- package/dist/shared/lib/validation/listen-and-speak-with-story-images.validation.js +1 -28
- package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +1 -12
- package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +1 -47
- package/dist/shared/lib/validation/read-and-color-objects.validation.js +1 -29
- package/dist/shared/lib/validation/read-displayed-content.validation.js +1 -28
- package/dist/shared/lib/validation/speaking-conversation.validation.js +1 -75
- package/dist/shared/lib/validation/speaking-describe-image.validation.js +1 -31
- package/dist/shared/lib/validation/spontaneous-qa.validation.js +1 -48
- package/dist/shared/lib/validation/word-fill-paragraph-default.validation.js +1 -38
- package/dist/shared/lib/validation/word-fill-paragraph-with-options.validation.js +1 -39
- package/dist/shared/lib/validation/word-fill-paragraph.validation.js +1 -43
- package/dist/shared/lib/validation/word-ordering.validation.js +1 -33
- package/dist/shared/types/branch.types.js +1 -6
- package/dist/shared/types/common.types.js +1 -50
- package/dist/shared/types/entities/exam-schedule.types.js +1 -5
- package/dist/shared/types/entities/exam.types.js +1 -1
- package/dist/shared/types/entities/question.types.js +1 -4
- package/dist/shared/types/entities/result.types.js +1 -4
- package/dist/shared/types/entities/user.types.js +1 -4
- package/dist/shared/types/exam-taking.types.js +1 -2
- package/dist/shared/types/history.types.js +1 -34
- package/dist/shared/types/index.js +1 -1
- package/dist/shared/types/questions/choose-correct-adjective.js +1 -4
- package/dist/shared/types/questions/choose-the-correct-answer.js +1 -9
- package/dist/shared/types/questions/fill-missing-words-in-grid.js +1 -11
- package/dist/shared/types/questions/index.js +1 -40
- package/dist/shared/types/questions/listen-and-speak-answer.js +1 -1
- package/dist/shared/types/questions/listen-and-speak-compare-images.js +1 -1
- package/dist/shared/types/questions/listen-and-speak-image-group.js +1 -1
- package/dist/shared/types/questions/listen-and-speak-info-exchange.js +1 -3
- package/dist/shared/types/questions/listen-and-speak-odd-one-out.js +1 -3
- package/dist/shared/types/questions/listen-and-speak-question-list.js +1 -1
- package/dist/shared/types/questions/listen-and-speak-with-story-images.js +1 -3
- package/dist/shared/types/questions/look-picture-choose-correct-answer.js +1 -5
- package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +1 -9
- package/dist/shared/types/questions/look-picture-fill-word-hint.js +1 -7
- package/dist/shared/types/questions/read-displayed-content.js +1 -1
- package/dist/shared/types/questions/speaking-conversation.js +1 -7
- package/dist/shared/types/questions/speaking-describe-image.js +1 -1
- package/dist/shared/types/questions/spontaneous-qa.js +1 -1
- package/dist/shared/types/questions/write-correct-verb-form.js +1 -12
- package/dist/shared/types/role.types.js +1 -1
- package/dist/shared/types/structure-management.js +1 -28
- package/dist/shared/types/student.types.js +1 -54
- package/dist/shared/types/teacher-dashboard-rooms.js +1 -2
- package/package.json +4 -2
|
@@ -1,31 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { Loader2 } from 'lucide-react';
|
|
4
|
-
import { cn } from '../../../shared/lib/utils';
|
|
5
|
-
import { EnglishCertificationBrand } from './EnglishCertificationBrand';
|
|
6
|
-
/**
|
|
7
|
-
* Formats seconds into HH:MM:SS string.
|
|
8
|
-
* Always shows hours for the 2-hour standardized exams.
|
|
9
|
-
*/
|
|
10
|
-
function formatTime(seconds) {
|
|
11
|
-
const h = Math.floor(seconds / 3600);
|
|
12
|
-
const m = Math.floor((seconds % 3600) / 60);
|
|
13
|
-
const s = seconds % 60;
|
|
14
|
-
return `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Formats a Date into HH:MM local time string.
|
|
18
|
-
*/
|
|
19
|
-
function formatSavedAt(date) {
|
|
20
|
-
return date.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false });
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Branded Ocean Edu top header bar for English Certification exam taking.
|
|
24
|
-
*
|
|
25
|
-
* Layout (desktop): brand block · test name · part/range meta · timer · submit.
|
|
26
|
-
*/
|
|
27
|
-
export function EnglishCertificationHeader({ examName, remainingSeconds, totalQuestions, answeredCount, isSubmitting, onSubmit, lastSavedAt, isSaving, partLabel, partSubLabel, questionRange, }) {
|
|
28
|
-
const isWarning = remainingSeconds <= 900 && remainingSeconds > 300; // 5-15 min
|
|
29
|
-
const isCritical = remainingSeconds <= 300; // ≤5 min
|
|
30
|
-
return (_jsxs("header", { className: cn('sticky top-0 z-50 flex h-[74px] items-center gap-4 border-b border-[#d9e4f2] px-4 lg:px-6', 'bg-white/95 shadow-[0_8px_26px_rgba(10,61,154,0.08)] backdrop-blur-md'), children: [_jsx("div", { className: "flex shrink-0 items-center gap-3", children: _jsx("div", { className: "hidden border-r border-[#d9e4f2] pr-4 sm:block", children: _jsx(EnglishCertificationBrand, {}) }) }), _jsxs("div", { className: "hidden min-w-0 lg:block", children: [_jsx("strong", { className: "block truncate text-[17px] font-semibold leading-tight text-[#0f1730]", children: examName }), _jsx("span", { className: "mt-1 block text-sm font-bold text-[#64748b]", children: "English Certification" })] }), _jsxs("div", { className: "flex flex-1 items-center justify-center gap-5 text-center", children: [partLabel && (_jsxs("div", { className: "leading-tight text-[#0b57d0]", children: [_jsx("div", { className: "text-sm font-black tracking-wide", children: partLabel }), partSubLabel && (_jsx("div", { className: "mt-1 text-xs font-semibold text-[#27324a]", children: partSubLabel }))] })), questionRange && (_jsxs("div", { className: "whitespace-nowrap text-[15px] font-bold text-[#0f1730]", children: ["Questions ", _jsx("span", { className: "text-[#0b57d0]", children: questionRange })] }))] }), _jsxs("div", { className: "flex shrink-0 flex-col items-center border-l border-[#d9e4f2] pl-4", children: [_jsx("span", { className: cn('font-mono text-xl font-black leading-none tracking-wide tabular-nums lg:text-2xl', !isWarning && !isCritical && 'text-[#0b57d0]', isWarning && 'text-orange-500', isCritical && 'animate-pulse text-red-500'), "aria-live": "polite", "aria-label": `Time remaining: ${formatTime(remainingSeconds)}`, children: formatTime(remainingSeconds) }), _jsx("span", { className: "mt-1 text-[11px] font-semibold text-[#64748b]", children: "Time Remaining" }), (isSaving || lastSavedAt) && (_jsx("span", { className: "mt-0.5 flex items-center gap-1 text-[10px] text-slate-400", children: isSaving ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-2.5 w-2.5 animate-spin" }), "Saving..."] })) : lastSavedAt ? (_jsxs(_Fragment, { children: ["Saved ", formatSavedAt(lastSavedAt)] })) : null }))] }), _jsxs("div", { className: "flex shrink-0 items-center gap-3", children: [_jsxs("div", { className: "hidden text-right sm:block", children: [_jsxs("div", { className: "text-sm font-semibold text-[#0f1730]", children: [_jsx("span", { className: "text-[#0b57d0]", children: answeredCount }), _jsxs("span", { className: "text-slate-400", children: ["/", totalQuestions] })] }), _jsx("div", { className: "text-[11px] text-slate-400", children: "Answered" })] }), _jsx("button", { type: "button", onClick: onSubmit, disabled: isSubmitting, className: cn('inline-flex h-12 items-center gap-1.5 rounded-lg px-5 text-sm font-black tracking-wide text-white', 'bg-gradient-to-b from-[#ffad31] to-[#ff8a00] shadow-[0_12px_24px_rgba(255,138,0,0.3)]', 'transition-all hover:-translate-y-0.5 hover:brightness-[0.98]', 'disabled:cursor-not-allowed disabled:opacity-60'), children: isSubmitting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin" }), "Submitting..."] })) : ('SUBMIT TEST') })] })] }));
|
|
31
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as t,Fragment as a}from"react/jsx-runtime";import{Loader2 as i}from"lucide-react";import{cn as n}from"../../../shared/lib/utils";import{EnglishCertificationBrand as s}from"./EnglishCertificationBrand";function l(e){const t=Math.floor(e/3600),a=Math.floor(e%3600/60),i=e%60;return`${String(t).padStart(2,"0")}:${String(a).padStart(2,"0")}:${String(i).padStart(2,"0")}`}export function EnglishCertificationHeader({examName:r,remainingSeconds:d,totalQuestions:c,answeredCount:o,isSubmitting:m,onSubmit:x,lastSavedAt:h,isSaving:b,partLabel:p,partSubLabel:g,questionRange:f}){const u=d<=900&&d>300,N=d<=300;return t("header",{className:n("sticky top-0 z-50 flex h-[74px] items-center gap-4 border-b border-[#d9e4f2] px-4 lg:px-6","bg-white/95 shadow-[0_8px_26px_rgba(10,61,154,0.08)] backdrop-blur-md"),children:[e("div",{className:"flex shrink-0 items-center gap-3",children:e("div",{className:"hidden border-r border-[#d9e4f2] pr-4 sm:block",children:e(s,{})})}),t("div",{className:"hidden min-w-0 lg:block",children:[e("strong",{className:"block truncate text-[17px] font-semibold leading-tight text-[#0f1730]",children:r}),e("span",{className:"mt-1 block text-sm font-bold text-[#64748b]",children:"English Certification"})]}),t("div",{className:"flex flex-1 items-center justify-center gap-5 text-center",children:[p&&t("div",{className:"leading-tight text-[#0b57d0]",children:[e("div",{className:"text-sm font-black tracking-wide",children:p}),g&&e("div",{className:"mt-1 text-xs font-semibold text-[#27324a]",children:g})]}),f&&t("div",{className:"whitespace-nowrap text-[15px] font-bold text-[#0f1730]",children:["Questions ",e("span",{className:"text-[#0b57d0]",children:f})]})]}),t("div",{className:"flex shrink-0 flex-col items-center border-l border-[#d9e4f2] pl-4",children:[e("span",{className:n("font-mono text-xl font-black leading-none tracking-wide tabular-nums lg:text-2xl",!u&&!N&&"text-[#0b57d0]",u&&"text-orange-500",N&&"animate-pulse text-red-500"),"aria-live":"polite","aria-label":`Time remaining: ${l(d)}`,children:l(d)}),e("span",{className:"mt-1 text-[11px] font-semibold text-[#64748b]",children:"Time Remaining"}),(b||h)&&e("span",{className:"mt-0.5 flex items-center gap-1 text-[10px] text-slate-400",children:b?t(a,{children:[e(i,{className:"h-2.5 w-2.5 animate-spin"}),"Saving..."]}):h?t(a,{children:["Saved ",(v=h,v.toLocaleTimeString("en-US",{hour:"2-digit",minute:"2-digit",hour12:!1}))]}):null})]}),t("div",{className:"flex shrink-0 items-center gap-3",children:[t("div",{className:"hidden text-right sm:block",children:[t("div",{className:"text-sm font-semibold text-[#0f1730]",children:[e("span",{className:"text-[#0b57d0]",children:o}),t("span",{className:"text-slate-400",children:["/",c]})]}),e("div",{className:"text-[11px] text-slate-400",children:"Answered"})]}),e("button",{type:"button",onClick:x,disabled:m,className:n("inline-flex h-12 items-center gap-1.5 rounded-lg px-5 text-sm font-black tracking-wide text-white","bg-gradient-to-b from-[#ffad31] to-[#ff8a00] shadow-[0_12px_24px_rgba(255,138,0,0.3)]","transition-all hover:-translate-y-0.5 hover:brightness-[0.98]","disabled:cursor-not-allowed disabled:opacity-60"),children:m?t(a,{children:[e(i,{className:"h-4 w-4 animate-spin"}),"Submitting..."]}):"SUBMIT TEST"})]})]});var v}
|
|
@@ -1,71 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useRef, useEffect, useMemo } from 'react';
|
|
4
|
-
import { cn } from '../../../shared/lib/utils';
|
|
5
|
-
/** Returns true when an answer entry counts as "answered". */
|
|
6
|
-
function isAnswered(answer) {
|
|
7
|
-
if (answer === undefined || answer === null || answer === '')
|
|
8
|
-
return false;
|
|
9
|
-
if (typeof answer === 'object' && 'optionId' in answer) {
|
|
10
|
-
return Boolean(answer.optionId);
|
|
11
|
-
}
|
|
12
|
-
return true;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Branded Ocean Edu "Answer Sheet" navigation panel.
|
|
16
|
-
*
|
|
17
|
-
* Shows an Answered / Unanswered / Marked summary, a per-part question grid
|
|
18
|
-
* (answered, current, marked states) and a legend, bound to real store data.
|
|
19
|
-
*/
|
|
20
|
-
export function EnglishCertificationNavigationPanel({ parts, questions, answers, flaggedQuestions, currentIndex, onQuestionClick, onPartClick, }) {
|
|
21
|
-
const buttonRefs = useRef(new Map());
|
|
22
|
-
// Scroll current button into view when currentIndex changes.
|
|
23
|
-
useEffect(() => {
|
|
24
|
-
const btn = buttonRefs.current.get(currentIndex);
|
|
25
|
-
btn?.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
|
|
26
|
-
}, [currentIndex]);
|
|
27
|
-
const { answeredCount, markedCount, unansweredCount } = useMemo(() => {
|
|
28
|
-
const answered = questions.filter((q) => isAnswered(answers[q.id])).length;
|
|
29
|
-
const marked = questions.filter((q) => flaggedQuestions.has(q.id)).length;
|
|
30
|
-
return {
|
|
31
|
-
answeredCount: answered,
|
|
32
|
-
markedCount: marked,
|
|
33
|
-
unansweredCount: questions.length - answered,
|
|
34
|
-
};
|
|
35
|
-
}, [questions, answers, flaggedQuestions]);
|
|
36
|
-
return (_jsxs("div", { className: "flex h-full flex-col", children: [_jsxs("div", { className: "grid grid-cols-3 gap-3 p-4 pb-2", children: [_jsx(SummaryBox, { label: "Answered", value: answeredCount, variant: "answered" }), _jsx(SummaryBox, { label: "Unanswered", value: unansweredCount, variant: "unanswered" }), _jsx(SummaryBox, { label: "Marked", value: markedCount, variant: "marked" })] }), _jsxs("div", { className: "flex-1 overflow-y-auto px-4 pb-4", children: [parts.map((part, partIdx) => {
|
|
37
|
-
const startGlobal = part.startIndex ?? 0;
|
|
38
|
-
const endGlobal = part.endIndex ?? startGlobal;
|
|
39
|
-
const totalInPart = endGlobal - startGlobal + 1;
|
|
40
|
-
return (_jsxs("section", { className: "mb-5", children: [_jsx("button", { type: "button", onClick: () => onPartClick(partIdx), className: cn('mb-3 w-full text-left text-[15px] font-black text-[#17213a] transition-colors hover:text-[#0b57d0]', currentIndex >= startGlobal &&
|
|
41
|
-
currentIndex <= endGlobal &&
|
|
42
|
-
'text-[#0b57d0]'), children: part.name || `Part ${partIdx + 1}` }), _jsx("div", { className: "grid grid-cols-5 gap-2.5", children: Array.from({ length: totalInPart }, (_, i) => {
|
|
43
|
-
const globalIdx = startGlobal + i;
|
|
44
|
-
const question = questions[globalIdx];
|
|
45
|
-
if (!question)
|
|
46
|
-
return null;
|
|
47
|
-
const answered = isAnswered(answers[question.id]);
|
|
48
|
-
const flagged = flaggedQuestions.has(question.id);
|
|
49
|
-
const current = globalIdx === currentIndex;
|
|
50
|
-
const displayNum = globalIdx + 1;
|
|
51
|
-
return (_jsxs("button", { ref: (el) => {
|
|
52
|
-
if (el)
|
|
53
|
-
buttonRefs.current.set(globalIdx, el);
|
|
54
|
-
else
|
|
55
|
-
buttonRefs.current.delete(globalIdx);
|
|
56
|
-
}, type: "button", onClick: () => onQuestionClick(globalIdx), className: cn('relative flex h-[50px] items-center justify-center rounded-md border text-[15px] font-bold transition-all', 'focus-visible:ring-2 focus-visible:ring-[#1167e8] focus-visible:ring-offset-1', answered
|
|
57
|
-
? 'border-[#b9edc8] bg-[#ddf8e7] text-[#111827]'
|
|
58
|
-
: 'border-[#d9e4f2] bg-white text-[#111827]', current &&
|
|
59
|
-
'border-[#1167e8] text-[#0b57d0] shadow-[inset_0_0_0_1px_#1167e8]'), "aria-label": `Question ${displayNum}${answered ? ', answered' : ', unanswered'}${flagged ? ', marked' : ''}`, "aria-current": current ? 'true' : undefined, children: [displayNum, flagged && (_jsx("span", { className: "absolute -top-px right-2 h-[18px] w-[9px] bg-[#ff8a00]", style: {
|
|
60
|
-
clipPath: 'polygon(0 0, 100% 0, 100% 100%, 50% 70%, 0 100%)',
|
|
61
|
-
}, "aria-hidden": "true" }))] }, question.id));
|
|
62
|
-
}) })] }, partIdx));
|
|
63
|
-
}), _jsxs("div", { className: "mt-2 rounded-md border border-[#d9e4f2] bg-white/70 p-4", children: [_jsx(LegendRow, { variant: "current", children: "Current Question" }), _jsx(LegendRow, { variant: "answered", children: "Answered" }), _jsx(LegendRow, { variant: "none", children: "Not Answered" }), _jsx(LegendRow, { variant: "marked", children: "Marked for Review" })] })] })] }));
|
|
64
|
-
}
|
|
65
|
-
function SummaryBox({ label, value, variant, }) {
|
|
66
|
-
return (_jsx("div", { className: "grid min-h-[78px] place-items-center rounded-md border border-[#d9e4f2] bg-white text-center", children: _jsxs("div", { children: [_jsx("span", { className: cn('block text-[11px] font-black uppercase tracking-wide', variant === 'answered' && 'text-[#0a3d9a]', variant === 'unanswered' && 'text-[#17213a]', variant === 'marked' && 'text-[#f97316]'), children: label }), _jsx("strong", { className: cn('mt-2 block text-[28px] leading-none', variant === 'answered' && 'text-[#0b57d0]', variant === 'unanswered' && 'text-[#17213a]', variant === 'marked' && 'text-[#f97316]'), children: value })] }) }));
|
|
67
|
-
}
|
|
68
|
-
function LegendRow({ variant, children, }) {
|
|
69
|
-
return (_jsxs("div", { className: "my-3 flex items-center gap-3.5 text-sm font-semibold text-[#334155]", children: [variant === 'marked' ? (_jsx("span", { className: "h-[18px] w-2.5 bg-[#ff8a00]", style: { clipPath: 'polygon(0 0, 100% 0, 100% 100%, 50% 70%, 0 100%)' } })) : (_jsx("span", { className: cn('h-[22px] w-[22px] rounded border border-[#d9e4f2] bg-white', variant === 'current' &&
|
|
70
|
-
'border-[#1167e8] shadow-[inset_0_0_0_1px_#1167e8]', variant === 'answered' && 'border-[#b9edc8] bg-[#ddf8e7]') })), children] }));
|
|
71
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useRef as r,useEffect as n,useMemo as a}from"react";import{cn as d}from"../../../shared/lib/utils";function i(e){return null!=e&&""!==e&&("object"!=typeof e||!("optionId"in e)||Boolean(e.optionId))}export function EnglishCertificationNavigationPanel({parts:o,questions:c,answers:u,flaggedQuestions:b,currentIndex:m,onQuestionClick:f,onPartClick:x}){const h=r(new Map);n(()=>{const e=h.current.get(m);e?.scrollIntoView({block:"nearest",behavior:"smooth"})},[m]);const{answeredCount:p,markedCount:g,unansweredCount:w}=a(()=>{const e=c.filter(e=>i(u[e.id])).length;return{answeredCount:e,markedCount:c.filter(e=>b.has(e.id)).length,unansweredCount:c.length-e}},[c,u,b]);return t("div",{className:"flex h-full flex-col",children:[t("div",{className:"grid grid-cols-3 gap-3 p-4 pb-2",children:[e(s,{label:"Answered",value:p,variant:"answered"}),e(s,{label:"Unanswered",value:w,variant:"unanswered"}),e(s,{label:"Marked",value:g,variant:"marked"})]}),t("div",{className:"flex-1 overflow-y-auto px-4 pb-4",children:[o.map((r,n)=>{const a=r.startIndex??0,s=r.endIndex??a,l=s-a+1;return t("section",{className:"mb-5",children:[e("button",{type:"button",onClick:()=>x(n),className:d("mb-3 w-full text-left text-[15px] font-black text-[#17213a] transition-colors hover:text-[#0b57d0]",m>=a&&m<=s&&"text-[#0b57d0]"),children:r.name||`Part ${n+1}`}),e("div",{className:"grid grid-cols-5 gap-2.5",children:Array.from({length:l},(r,n)=>{const s=a+n,l=c[s];if(!l)return null;const o=i(u[l.id]),x=b.has(l.id),p=s===m,g=s+1;return t("button",{ref:e=>{e?h.current.set(s,e):h.current.delete(s)},type:"button",onClick:()=>f(s),className:d("relative flex h-[50px] items-center justify-center rounded-md border text-[15px] font-bold transition-all","focus-visible:ring-2 focus-visible:ring-[#1167e8] focus-visible:ring-offset-1",o?"border-[#b9edc8] bg-[#ddf8e7] text-[#111827]":"border-[#d9e4f2] bg-white text-[#111827]",p&&"border-[#1167e8] text-[#0b57d0] shadow-[inset_0_0_0_1px_#1167e8]"),"aria-label":`Question ${g}${o?", answered":", unanswered"}${x?", marked":""}`,"aria-current":p?"true":void 0,children:[g,x&&e("span",{className:"absolute -top-px right-2 h-[18px] w-[9px] bg-[#ff8a00]",style:{clipPath:"polygon(0 0, 100% 0, 100% 100%, 50% 70%, 0 100%)"},"aria-hidden":"true"})]},l.id)})})]},n)}),t("div",{className:"mt-2 rounded-md border border-[#d9e4f2] bg-white/70 p-4",children:[e(l,{variant:"current",children:"Current Question"}),e(l,{variant:"answered",children:"Answered"}),e(l,{variant:"none",children:"Not Answered"}),e(l,{variant:"marked",children:"Marked for Review"})]})]})]})}function s({label:r,value:n,variant:a}){return e("div",{className:"grid min-h-[78px] place-items-center rounded-md border border-[#d9e4f2] bg-white text-center",children:t("div",{children:[e("span",{className:d("block text-[11px] font-black uppercase tracking-wide","answered"===a&&"text-[#0a3d9a]","unanswered"===a&&"text-[#17213a]","marked"===a&&"text-[#f97316]"),children:r}),e("strong",{className:d("mt-2 block text-[28px] leading-none","answered"===a&&"text-[#0b57d0]","unanswered"===a&&"text-[#17213a]","marked"===a&&"text-[#f97316]"),children:n})]})})}function l({variant:r,children:n}){return t("div",{className:"my-3 flex items-center gap-3.5 text-sm font-semibold text-[#334155]",children:[e("span","marked"===r?{className:"h-[18px] w-2.5 bg-[#ff8a00]",style:{clipPath:"polygon(0 0, 100% 0, 100% 100%, 50% 70%, 0 100%)"}}:{className:d("h-[22px] w-[22px] rounded border border-[#d9e4f2] bg-white","current"===r&&"border-[#1167e8] shadow-[inset_0_0_0_1px_#1167e8]","answered"===r&&"border-[#b9edc8] bg-[#ddf8e7]")}),n]})}
|
|
@@ -1,33 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { cn } from '../../../shared/lib/utils';
|
|
4
|
-
import { CheckCircle2, XCircle } from 'lucide-react';
|
|
5
|
-
/**
|
|
6
|
-
* Shared A–D answer selector for all English Certification question types.
|
|
7
|
-
*
|
|
8
|
-
* Renders either large branded cards or compact rows, and shows
|
|
9
|
-
* correct/incorrect indicators in review mode.
|
|
10
|
-
*/
|
|
11
|
-
export function EnglishCertificationOptionSelector({ options, selectedOptionId, onSelect, disabled = false, isReviewMode = false, correctOptionId, variant = 'cards', }) {
|
|
12
|
-
if (variant === 'compact') {
|
|
13
|
-
return (_jsx("div", { className: "flex flex-col gap-1", role: "radiogroup", children: options.map((option) => {
|
|
14
|
-
const isSelected = option.id === selectedOptionId;
|
|
15
|
-
const isCorrect = isReviewMode && option.id === correctOptionId;
|
|
16
|
-
const isWrong = isReviewMode && isSelected && option.id !== correctOptionId;
|
|
17
|
-
return (_jsxs("button", { type: "button", role: "radio", "aria-checked": isSelected, disabled: disabled, onClick: () => !disabled && onSelect(option.id), className: cn('flex items-center gap-2.5 rounded-md border border-transparent px-2.5 py-1.5 text-left text-base transition-all', 'hover:border-[#c8def7] hover:bg-[#f4f9ff]', isSelected &&
|
|
18
|
-
!isReviewMode &&
|
|
19
|
-
'border-[#99c5f8] bg-[#eff7ff] shadow-[inset_0_0_0_1px_rgba(17,103,232,0.08)]', isCorrect && 'border-[#b9edc8] bg-[#ddf8e7]', isWrong && 'border-red-300 bg-red-50', disabled && 'cursor-not-allowed opacity-60'), children: [_jsx("span", { className: cn('grid h-4 w-4 shrink-0 place-items-center rounded-full border-2 border-[#94a3b8]', isSelected && !isReviewMode && 'border-[#0b57d0]', isCorrect && 'border-[#22c55e]', isWrong && 'border-red-500'), children: isSelected && (_jsx("span", { className: cn('h-2 w-2 rounded-full bg-[#0b57d0]', isCorrect && 'bg-[#22c55e]', isWrong && 'bg-red-500') })) }), _jsx("span", { className: "font-bold text-[#23304c]", children: option.label }), _jsx("span", { className: "flex-1 leading-snug text-[#1e293b]", children: option.text }), isReviewMode && isCorrect && (_jsx(CheckCircle2, { className: "h-4 w-4 shrink-0 text-[#22c55e]", "aria-label": "Correct" })), isReviewMode && isWrong && (_jsx(XCircle, { className: "h-4 w-4 shrink-0 text-red-500", "aria-label": "Incorrect" }))] }, option.id));
|
|
20
|
-
}) }));
|
|
21
|
-
}
|
|
22
|
-
// 'cards' variant — large A–D answer cards.
|
|
23
|
-
return (_jsx("div", { className: "grid gap-4 sm:gap-6", style: { gridTemplateColumns: `repeat(${Math.min(options.length, 4)}, minmax(0, 1fr))` }, role: "radiogroup", children: options.map((option) => {
|
|
24
|
-
const isSelected = option.id === selectedOptionId;
|
|
25
|
-
const isCorrect = isReviewMode && option.id === correctOptionId;
|
|
26
|
-
const isWrong = isReviewMode && isSelected && option.id !== correctOptionId;
|
|
27
|
-
const isActive = isSelected && !isReviewMode;
|
|
28
|
-
return (_jsxs("button", { type: "button", role: "radio", "aria-checked": isSelected, disabled: disabled, onClick: () => !disabled && onSelect(option.id), className: cn('relative flex min-h-[96px] flex-col items-center justify-center rounded-[10px] border p-3 transition-all', 'border-[#d9e4f2] bg-white shadow-[0_5px_16px_rgba(13,71,161,0.08)]', 'hover:-translate-y-0.5', isActive &&
|
|
29
|
-
'border-[#0b57d0] text-white shadow-[0_16px_30px_rgba(11,87,208,0.25)]', isCorrect && 'border-[#22c55e] bg-[#ddf8e7]', isWrong && 'border-red-400 bg-red-50', disabled && 'cursor-not-allowed opacity-60'), style: isActive
|
|
30
|
-
? { background: 'linear-gradient(180deg, #1f7cff, #0b57d0)' }
|
|
31
|
-
: undefined, children: [_jsx("span", { className: cn('grid h-[52px] w-[52px] place-items-center rounded-full border-[1.5px] text-2xl font-black', 'border-[#0b57d0] bg-white text-[#0b57d0]', isActive && 'border-white', isCorrect && 'border-[#22c55e] text-[#22c55e]', isWrong && 'border-red-500 text-red-500'), children: option.label }), option.text && (_jsx("span", { className: cn('mt-2 line-clamp-2 text-center text-sm font-semibold text-[#0f1730]', isActive && 'text-white', isCorrect && 'text-[#15803d]', isWrong && 'text-red-700'), children: option.text })), (isActive || isCorrect) && (_jsx("span", { className: "absolute right-2.5 top-2.5 grid h-[23px] w-[23px] place-items-center rounded-full bg-white text-[15px] font-black text-[#0b57d0]", children: "\u2713" }))] }, option.id));
|
|
32
|
-
}) }));
|
|
33
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{cn as t}from"../../../shared/lib/utils";import{CheckCircle2 as d,XCircle as a}from"lucide-react";export function EnglishCertificationOptionSelector({options:o,selectedOptionId:l,onSelect:i,disabled:n=!1,isReviewMode:s=!1,correctOptionId:c,variant:b="cards"}){return e("div","compact"===b?{className:"flex flex-col gap-1",role:"radiogroup",children:o.map(o=>{const b=o.id===l,p=s&&o.id===c,x=s&&b&&o.id!==c;return r("button",{type:"button",role:"radio","aria-checked":b,disabled:n,onClick:()=>!n&&i(o.id),className:t("flex items-center gap-2.5 rounded-md border border-transparent px-2.5 py-1.5 text-left text-base transition-all","hover:border-[#c8def7] hover:bg-[#f4f9ff]",b&&!s&&"border-[#99c5f8] bg-[#eff7ff] shadow-[inset_0_0_0_1px_rgba(17,103,232,0.08)]",p&&"border-[#b9edc8] bg-[#ddf8e7]",x&&"border-red-300 bg-red-50",n&&"cursor-not-allowed opacity-60"),children:[e("span",{className:t("grid h-4 w-4 shrink-0 place-items-center rounded-full border-2 border-[#94a3b8]",b&&!s&&"border-[#0b57d0]",p&&"border-[#22c55e]",x&&"border-red-500"),children:b&&e("span",{className:t("h-2 w-2 rounded-full bg-[#0b57d0]",p&&"bg-[#22c55e]",x&&"bg-red-500")})}),e("span",{className:"font-bold text-[#23304c]",children:o.label}),e("span",{className:"flex-1 leading-snug text-[#1e293b]",children:o.text}),s&&p&&e(d,{className:"h-4 w-4 shrink-0 text-[#22c55e]","aria-label":"Correct"}),s&&x&&e(a,{className:"h-4 w-4 shrink-0 text-red-500","aria-label":"Incorrect"})]},o.id)})}:{className:"grid gap-4 sm:gap-6",style:{gridTemplateColumns:`repeat(${Math.min(o.length,4)}, minmax(0, 1fr))`},role:"radiogroup",children:o.map(d=>{const a=d.id===l,o=s&&d.id===c,b=s&&a&&d.id!==c,p=a&&!s;return r("button",{type:"button",role:"radio","aria-checked":a,disabled:n,onClick:()=>!n&&i(d.id),className:t("relative flex min-h-[96px] flex-col items-center justify-center rounded-[10px] border p-3 transition-all","border-[#d9e4f2] bg-white shadow-[0_5px_16px_rgba(13,71,161,0.08)]","hover:-translate-y-0.5",p&&"border-[#0b57d0] text-white shadow-[0_16px_30px_rgba(11,87,208,0.25)]",o&&"border-[#22c55e] bg-[#ddf8e7]",b&&"border-red-400 bg-red-50",n&&"cursor-not-allowed opacity-60"),style:p?{background:"linear-gradient(180deg, #1f7cff, #0b57d0)"}:void 0,children:[e("span",{className:t("grid h-[52px] w-[52px] place-items-center rounded-full border-[1.5px] text-2xl font-black","border-[#0b57d0] bg-white text-[#0b57d0]",p&&"border-white",o&&"border-[#22c55e] text-[#22c55e]",b&&"border-red-500 text-red-500"),children:d.label}),d.text&&e("span",{className:t("mt-2 line-clamp-2 text-center text-sm font-semibold text-[#0f1730]",p&&"text-white",o&&"text-[#15803d]",b&&"text-red-700"),children:d.text}),(p||o)&&e("span",{className:"absolute right-2.5 top-2.5 grid h-[23px] w-[23px] place-items-center rounded-full bg-white text-[15px] font-black text-[#0b57d0]",children:"✓"})]},d.id)})})}
|
|
@@ -1,16 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useImageUrl } from '../../../shared/lib/hooks/useImageUrl';
|
|
4
|
-
/**
|
|
5
|
-
* Reading passage panel for grouped English Certification parts.
|
|
6
|
-
*
|
|
7
|
-
* Extracted into its own component so it can call {@link useImageUrl} to resolve
|
|
8
|
-
* the passage image. The API persists images as S3 keys (e.g.
|
|
9
|
-
* `questions/images/...`) which must be exchanged for a presigned URL before
|
|
10
|
-
* they can be rendered. Local paths (`/...`) and absolute URLs pass through
|
|
11
|
-
* unchanged.
|
|
12
|
-
*/
|
|
13
|
-
export function EnglishCertificationPassagePanel({ passage, passageTitle, passageSubtitle, passageImageUrl, passageIsHtml, partName, }) {
|
|
14
|
-
const resolvedImageUrl = useImageUrl(passageImageUrl || null);
|
|
15
|
-
return (_jsxs("article", { className: "flex flex-col overflow-hidden rounded-xl border border-[#d9e4f2] bg-white/90 shadow-[0_14px_35px_rgba(13,71,161,0.12)] backdrop-blur-md", children: [_jsx("div", { className: "flex h-[52px] items-end border-b border-[#d9e4f2] bg-white/70 px-5", children: _jsx("span", { className: "flex h-full items-center border-b-[3px] border-[#0b57d0] text-sm font-black tracking-wide text-[#0a3d9a]", children: "PASSAGE" }) }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "px-6 pt-6 leading-relaxed", children: [passageTitle && (_jsx("h1", { className: "mb-1 text-[clamp(20px,1.6vw,28px)] font-black leading-tight text-[#061a55]", children: passageTitle })), passageSubtitle && (_jsx("p", { className: "mb-4 text-[15px] font-semibold text-[#475569]", children: passageSubtitle })), resolvedImageUrl && (_jsx("div", { className: "mb-4 overflow-hidden rounded-lg border border-[#d9e4f2]", children: _jsx("img", { src: resolvedImageUrl, alt: "passage", className: "h-auto w-full object-cover" }) })), passageIsHtml ? (_jsx("div", { className: "ec-passage-html text-[15.8px] leading-relaxed text-[#1e293b] [&_p]:mb-3 [&_strong]:font-bold [&_ul]:my-3 [&_ul]:list-disc [&_ul]:pl-6 [&_table]:my-3 [&_td]:border [&_td]:border-[#d9e4f2] [&_td]:px-2 [&_td]:py-1", dangerouslySetInnerHTML: { __html: passage } })) : (_jsx("div", { className: "whitespace-pre-wrap text-[15.8px] text-[#1e293b]", children: passage }))] }), _jsxs("div", { className: "mt-4 flex items-center gap-2.5 border-t border-[#d9e4f2] bg-white/92 px-5 py-3 text-[13px] font-bold text-[#334155] backdrop-blur-md", children: [_jsx("span", { className: "grid h-[18px] w-[18px] place-items-center rounded-full border-2 border-[#1167e8] text-[11px] font-black text-[#1167e8]", children: "i" }), partName || 'Reading', " \u00B7 Reading passage"] })] })] }));
|
|
16
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as a}from"react/jsx-runtime";import{useImageUrl as t}from"../../../shared/lib/hooks/useImageUrl";export function EnglishCertificationPassagePanel({passage:l,passageTitle:r,passageSubtitle:d,passageImageUrl:s,passageIsHtml:i,partName:o}){const c=t(s||null);return a("article",{className:"flex flex-col overflow-hidden rounded-xl border border-[#d9e4f2] bg-white/90 shadow-[0_14px_35px_rgba(13,71,161,0.12)] backdrop-blur-md",children:[e("div",{className:"flex h-[52px] items-end border-b border-[#d9e4f2] bg-white/70 px-5",children:e("span",{className:"flex h-full items-center border-b-[3px] border-[#0b57d0] text-sm font-black tracking-wide text-[#0a3d9a]",children:"PASSAGE"})}),a("div",{className:"flex-1",children:[a("div",{className:"px-6 pt-6 leading-relaxed",children:[r&&e("h1",{className:"mb-1 text-[clamp(20px,1.6vw,28px)] font-black leading-tight text-[#061a55]",children:r}),d&&e("p",{className:"mb-4 text-[15px] font-semibold text-[#475569]",children:d}),c&&e("div",{className:"mb-4 overflow-hidden rounded-lg border border-[#d9e4f2]",children:e("img",{src:c,alt:"passage",className:"h-auto w-full object-cover"})}),e("div",i?{className:"ec-passage-html text-[15.8px] leading-relaxed text-[#1e293b] [&_p]:mb-3 [&_strong]:font-bold [&_ul]:my-3 [&_ul]:list-disc [&_ul]:pl-6 [&_table]:my-3 [&_td]:border [&_td]:border-[#d9e4f2] [&_td]:px-2 [&_td]:py-1",dangerouslySetInnerHTML:{__html:l}}:{className:"whitespace-pre-wrap text-[15.8px] text-[#1e293b]",children:l})]}),a("div",{className:"mt-4 flex items-center gap-2.5 border-t border-[#d9e4f2] bg-white/92 px-5 py-3 text-[13px] font-bold text-[#334155] backdrop-blur-md",children:[e("span",{className:"grid h-[18px] w-[18px] place-items-center rounded-full border-2 border-[#1167e8] text-[11px] font-black text-[#1167e8]",children:"i"}),o||"Reading"," · Reading passage"]})]})]})}
|
|
@@ -1,22 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { EnglishCertificationOptionSelector } from './EnglishCertificationOptionSelector';
|
|
4
|
-
import { EnglishCertificationFlagButton } from './EnglishCertificationFlagButton';
|
|
5
|
-
/**
|
|
6
|
-
* Single MCQ rendered as a standalone Ocean Edu white card.
|
|
7
|
-
*
|
|
8
|
-
* Replaces the former shared-section/divider layout so each question matches the
|
|
9
|
-
* target mockup (own card, number chip, flag button, compact options).
|
|
10
|
-
*/
|
|
11
|
-
export function EnglishCertificationQuestionCard({ question, globalQuestionNumber, answer, isFlagged, sharedInstruction, onAnswerChange, onToggleFlag, isReviewMode = false, }) {
|
|
12
|
-
const content = question.content || {};
|
|
13
|
-
const rawOptions = content.options || [];
|
|
14
|
-
const questionText = content.question?.trim() ?? '';
|
|
15
|
-
// Skip per-question text when it merely repeats the shared part instruction.
|
|
16
|
-
const showQuestionText = questionText.length > 0 && questionText !== sharedInstruction;
|
|
17
|
-
return (_jsx("article", { className: "rounded-xl border border-[#d9e4f2] bg-white/90 px-4 py-4 shadow-[0_5px_16px_rgba(13,71,161,0.08)] backdrop-blur-md transition-colors", children: _jsxs("div", { className: "grid grid-cols-[44px_1fr_32px] gap-1", children: [_jsx("div", { className: "grid h-9 w-9 place-items-center rounded-full bg-[#eef5ff] text-base font-black leading-none text-[#0b57d0]", children: globalQuestionNumber }), _jsxs("div", { className: "min-w-0", children: [showQuestionText && (_jsx("div", { className: "mb-2 text-base font-black leading-snug text-[#10172f]", children: questionText })), _jsx(EnglishCertificationOptionSelector, { options: rawOptions.map((option, optIdx) => ({
|
|
18
|
-
id: option?.id || String.fromCharCode(65 + optIdx),
|
|
19
|
-
label: option?.id || String.fromCharCode(65 + optIdx),
|
|
20
|
-
text: option?.text || String(option ?? ''),
|
|
21
|
-
})), selectedOptionId: answer?.optionId ?? null, onSelect: (optionId) => onAnswerChange(question.id, { optionId }), isReviewMode: isReviewMode, disabled: isReviewMode, variant: "compact" })] }), _jsx(EnglishCertificationFlagButton, { isFlagged: isFlagged, onToggle: () => onToggleFlag(question.id), size: "sm", className: "self-start justify-self-end" })] }) }));
|
|
22
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as i}from"react/jsx-runtime";import{EnglishCertificationOptionSelector as t}from"./EnglishCertificationOptionSelector";import{EnglishCertificationFlagButton as n}from"./EnglishCertificationFlagButton";export function EnglishCertificationQuestionCard({question:o,globalQuestionNumber:r,answer:s,isFlagged:a,sharedInstruction:l,onAnswerChange:d,onToggleFlag:c,isReviewMode:g=!1}){const m=o.content||{},f=m.options||[],p=m.question?.trim()??"",u=p.length>0&&p!==l;return e("article",{className:"rounded-xl border border-[#d9e4f2] bg-white/90 px-4 py-4 shadow-[0_5px_16px_rgba(13,71,161,0.08)] backdrop-blur-md transition-colors",children:i("div",{className:"grid grid-cols-[44px_1fr_32px] gap-1",children:[e("div",{className:"grid h-9 w-9 place-items-center rounded-full bg-[#eef5ff] text-base font-black leading-none text-[#0b57d0]",children:r}),i("div",{className:"min-w-0",children:[u&&e("div",{className:"mb-2 text-base font-black leading-snug text-[#10172f]",children:p}),e(t,{options:f.map((e,i)=>({id:e?.id||String.fromCharCode(65+i),label:e?.id||String.fromCharCode(65+i),text:e?.text||String(e??"")})),selectedOptionId:s?.optionId??null,onSelect:e=>d(o.id,{optionId:e}),isReviewMode:g,disabled:g,variant:"compact"})]}),e(n,{isFlagged:a,onToggle:()=>c(o.id),size:"sm",className:"self-start justify-self-end"})]})})}
|
|
@@ -1,37 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { useMemo, useState } from 'react';
|
|
4
|
-
import { Dialog, DialogContent, DialogHeader, DialogTitle, } from '../../../components/ui/dialog';
|
|
5
|
-
import { ResolvedImage } from '../../../components/common/ResolvedImage';
|
|
6
|
-
import { EnglishCertificationOptionSelector } from './EnglishCertificationOptionSelector';
|
|
7
|
-
import { EnglishCertificationFlagButton } from './EnglishCertificationFlagButton';
|
|
8
|
-
import { ENGLISH_CERT_CONTENT_WIDTH_CLASS } from './english-certification-theme';
|
|
9
|
-
/**
|
|
10
|
-
* Ocean Edu branded card for a single multiple-choice question.
|
|
11
|
-
*
|
|
12
|
-
* Renders the question badge, instruction, optional photo with zoom and the
|
|
13
|
-
* A–D answer cards. Audio is intentionally NOT rendered here — listening parts
|
|
14
|
-
* show one shared audio bar at the top of the question list.
|
|
15
|
-
*/
|
|
16
|
-
export function EnglishCertificationQuestionItem({ question, questionNumber, questionConfig, defaultQuestion, isListeningPart = false, answer, isFlagged, onAnswerChange, onToggleFlag, isReviewMode = false, domId, }) {
|
|
17
|
-
const [isImageOpen, setIsImageOpen] = useState(false);
|
|
18
|
-
const selectedOptionId = answer?.optionId ?? null;
|
|
19
|
-
const options = useMemo(() => {
|
|
20
|
-
const rawOptions = question.content?.options;
|
|
21
|
-
if (!Array.isArray(rawOptions))
|
|
22
|
-
return [];
|
|
23
|
-
return rawOptions.map((opt, idx) => ({
|
|
24
|
-
id: String(opt?.id || `${question.id}-opt-${idx}`),
|
|
25
|
-
label: String.fromCharCode(65 + idx),
|
|
26
|
-
text: String(opt?.text || opt?.value || opt || ''),
|
|
27
|
-
}));
|
|
28
|
-
}, [question.content?.options, question.id]);
|
|
29
|
-
const hasImageConfig = questionConfig.includes('imageUrl');
|
|
30
|
-
const imageUrl = question.content?.imageUrl;
|
|
31
|
-
const shouldShowImage = hasImageConfig && Boolean(imageUrl);
|
|
32
|
-
const questionText = question.content?.question?.trim() || defaultQuestion || '';
|
|
33
|
-
const fallbackInstruction = isListeningPart
|
|
34
|
-
? 'Look at the picture and listen to the four statements. Choose the best answer.'
|
|
35
|
-
: 'Read the question carefully and choose the best answer.';
|
|
36
|
-
return (_jsxs("div", { id: domId, className: `${ENGLISH_CERT_CONTENT_WIDTH_CLASS} scroll-mt-24 overflow-hidden rounded-xl border border-[#d9e4f2] bg-white/90 shadow-[0_14px_35px_rgba(13,71,161,0.12)] backdrop-blur-md`, children: [_jsxs("div", { className: "flex items-center justify-between px-9 pt-6", children: [_jsxs("span", { className: "inline-flex h-[42px] items-center rounded-lg px-[18px] text-base font-black text-white shadow-[0_8px_16px_rgba(11,87,208,0.18)]", style: { background: 'linear-gradient(180deg, #1f7cff, #0b57d0)' }, children: ["Question ", questionNumber] }), _jsx(EnglishCertificationFlagButton, { isFlagged: isFlagged, onToggle: () => onToggleFlag(question.id) })] }), _jsxs("div", { className: "px-9 pb-8", children: [_jsxs("div", { className: "my-4 flex items-center gap-3 text-[17px] font-bold text-[#0f1730]", children: [isListeningPart && (_jsx("span", { className: "grid h-7 w-7 shrink-0 place-items-center rounded-full bg-[#0b57d0] text-white", children: "\uD83D\uDD0A" })), _jsx("span", { children: questionText || fallbackInstruction })] }), shouldShowImage && imageUrl && (_jsxs("button", { type: "button", onClick: () => setIsImageOpen(true), className: "group relative mx-auto mb-6 block w-full overflow-hidden rounded-[13px] border border-[#cbdcf0] shadow-[0_16px_35px_rgba(6,26,85,0.13)]", "aria-label": "Open image in large view", children: [_jsx(ResolvedImage, { src: imageUrl, alt: `Question ${questionNumber} visual prompt`, className: "h-auto max-h-[360px] w-full object-cover transition-transform group-hover:scale-[1.01]" }), _jsx("span", { className: "absolute right-3 top-3 grid h-9 w-9 place-items-center rounded-full bg-black/55 text-lg text-white", children: "\u2315" })] })), _jsx("div", { className: "w-full", children: _jsx(EnglishCertificationOptionSelector, { options: options, selectedOptionId: selectedOptionId, onSelect: (optionId) => onAnswerChange(question.id, { optionId }), disabled: isReviewMode, isReviewMode: isReviewMode, variant: "cards" }) })] }), shouldShowImage && imageUrl && (_jsx(Dialog, { open: isImageOpen, onOpenChange: setIsImageOpen, children: _jsxs(DialogContent, { className: "max-w-4xl border-none bg-transparent p-0 shadow-none", children: [_jsx(DialogHeader, { className: "sr-only", children: _jsx(DialogTitle, { children: "Question image" }) }), _jsx("div", { className: "overflow-hidden rounded-lg bg-white", children: _jsx(ResolvedImage, { src: imageUrl, alt: `Question ${questionNumber} enlarged visual prompt`, className: "h-auto w-full object-contain" }) })] }) }))] }));
|
|
37
|
-
}
|
|
1
|
+
"use client";import{jsxs as e,jsx as t}from"react/jsx-runtime";import{useMemo as n,useState as o}from"react";import{Dialog as i,DialogContent as r,DialogHeader as a,DialogTitle as s}from"../../../components/ui/dialog";import{ResolvedImage as l}from"../../../components/common/ResolvedImage";import{EnglishCertificationOptionSelector as d}from"./EnglishCertificationOptionSelector";import{EnglishCertificationFlagButton as c}from"./EnglishCertificationFlagButton";import{ENGLISH_CERT_CONTENT_WIDTH_CLASS as m}from"./english-certification-theme";export function EnglishCertificationQuestionItem({question:p,questionNumber:h,questionConfig:u,defaultQuestion:g,isListeningPart:f=!1,answer:b,isFlagged:x,onAnswerChange:w,onToggleFlag:v,isReviewMode:N=!1,domId:C}){const[_,k]=o(!1),y=b?.optionId??null,j=n(()=>{const e=p.content?.options;return Array.isArray(e)?e.map((e,t)=>({id:String(e?.id||`${p.id}-opt-${t}`),label:String.fromCharCode(65+t),text:String(e?.text||e?.value||e||"")})):[]},[p.content?.options,p.id]),I=u.includes("imageUrl"),Q=p.content?.imageUrl,q=I&&Boolean(Q),S=p.content?.question?.trim()||g||"",$=f?"Look at the picture and listen to the four statements. Choose the best answer.":"Read the question carefully and choose the best answer.";return e("div",{id:C,className:`${m} scroll-mt-24 overflow-hidden rounded-xl border border-[#d9e4f2] bg-white/90 shadow-[0_14px_35px_rgba(13,71,161,0.12)] backdrop-blur-md`,children:[e("div",{className:"flex items-center justify-between px-9 pt-6",children:[e("span",{className:"inline-flex h-[42px] items-center rounded-lg px-[18px] text-base font-black text-white shadow-[0_8px_16px_rgba(11,87,208,0.18)]",style:{background:"linear-gradient(180deg, #1f7cff, #0b57d0)"},children:["Question ",h]}),t(c,{isFlagged:x,onToggle:()=>v(p.id)})]}),e("div",{className:"px-9 pb-8",children:[e("div",{className:"my-4 flex items-center gap-3 text-[17px] font-bold text-[#0f1730]",children:[f&&t("span",{className:"grid h-7 w-7 shrink-0 place-items-center rounded-full bg-[#0b57d0] text-white",children:"🔊"}),t("span",{children:S||$})]}),q&&Q&&e("button",{type:"button",onClick:()=>k(!0),className:"group relative mx-auto mb-6 block w-full overflow-hidden rounded-[13px] border border-[#cbdcf0] shadow-[0_16px_35px_rgba(6,26,85,0.13)]","aria-label":"Open image in large view",children:[t(l,{src:Q,alt:`Question ${h} visual prompt`,className:"h-auto max-h-[360px] w-full object-cover transition-transform group-hover:scale-[1.01]"}),t("span",{className:"absolute right-3 top-3 grid h-9 w-9 place-items-center rounded-full bg-black/55 text-lg text-white",children:"⌕"})]}),t("div",{className:"w-full",children:t(d,{options:j,selectedOptionId:y,onSelect:e=>w(p.id,{optionId:e}),disabled:N,isReviewMode:N,variant:"cards"})})]}),q&&Q&&t(i,{open:_,onOpenChange:k,children:e(r,{className:"max-w-4xl border-none bg-transparent p-0 shadow-none",children:[t(a,{className:"sr-only",children:t(s,{children:"Question image"})}),t("div",{className:"overflow-hidden rounded-lg bg-white",children:t(l,{src:Q,alt:`Question ${h} enlarged visual prompt`,className:"h-auto w-full object-contain"})})]})})]})}
|
package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js
CHANGED
|
@@ -1,72 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { SkillEnum } from '../../../shared/types/common.types';
|
|
4
|
-
import { EnglishCertificationSingleQuestionList } from './EnglishCertificationSingleQuestionList';
|
|
5
|
-
import { EnglishCertificationGroupedQuestion } from './EnglishCertificationGroupedQuestion';
|
|
6
|
-
import { EnglishCertificationAudioPlayer } from './EnglishCertificationAudioPlayer';
|
|
7
|
-
import { ENGLISH_CERT_CONTENT_WIDTH_CLASS } from './english-certification-theme';
|
|
8
|
-
import { EcWordFillStructuredForm } from './EcWordFillStructuredForm';
|
|
9
|
-
import { EcMatchByWritingAnswer } from './EcMatchByWritingAnswer';
|
|
10
|
-
import { EcAnswerTheQuestion } from './EcAnswerTheQuestion';
|
|
11
|
-
import { EcWordFillParagraph } from './EcWordFillParagraph';
|
|
12
|
-
import { EcWritingTask } from './EcWritingTask';
|
|
13
|
-
/**
|
|
14
|
-
* Question types that the branded English Certification theme renders natively
|
|
15
|
-
* as multiple-choice cards (TOEIC + IELTS MCQ variants such as TRUE/FALSE/NOT GIVEN).
|
|
16
|
-
*/
|
|
17
|
-
const EC_NATIVE_MCQ_TYPES = new Set(['CHOOSE_THE_CORRECT_ANSWER']);
|
|
18
|
-
/**
|
|
19
|
-
* Dispatcher for English Certification question rendering based on part config.
|
|
20
|
-
*
|
|
21
|
-
* - CHOOSE_THE_CORRECT_ANSWER → branded EC MCQ list / grouped split-view.
|
|
22
|
-
* - DOCUMENT-group Reading parts → branded split-view (passage + questions).
|
|
23
|
-
* - All other IELTS types are rendered by DEDICATED branded EC renderers
|
|
24
|
-
* (full-width, structured to match TOEIC) — never delegated to the shared
|
|
25
|
-
* Movers renderer, which squeezed IELTS tables into narrow columns.
|
|
26
|
-
*/
|
|
27
|
-
export function EnglishCertificationQuestionRenderer({ part, partConfig, answers, flaggedQuestions, onAnswerChange, onToggleFlag, isReviewMode = false, }) {
|
|
28
|
-
if (!part.questions.length) {
|
|
29
|
-
return (_jsx("div", { className: "flex h-full items-center justify-center rounded-xl border border-red-200 bg-red-50 p-8 text-red-600", children: "No questions found for this part" }));
|
|
30
|
-
}
|
|
31
|
-
// Part-level audio (Listening sections always show the audio bar so students
|
|
32
|
-
// see the player even before the admin uploads the audio file).
|
|
33
|
-
const isListeningPart = partConfig.category === SkillEnum.LISTENING;
|
|
34
|
-
const partAudioUrl = isListeningPart
|
|
35
|
-
? part.audioUrl ?? partConfig.audioUrl ?? ''
|
|
36
|
-
: undefined;
|
|
37
|
-
const questionType = part.questionType;
|
|
38
|
-
// Reading parts with a DOCUMENT passage (TOEIC Part 6/7 and IELTS Reading)
|
|
39
|
-
// use the branded split view: passage on the left, all questions on the
|
|
40
|
-
// right. IELTS Reading mixes sub-section types, handled inside the renderer.
|
|
41
|
-
const hasDocumentGroup = Boolean(part.isGroup) && part.group?.type === 'DOCUMENT';
|
|
42
|
-
const isGroupedMcq = partConfig.isGroup && EC_NATIVE_MCQ_TYPES.has(questionType);
|
|
43
|
-
// Mixed multi-section parts (e.g. Listening SECTION 2) are kept whole by
|
|
44
|
-
// groupQuestionsByPart with their sections attached. Route them to the
|
|
45
|
-
// grouped renderer, which stacks each sub-section via ReadingSection.
|
|
46
|
-
const hasSections = Boolean(part.sections?.length);
|
|
47
|
-
if (hasDocumentGroup || isGroupedMcq || hasSections) {
|
|
48
|
-
return (_jsx("div", { className: "h-full", children: _jsx(EnglishCertificationGroupedQuestion, { part: part, partConfig: partConfig, answers: answers, flaggedQuestions: flaggedQuestions, onAnswerChange: onAnswerChange, onToggleFlag: onToggleFlag, isReviewMode: isReviewMode, isListeningPart: isListeningPart, partAudioUrl: partAudioUrl }) }));
|
|
49
|
-
}
|
|
50
|
-
// Native branded MCQ list (non-grouped CHOOSE_THE_CORRECT_ANSWER).
|
|
51
|
-
if (EC_NATIVE_MCQ_TYPES.has(questionType)) {
|
|
52
|
-
return (_jsx(EnglishCertificationSingleQuestionList, { part: part, partConfig: partConfig, answers: answers, flaggedQuestions: flaggedQuestions, onAnswerChange: onAnswerChange, onToggleFlag: onToggleFlag, isReviewMode: isReviewMode, isListeningPart: isListeningPart, partAudioUrl: partAudioUrl }));
|
|
53
|
-
}
|
|
54
|
-
// Dedicated branded EC renderers for the remaining IELTS question types.
|
|
55
|
-
const renderDedicated = () => {
|
|
56
|
-
switch (questionType) {
|
|
57
|
-
case 'WORD_FILL_STRUCTURED_FORM':
|
|
58
|
-
return (_jsx(EcWordFillStructuredForm, { part: part, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
59
|
-
case 'MATCH_BY_WRITING_ANSWER':
|
|
60
|
-
return (_jsx(EcMatchByWritingAnswer, { part: part, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
61
|
-
case 'ANSWER_THE_QUESTION':
|
|
62
|
-
return (_jsx(EcAnswerTheQuestion, { part: part, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
63
|
-
case 'WORD_FILL_PARAGRAPH':
|
|
64
|
-
return (_jsx(EcWordFillParagraph, { part: part, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
65
|
-
case 'WRITE_A_SHORT_LETTER':
|
|
66
|
-
return (_jsx(EcWritingTask, { part: part, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
67
|
-
default:
|
|
68
|
-
return (_jsxs("div", { className: "rounded-xl border border-amber-200 bg-amber-50 p-6 text-sm text-amber-700", children: ["Lo\u1EA1i c\u00E2u h\u1ECFi \"", questionType, "\" ch\u01B0a \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3 trong giao di\u1EC7n n\u00E0y."] }));
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
return (_jsxs("div", { className: "flex flex-col items-center gap-5 pb-4", children: [isListeningPart && (_jsx("div", { className: ENGLISH_CERT_CONTENT_WIDTH_CLASS, children: _jsx(EnglishCertificationAudioPlayer, { audioUrl: partAudioUrl ?? '', partNumber: part.partNo }) })), _jsx("div", { className: `${ENGLISH_CERT_CONTENT_WIDTH_CLASS} w-full`, children: renderDedicated() })] }));
|
|
72
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{SkillEnum as i}from"../../../shared/types/common.types";import{EnglishCertificationSingleQuestionList as n}from"./EnglishCertificationSingleQuestionList";import{EnglishCertificationGroupedQuestion as o}from"./EnglishCertificationGroupedQuestion";import{EnglishCertificationAudioPlayer as s}from"./EnglishCertificationAudioPlayer";import{ENGLISH_CERT_CONTENT_WIDTH_CLASS as t}from"./english-certification-theme";import{EcWordFillStructuredForm as a}from"./EcWordFillStructuredForm";import{EcMatchByWritingAnswer as l}from"./EcMatchByWritingAnswer";import{EcAnswerTheQuestion as d}from"./EcAnswerTheQuestion";import{EcWordFillParagraph as c}from"./EcWordFillParagraph";import{EcWritingTask as g}from"./EcWritingTask";const u=new Set(["CHOOSE_THE_CORRECT_ANSWER"]);export function EnglishCertificationQuestionRenderer({part:p,partConfig:m,answers:f,flaggedQuestions:h,onAnswerChange:w,onToggleFlag:R,isReviewMode:E=!1}){if(!p.questions.length)return e("div",{className:"flex h-full items-center justify-center rounded-xl border border-red-200 bg-red-50 p-8 text-red-600",children:"No questions found for this part"});const A=m.category===i.LISTENING,C=A?p.audioUrl??m.audioUrl??"":void 0,T=p.questionType,N=Boolean(p.isGroup)&&"DOCUMENT"===p.group?.type,_=m.isGroup&&u.has(T),v=Boolean(p.sections?.length);if(N||_||v)return e("div",{className:"h-full",children:e(o,{part:p,partConfig:m,answers:f,flaggedQuestions:h,onAnswerChange:w,onToggleFlag:R,isReviewMode:E,isListeningPart:A,partAudioUrl:C})});if(u.has(T))return e(n,{part:p,partConfig:m,answers:f,flaggedQuestions:h,onAnswerChange:w,onToggleFlag:R,isReviewMode:E,isListeningPart:A,partAudioUrl:C});return r("div",{className:"flex flex-col items-center gap-5 pb-4",children:[A&&e("div",{className:t,children:e(s,{audioUrl:C??"",partNumber:p.partNo})}),e("div",{className:`${t} w-full`,children:(()=>{switch(T){case"WORD_FILL_STRUCTURED_FORM":return e(a,{part:p,answers:f,onAnswerChange:w,isReviewMode:E});case"MATCH_BY_WRITING_ANSWER":return e(l,{part:p,answers:f,onAnswerChange:w,isReviewMode:E});case"ANSWER_THE_QUESTION":return e(d,{part:p,answers:f,onAnswerChange:w,isReviewMode:E});case"WORD_FILL_PARAGRAPH":return e(c,{part:p,answers:f,onAnswerChange:w,isReviewMode:E});case"WRITE_A_SHORT_LETTER":return e(g,{part:p,answers:f,onAnswerChange:w,isReviewMode:E});default:return r("div",{className:"rounded-xl border border-amber-200 bg-amber-50 p-6 text-sm text-amber-700",children:['Loại câu hỏi "',T,'" chưa được hỗ trợ trong giao diện này.']})}})()})]})}
|
|
@@ -1,90 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { EnglishCertificationOptionSelector } from './EnglishCertificationOptionSelector';
|
|
4
|
-
import { EnglishCertificationFlagButton } from './EnglishCertificationFlagButton';
|
|
5
|
-
import { EcWordFillStructuredForm } from './EcWordFillStructuredForm';
|
|
6
|
-
import { EcMatchByWritingAnswer } from './EcMatchByWritingAnswer';
|
|
7
|
-
import { EcAnswerTheQuestion } from './EcAnswerTheQuestion';
|
|
8
|
-
import { EcWordFillParagraph } from './EcWordFillParagraph';
|
|
9
|
-
import { EcWritingTask } from './EcWritingTask';
|
|
10
|
-
/**
|
|
11
|
-
* Maps a question's options to the selector shape.
|
|
12
|
-
*
|
|
13
|
-
* Handles three shapes:
|
|
14
|
-
* - `{ id, text }` objects (standard MCQ options).
|
|
15
|
-
* - plain strings from `section.defaultValue` (IELTS TRUE/FALSE/NOT GIVEN),
|
|
16
|
-
* where the string itself is both the stored value and the displayed text.
|
|
17
|
-
*/
|
|
18
|
-
function mapOptions(rawOptions) {
|
|
19
|
-
return rawOptions.map((option, optIdx) => {
|
|
20
|
-
const letter = String.fromCharCode(65 + optIdx);
|
|
21
|
-
if (typeof option === 'string') {
|
|
22
|
-
// IELTS TRUE/FALSE/NOT GIVEN — store the string as the option id.
|
|
23
|
-
return { id: option, label: letter, text: option };
|
|
24
|
-
}
|
|
25
|
-
return {
|
|
26
|
-
id: String(option?.id || letter),
|
|
27
|
-
label: String(option?.id || letter),
|
|
28
|
-
text: String(option?.text ?? option?.value ?? option ?? ''),
|
|
29
|
-
};
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Renders one sub-section of an IELTS Reading part inside the right-hand
|
|
34
|
-
* question column.
|
|
35
|
-
*
|
|
36
|
-
* - CHOOSE_THE_CORRECT_ANSWER (incl. TRUE/FALSE/NOT GIVEN) → branded MCQ cards.
|
|
37
|
-
* - Any other type (ANSWER_THE_QUESTION, WORD_FILL_PARAGRAPH,
|
|
38
|
-
* WORD_FILL_STRUCTURED_FORM, MATCH_BY_WRITING_ANSWER, …) is delegated to the
|
|
39
|
-
* shared TOEIC-style renderer so the proven transformers/answer shapes apply.
|
|
40
|
-
*/
|
|
41
|
-
export function EnglishCertificationReadingSection({ part, section, questions, startIndex, answers, flaggedQuestions, onAnswerChange, onToggleFlag, isReviewMode = false, }) {
|
|
42
|
-
if (!questions.length)
|
|
43
|
-
return null;
|
|
44
|
-
const sectionInstruction = (section.instructions || section.name || '').trim();
|
|
45
|
-
const isMcq = section.questionType === 'CHOOSE_THE_CORRECT_ANSWER';
|
|
46
|
-
if (isMcq) {
|
|
47
|
-
return (_jsxs("div", { className: "flex flex-col gap-3", children: [sectionInstruction && (_jsx("p", { className: "text-sm font-semibold leading-relaxed text-[#0b57d0]", children: sectionInstruction })), _jsx("div", { className: "divide-y divide-[#e8eef7]", children: questions.map((question) => {
|
|
48
|
-
const globalNumber = part.questions.indexOf(question) + startIndex + 1;
|
|
49
|
-
const content = question.content || {};
|
|
50
|
-
const rawOptions = Array.isArray(content.options)
|
|
51
|
-
? content.options
|
|
52
|
-
: section.defaultValue ?? [];
|
|
53
|
-
const questionText = content.question?.trim() ?? '';
|
|
54
|
-
const answer = answers[question.id];
|
|
55
|
-
return (_jsxs("article", { className: "grid grid-cols-[44px_1fr_32px] gap-1 py-4", children: [_jsxs("div", { className: "text-[18px] font-black leading-snug text-[#0b57d0]", children: [globalNumber, "."] }), _jsxs("div", { className: "min-w-0", children: [questionText && (_jsx("div", { className: "mb-2 text-[15px] font-black leading-snug text-[#10172f]", children: questionText })), _jsx(EnglishCertificationOptionSelector, { options: mapOptions(rawOptions), selectedOptionId: answer?.optionId ?? null, onSelect: (optionId) => onAnswerChange(question.id, { optionId }), isReviewMode: isReviewMode, disabled: isReviewMode, variant: "compact" })] }), _jsx(EnglishCertificationFlagButton, { isFlagged: flaggedQuestions.has(question.id), onToggle: () => onToggleFlag(question.id), size: "sm", className: "self-start justify-self-end" })] }, question.id));
|
|
56
|
-
}) })] }));
|
|
57
|
-
}
|
|
58
|
-
// Build a synthetic sub-part carrying only this section's questions so the
|
|
59
|
-
// dedicated branded EC renderers can transform/render it standalone.
|
|
60
|
-
const subPart = {
|
|
61
|
-
...part,
|
|
62
|
-
questionType: section.questionType,
|
|
63
|
-
questions,
|
|
64
|
-
instructions: sectionInstruction || part.instructions,
|
|
65
|
-
isGroup: false,
|
|
66
|
-
group: undefined,
|
|
67
|
-
sections: undefined,
|
|
68
|
-
viewMode: section.viewMode,
|
|
69
|
-
wordBankConfig: section.wordBankConfig,
|
|
70
|
-
startIndex: section.startIndex,
|
|
71
|
-
endIndex: section.endIndex,
|
|
72
|
-
};
|
|
73
|
-
const renderSubSection = () => {
|
|
74
|
-
switch (section.questionType) {
|
|
75
|
-
case 'WORD_FILL_STRUCTURED_FORM':
|
|
76
|
-
return (_jsx(EcWordFillStructuredForm, { part: subPart, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
77
|
-
case 'MATCH_BY_WRITING_ANSWER':
|
|
78
|
-
return (_jsx(EcMatchByWritingAnswer, { part: subPart, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
79
|
-
case 'ANSWER_THE_QUESTION':
|
|
80
|
-
return (_jsx(EcAnswerTheQuestion, { part: subPart, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
81
|
-
case 'WORD_FILL_PARAGRAPH':
|
|
82
|
-
return (_jsx(EcWordFillParagraph, { part: subPart, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
83
|
-
case 'WRITE_A_SHORT_LETTER':
|
|
84
|
-
return (_jsx(EcWritingTask, { part: subPart, answers: answers, onAnswerChange: onAnswerChange, isReviewMode: isReviewMode }));
|
|
85
|
-
default:
|
|
86
|
-
return (_jsxs("div", { className: "rounded-xl border border-amber-200 bg-amber-50 p-4 text-sm text-amber-700", children: ["Lo\u1EA1i c\u00E2u h\u1ECFi \"", section.questionType, "\" ch\u01B0a \u0111\u01B0\u1EE3c h\u1ED7 tr\u1EE3."] }));
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
return (_jsxs("div", { className: "flex flex-col gap-3", children: [sectionInstruction && (_jsx("p", { className: "text-sm font-semibold leading-relaxed text-[#0b57d0]", children: sectionInstruction })), renderSubSection()] }));
|
|
90
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as i}from"react/jsx-runtime";import{EnglishCertificationOptionSelector as t}from"./EnglishCertificationOptionSelector";import{EnglishCertificationFlagButton as n}from"./EnglishCertificationFlagButton";import{EcWordFillStructuredForm as r}from"./EcWordFillStructuredForm";import{EcMatchByWritingAnswer as s}from"./EcMatchByWritingAnswer";import{EcAnswerTheQuestion as o}from"./EcAnswerTheQuestion";import{EcWordFillParagraph as a}from"./EcWordFillParagraph";import{EcWritingTask as d}from"./EcWritingTask";function l(e){return e.map((e,i)=>{const t=String.fromCharCode(65+i);return"string"==typeof e?{id:e,label:t,text:e}:{id:String(e?.id||t),label:String(e?.id||t),text:String(e?.text??e?.value??e??"")}})}export function EnglishCertificationReadingSection({part:c,section:m,questions:u,startIndex:p,answers:g,flaggedQuestions:f,onAnswerChange:x,onToggleFlag:h,isReviewMode:w=!1}){if(!u.length)return null;const R=(m.instructions||m.name||"").trim();if("CHOOSE_THE_CORRECT_ANSWER"===m.questionType)return i("div",{className:"flex flex-col gap-3",children:[R&&e("p",{className:"text-sm font-semibold leading-relaxed text-[#0b57d0]",children:R}),e("div",{className:"divide-y divide-[#e8eef7]",children:u.map(r=>{const s=c.questions.indexOf(r)+p+1,o=r.content||{},a=Array.isArray(o.options)?o.options:m.defaultValue??[],d=o.question?.trim()??"",u=g[r.id];return i("article",{className:"grid grid-cols-[44px_1fr_32px] gap-1 py-4",children:[i("div",{className:"text-[18px] font-black leading-snug text-[#0b57d0]",children:[s,"."]}),i("div",{className:"min-w-0",children:[d&&e("div",{className:"mb-2 text-[15px] font-black leading-snug text-[#10172f]",children:d}),e(t,{options:l(a),selectedOptionId:u?.optionId??null,onSelect:e=>x(r.id,{optionId:e}),isReviewMode:w,disabled:w,variant:"compact"})]}),e(n,{isFlagged:f.has(r.id),onToggle:()=>h(r.id),size:"sm",className:"self-start justify-self-end"})]},r.id)})})]});const v={...c,questionType:m.questionType,questions:u,instructions:R||c.instructions,isGroup:!1,group:void 0,sections:void 0,viewMode:m.viewMode,wordBankConfig:m.wordBankConfig,startIndex:m.startIndex,endIndex:m.endIndex};return i("div",{className:"flex flex-col gap-3",children:[R&&e("p",{className:"text-sm font-semibold leading-relaxed text-[#0b57d0]",children:R}),(()=>{switch(m.questionType){case"WORD_FILL_STRUCTURED_FORM":return e(r,{part:v,answers:g,onAnswerChange:x,isReviewMode:w});case"MATCH_BY_WRITING_ANSWER":return e(s,{part:v,answers:g,onAnswerChange:x,isReviewMode:w});case"ANSWER_THE_QUESTION":return e(o,{part:v,answers:g,onAnswerChange:x,isReviewMode:w});case"WORD_FILL_PARAGRAPH":return e(a,{part:v,answers:g,onAnswerChange:x,isReviewMode:w});case"WRITE_A_SHORT_LETTER":return e(d,{part:v,answers:g,onAnswerChange:x,isReviewMode:w});default:return i("div",{className:"rounded-xl border border-amber-200 bg-amber-50 p-4 text-sm text-amber-700",children:['Loại câu hỏi "',m.questionType,'" chưa được hỗ trợ.']})}})()]})}
|
|
@@ -1,18 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { EnglishCertificationAudioPlayer } from './EnglishCertificationAudioPlayer';
|
|
4
|
-
import { EnglishCertificationQuestionItem } from './EnglishCertificationQuestionItem';
|
|
5
|
-
import { ENGLISH_CERT_CONTENT_WIDTH_CLASS } from './english-certification-theme';
|
|
6
|
-
/**
|
|
7
|
-
* Ocean Edu branded renderer for a single (non-grouped) question.
|
|
8
|
-
*
|
|
9
|
-
* Thin wrapper around {@link EnglishCertificationQuestionItem} that prepends the
|
|
10
|
-
* shared audio bar for listening parts. Kept for review mode and any
|
|
11
|
-
* one-question-per-screen flow; multi-question screens use the question list.
|
|
12
|
-
*/
|
|
13
|
-
export function EnglishCertificationSingleQuestion({ question, questionNumber, questionConfig, defaultQuestion, partAudioUrl, isListeningPart = false, answer, isFlagged, onAnswerChange, onToggleFlag, isReviewMode = false, }) {
|
|
14
|
-
// Resolve effective audio: prefer the question's own audio, otherwise fall
|
|
15
|
-
// back to the part-level audio (TOEIC Listening Part 1/2 define audio per part).
|
|
16
|
-
const effectiveAudioUrl = question.audio_url || partAudioUrl || '';
|
|
17
|
-
return (_jsxs("div", { className: "flex flex-col items-center gap-5", children: [effectiveAudioUrl && (_jsx("div", { className: ENGLISH_CERT_CONTENT_WIDTH_CLASS, children: _jsx(EnglishCertificationAudioPlayer, { audioUrl: effectiveAudioUrl, partNumber: question.part_no }) })), _jsx(EnglishCertificationQuestionItem, { question: question, questionNumber: questionNumber, questionConfig: questionConfig, defaultQuestion: defaultQuestion, isListeningPart: isListeningPart, answer: answer, isFlagged: isFlagged, onAnswerChange: onAnswerChange, onToggleFlag: onToggleFlag, isReviewMode: isReviewMode })] }));
|
|
18
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as i}from"react/jsx-runtime";import{EnglishCertificationAudioPlayer as n}from"./EnglishCertificationAudioPlayer";import{EnglishCertificationQuestionItem as t}from"./EnglishCertificationQuestionItem";import{ENGLISH_CERT_CONTENT_WIDTH_CLASS as o}from"./english-certification-theme";export function EnglishCertificationSingleQuestion({question:s,questionNumber:r,questionConfig:a,defaultQuestion:l,partAudioUrl:u,isListeningPart:g=!1,answer:m,isFlagged:c,onAnswerChange:f,onToggleFlag:d,isReviewMode:h=!1}){const p=s.audio_url||u||"";return i("div",{className:"flex flex-col items-center gap-5",children:[p&&e("div",{className:o,children:e(n,{audioUrl:p,partNumber:s.part_no})}),e(t,{question:s,questionNumber:r,questionConfig:a,defaultQuestion:l,isListeningPart:g,answer:m,isFlagged:c,onAnswerChange:f,onToggleFlag:d,isReviewMode:h})]})}
|
package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js
CHANGED
|
@@ -1,29 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { EnglishCertificationAudioPlayer } from './EnglishCertificationAudioPlayer';
|
|
4
|
-
import { EnglishCertificationQuestionItem } from './EnglishCertificationQuestionItem';
|
|
5
|
-
import { ENGLISH_CERT_CONTENT_WIDTH_CLASS } from './english-certification-theme';
|
|
6
|
-
/** Stable DOM id for a question card, used for Answer-Sheet scroll-to. */
|
|
7
|
-
export function englishCertQuestionDomId(questionId) {
|
|
8
|
-
return `ec-question-${questionId}`;
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Renders every question of a single-question part (or section) on one screen.
|
|
12
|
-
*
|
|
13
|
-
* Listening parts show a single shared audio bar at the top; all questions then
|
|
14
|
-
* stack vertically. Navigation across parts/sections is handled by the footer.
|
|
15
|
-
*/
|
|
16
|
-
export function EnglishCertificationSingleQuestionList({ part, partConfig, answers, flaggedQuestions, onAnswerChange, onToggleFlag, isReviewMode = false, isListeningPart = false, partAudioUrl, }) {
|
|
17
|
-
const startIndex = partConfig.startIndex ?? 0;
|
|
18
|
-
const questionConfig = Array.isArray(partConfig.questionConfig)
|
|
19
|
-
? partConfig.questionConfig
|
|
20
|
-
: [];
|
|
21
|
-
const defaultQuestion = partConfig.defaultQuestion;
|
|
22
|
-
const showAudio = isListeningPart && Boolean(partAudioUrl);
|
|
23
|
-
return (_jsxs("div", { className: "flex flex-col items-center gap-5 pb-4", children: [showAudio && (_jsx("div", { className: ENGLISH_CERT_CONTENT_WIDTH_CLASS, children: _jsx(EnglishCertificationAudioPlayer, { audioUrl: partAudioUrl, partNumber: part.partNo }) })), part.questions.map((question, idx) => {
|
|
24
|
-
const effectiveQuestionConfig = question.audio_url
|
|
25
|
-
? Array.from(new Set([...questionConfig, 'audioUrl']))
|
|
26
|
-
: questionConfig;
|
|
27
|
-
return (_jsx(EnglishCertificationQuestionItem, { domId: englishCertQuestionDomId(question.id), question: question, questionNumber: startIndex + idx + 1, questionConfig: effectiveQuestionConfig, defaultQuestion: defaultQuestion, isListeningPart: isListeningPart, answer: answers[question.id], isFlagged: flaggedQuestions.has(question.id), onAnswerChange: onAnswerChange, onToggleFlag: onToggleFlag, isReviewMode: isReviewMode }, question.id));
|
|
28
|
-
})] }));
|
|
29
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as i}from"react/jsx-runtime";import{EnglishCertificationAudioPlayer as n}from"./EnglishCertificationAudioPlayer";import{EnglishCertificationQuestionItem as t}from"./EnglishCertificationQuestionItem";import{ENGLISH_CERT_CONTENT_WIDTH_CLASS as o}from"./english-certification-theme";export function englishCertQuestionDomId(e){return`ec-question-${e}`}export function EnglishCertificationSingleQuestionList({part:r,partConfig:s,answers:a,flaggedQuestions:u,onAnswerChange:l,onToggleFlag:g,isReviewMode:d=!1,isListeningPart:m=!1,partAudioUrl:f}){const c=s.startIndex??0,p=Array.isArray(s.questionConfig)?s.questionConfig:[],h=s.defaultQuestion,C=m&&Boolean(f);return i("div",{className:"flex flex-col items-center gap-5 pb-4",children:[C&&e("div",{className:o,children:e(n,{audioUrl:f,partNumber:r.partNo})}),r.questions.map((i,n)=>{const o=i.audio_url?Array.from(new Set([...p,"audioUrl"])):p;return e(t,{domId:englishCertQuestionDomId(i.id),question:i,questionNumber:c+n+1,questionConfig:o,defaultQuestion:h,isListeningPart:m,answer:a[i.id],isFlagged:u.has(i.id),onAnswerChange:l,onToggleFlag:g,isReviewMode:d},i.id)})]})}
|
|
@@ -1,58 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* English Certification theme — shared design tokens and reusable class fragments.
|
|
3
|
-
*
|
|
4
|
-
* A branded Ocean Edu look shared across standardized English certification
|
|
5
|
-
* exams (TOEIC, IELTS, …). Centralises colours, radii, shadows and branded SVG
|
|
6
|
-
* assets so the look stays consistent and easy to tune.
|
|
7
|
-
*/
|
|
8
|
-
/** Brand palette (hex) reused by inline styles and gradients. */
|
|
9
|
-
export const ENGLISH_CERT_COLORS = {
|
|
10
|
-
blue950: '#061a55',
|
|
11
|
-
blue900: '#072b7a',
|
|
12
|
-
blue800: '#0a3d9a',
|
|
13
|
-
blue700: '#0b57d0',
|
|
14
|
-
blue600: '#1167e8',
|
|
15
|
-
blue500: '#1f7cff',
|
|
16
|
-
blue100: '#e9f3ff',
|
|
17
|
-
blue50: '#f4f9ff',
|
|
18
|
-
orange600: '#f97316',
|
|
19
|
-
orange500: '#ff8a00',
|
|
20
|
-
orange100: '#fff1db',
|
|
21
|
-
green500: '#22c55e',
|
|
22
|
-
green100: '#ddf8e7',
|
|
23
|
-
red500: '#ef4444',
|
|
24
|
-
text: '#0f1730',
|
|
25
|
-
muted: '#64748b',
|
|
26
|
-
border: '#d9e4f2',
|
|
27
|
-
};
|
|
28
|
-
/** Fixed layout dimensions (px). */
|
|
29
|
-
export const ENGLISH_CERT_LAYOUT = {
|
|
30
|
-
headerHeight: 74,
|
|
31
|
-
footerHeight: 76,
|
|
32
|
-
radius: 12,
|
|
33
|
-
/** Shared max content width (px) for question card, image, audio bar and answers. */
|
|
34
|
-
contentMaxWidth: 940,
|
|
35
|
-
};
|
|
36
|
-
/**
|
|
37
|
-
* Shared content-width class fragment (Tailwind).
|
|
38
|
-
* Centres the inner content blocks (image, audio bar, answer grid) so they line
|
|
39
|
-
* up consistently across every question screen.
|
|
40
|
-
*/
|
|
41
|
-
export const ENGLISH_CERT_CONTENT_WIDTH_CLASS = 'mx-auto w-full max-w-[940px]';
|
|
42
|
-
/**
|
|
43
|
-
* Full-page gradient background applied to the app shell.
|
|
44
|
-
* Combines two soft radial highlights with a vertical base gradient.
|
|
45
|
-
*/
|
|
46
|
-
export const ENGLISH_CERT_APP_BACKGROUND = 'radial-gradient(circle at 20% -10%, rgba(31,124,255,0.13), transparent 28%),' +
|
|
47
|
-
'radial-gradient(circle at 70% 0%, rgba(0,184,255,0.13), transparent 24%),' +
|
|
48
|
-
'linear-gradient(180deg, #f9fdff 0%, #edf7ff 52%, #f9fdff 100%)';
|
|
49
|
-
/** Reusable glass-card class fragment (Tailwind). */
|
|
50
|
-
export const ENGLISH_CERT_CARD_CLASS = 'rounded-xl border border-[#d9e4f2] bg-white/90 shadow-[0_14px_35px_rgba(13,71,161,0.12)] backdrop-blur-md';
|
|
51
|
-
/** Branded gradient used for the answer-sheet header and primary actions. */
|
|
52
|
-
export const ENGLISH_CERT_PRIMARY_GRADIENT = 'linear-gradient(180deg, #1f7cff, #0b57d0)';
|
|
53
|
-
/**
|
|
54
|
-
* Decorative footer wave (URL-encoded SVG) shown along the bottom action bar.
|
|
55
|
-
*/
|
|
56
|
-
export const ENGLISH_CERT_FOOTER_WAVE = "url(\"data:image/svg+xml,%3Csvg width='1600' height='80' viewBox='0 0 1600 80' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 44C190 20 334 28 520 44C746 64 850 15 1070 34C1258 50 1408 38 1600 28V80H0V44Z' fill='%230B57D0'/%3E%3Cpath d='M0 50C230 26 346 50 540 55C760 61 872 26 1088 45C1280 62 1430 40 1600 38V80H0V50Z' fill='%23051A55' fill-opacity='0.35'/%3E%3C/svg%3E\")";
|
|
57
|
-
/** Encouraging tagline shown in the branded footer. */
|
|
58
|
-
export const ENGLISH_CERT_FOOTER_NOTE = 'Believe in yourself. You are stronger than you think.';
|
|
1
|
+
export const ENGLISH_CERT_COLORS={blue950:"#061a55",blue900:"#072b7a",blue800:"#0a3d9a",blue700:"#0b57d0",blue600:"#1167e8",blue500:"#1f7cff",blue100:"#e9f3ff",blue50:"#f4f9ff",orange600:"#f97316",orange500:"#ff8a00",orange100:"#fff1db",green500:"#22c55e",green100:"#ddf8e7",red500:"#ef4444",text:"#0f1730",muted:"#64748b",border:"#d9e4f2"};export const ENGLISH_CERT_LAYOUT={headerHeight:74,footerHeight:76,radius:12,contentMaxWidth:940};export const ENGLISH_CERT_CONTENT_WIDTH_CLASS="mx-auto w-full max-w-[940px]";export const ENGLISH_CERT_APP_BACKGROUND="radial-gradient(circle at 20% -10%, rgba(31,124,255,0.13), transparent 28%),radial-gradient(circle at 70% 0%, rgba(0,184,255,0.13), transparent 24%),linear-gradient(180deg, #f9fdff 0%, #edf7ff 52%, #f9fdff 100%)";export const ENGLISH_CERT_CARD_CLASS="rounded-xl border border-[#d9e4f2] bg-white/90 shadow-[0_14px_35px_rgba(13,71,161,0.12)] backdrop-blur-md";export const ENGLISH_CERT_PRIMARY_GRADIENT="linear-gradient(180deg, #1f7cff, #0b57d0)";export const ENGLISH_CERT_FOOTER_WAVE="url(\"data:image/svg+xml,%3Csvg width='1600' height='80' viewBox='0 0 1600 80' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0 44C190 20 334 28 520 44C746 64 850 15 1070 34C1258 50 1408 38 1600 28V80H0V44Z' fill='%230B57D0'/%3E%3Cpath d='M0 50C230 26 346 50 540 55C760 61 872 26 1088 45C1280 62 1430 40 1600 38V80H0V50Z' fill='%23051A55' fill-opacity='0.35'/%3E%3C/svg%3E\")";export const ENGLISH_CERT_FOOTER_NOTE="Believe in yourself. You are stronger than you think.";
|