@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,111 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
// Validation schema for Choose the Correct Answer form
|
|
3
|
-
export const chooseCorrectAnswerFormSchema = z.object({
|
|
4
|
-
question: z.string().optional(),
|
|
5
|
-
optionType: z.enum(['text', 'image']).default('text'),
|
|
6
|
-
options: z
|
|
7
|
-
.array(z.string())
|
|
8
|
-
.min(2, 'Phải có ít nhất 2 đáp án!'),
|
|
9
|
-
correctAnswer: z.union([z.number(), z.array(z.number())]).default(-1),
|
|
10
|
-
multipleAnswers: z.boolean().default(false),
|
|
11
|
-
imageUrl: z.string().optional(),
|
|
12
|
-
explanation: z.string().optional(),
|
|
13
|
-
askerName: z.string().optional(),
|
|
14
|
-
answererName: z.string().optional(),
|
|
15
|
-
isExample: z.boolean().default(false),
|
|
16
|
-
points: z
|
|
17
|
-
.union([z.string(), z.number()])
|
|
18
|
-
.transform((val, ctx) => {
|
|
19
|
-
if (val === '' || val === undefined || val === null) {
|
|
20
|
-
ctx.addIssue({
|
|
21
|
-
code: z.ZodIssueCode.custom,
|
|
22
|
-
message: 'Vui lòng nhập điểm số!',
|
|
23
|
-
});
|
|
24
|
-
return z.NEVER;
|
|
25
|
-
}
|
|
26
|
-
const num = typeof val === 'string' ? parseInt(val, 10) : val;
|
|
27
|
-
if (isNaN(num)) {
|
|
28
|
-
ctx.addIssue({
|
|
29
|
-
code: z.ZodIssueCode.custom,
|
|
30
|
-
message: 'Vui lòng nhập điểm số hợp lệ!',
|
|
31
|
-
});
|
|
32
|
-
return z.NEVER;
|
|
33
|
-
}
|
|
34
|
-
return num;
|
|
35
|
-
})
|
|
36
|
-
.pipe(z.number().min(1, 'Điểm số phải >= 1')),
|
|
37
|
-
}).superRefine((data, ctx) => {
|
|
38
|
-
const filledOptions = data.options.filter((opt) => opt.trim() !== '');
|
|
39
|
-
if (filledOptions.length < 2) {
|
|
40
|
-
ctx.addIssue({
|
|
41
|
-
code: z.ZodIssueCode.custom,
|
|
42
|
-
message: data.optionType === 'image'
|
|
43
|
-
? 'Phải upload ít nhất 2 hình ảnh đáp án!'
|
|
44
|
-
: 'Phải có ít nhất 2 đáp án được điền!',
|
|
45
|
-
path: ['options'],
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
// Validate each option based on type
|
|
49
|
-
data.options.forEach((opt, index) => {
|
|
50
|
-
if (opt.trim() === '') {
|
|
51
|
-
ctx.addIssue({
|
|
52
|
-
code: z.ZodIssueCode.custom,
|
|
53
|
-
message: data.optionType === 'image'
|
|
54
|
-
? 'Vui lòng upload hình ảnh!'
|
|
55
|
-
: 'Vui lòng nhập đáp án!',
|
|
56
|
-
path: ['options', index],
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
if (data.multipleAnswers) {
|
|
61
|
-
const answers = Array.isArray(data.correctAnswer)
|
|
62
|
-
? data.correctAnswer
|
|
63
|
-
: data.correctAnswer === -1
|
|
64
|
-
? []
|
|
65
|
-
: [data.correctAnswer];
|
|
66
|
-
if (answers.length === 0) {
|
|
67
|
-
ctx.addIssue({
|
|
68
|
-
code: z.ZodIssueCode.custom,
|
|
69
|
-
message: 'Vui lòng chọn ít nhất 1 đáp án đúng!',
|
|
70
|
-
path: ['correctAnswer'],
|
|
71
|
-
});
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
const normalizedAnswers = [...new Set(answers)];
|
|
75
|
-
if (normalizedAnswers.length !== answers.length) {
|
|
76
|
-
ctx.addIssue({
|
|
77
|
-
code: z.ZodIssueCode.custom,
|
|
78
|
-
message: 'Đáp án đúng không được trùng lặp!',
|
|
79
|
-
path: ['correctAnswer'],
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
normalizedAnswers.forEach((answer) => {
|
|
83
|
-
if (answer < 0 || answer >= data.options.length) {
|
|
84
|
-
ctx.addIssue({
|
|
85
|
-
code: z.ZodIssueCode.custom,
|
|
86
|
-
message: 'Đáp án đúng không hợp lệ!',
|
|
87
|
-
path: ['correctAnswer'],
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
return;
|
|
92
|
-
}
|
|
93
|
-
const singleAnswer = Array.isArray(data.correctAnswer)
|
|
94
|
-
? (data.correctAnswer[0] ?? -1)
|
|
95
|
-
: data.correctAnswer;
|
|
96
|
-
if (singleAnswer === -1) {
|
|
97
|
-
ctx.addIssue({
|
|
98
|
-
code: z.ZodIssueCode.custom,
|
|
99
|
-
message: 'Vui lòng chọn đáp án đúng!',
|
|
100
|
-
path: ['correctAnswer'],
|
|
101
|
-
});
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
if (singleAnswer < 0 || singleAnswer >= data.options.length) {
|
|
105
|
-
ctx.addIssue({
|
|
106
|
-
code: z.ZodIssueCode.custom,
|
|
107
|
-
message: 'Đáp án đúng không hợp lệ!',
|
|
108
|
-
path: ['correctAnswer'],
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
});
|
|
1
|
+
import{z as e}from"zod";export const chooseCorrectAnswerFormSchema=e.object({question:e.string().optional(),optionType:e.enum(["text","image"]).default("text"),options:e.array(e.string()).min(2,"Phải có ít nhất 2 đáp án!"),correctAnswer:e.union([e.number(),e.array(e.number())]).default(-1),multipleAnswers:e.boolean().default(!1),imageUrl:e.string().optional(),explanation:e.string().optional(),askerName:e.string().optional(),answererName:e.string().optional(),isExample:e.boolean().default(!1),points:e.union([e.string(),e.number()]).transform((s,n)=>{if(""===s||null==s)return n.addIssue({code:e.ZodIssueCode.custom,message:"Vui lòng nhập điểm số!"}),e.NEVER;const o="string"==typeof s?parseInt(s,10):s;return isNaN(o)?(n.addIssue({code:e.ZodIssueCode.custom,message:"Vui lòng nhập điểm số hợp lệ!"}),e.NEVER):o}).pipe(e.number().min(1,"Điểm số phải >= 1"))}).superRefine((s,n)=>{if(s.options.filter(e=>""!==e.trim()).length<2&&n.addIssue({code:e.ZodIssueCode.custom,message:"image"===s.optionType?"Phải upload ít nhất 2 hình ảnh đáp án!":"Phải có ít nhất 2 đáp án được điền!",path:["options"]}),s.options.forEach((o,t)=>{""===o.trim()&&n.addIssue({code:e.ZodIssueCode.custom,message:"image"===s.optionType?"Vui lòng upload hình ảnh!":"Vui lòng nhập đáp án!",path:["options",t]})}),s.multipleAnswers){const o=Array.isArray(s.correctAnswer)?s.correctAnswer:-1===s.correctAnswer?[]:[s.correctAnswer];if(0===o.length)return void n.addIssue({code:e.ZodIssueCode.custom,message:"Vui lòng chọn ít nhất 1 đáp án đúng!",path:["correctAnswer"]});const t=[...new Set(o)];return t.length!==o.length&&n.addIssue({code:e.ZodIssueCode.custom,message:"Đáp án đúng không được trùng lặp!",path:["correctAnswer"]}),void t.forEach(o=>{(o<0||o>=s.options.length)&&n.addIssue({code:e.ZodIssueCode.custom,message:"Đáp án đúng không hợp lệ!",path:["correctAnswer"]})})}const o=Array.isArray(s.correctAnswer)?s.correctAnswer[0]??-1:s.correctAnswer;-1!==o?(o<0||o>=s.options.length)&&n.addIssue({code:e.ZodIssueCode.custom,message:"Đáp án đúng không hợp lệ!",path:["correctAnswer"]}):n.addIssue({code:e.ZodIssueCode.custom,message:"Vui lòng chọn đáp án đúng!",path:["correctAnswer"]})});
|
|
@@ -1,19 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
// Label position validation schema
|
|
3
|
-
const labelPositionSchema = z.object({
|
|
4
|
-
id: z.string(),
|
|
5
|
-
number: z.number().min(1, 'Số thứ tự phải >= 1'),
|
|
6
|
-
correctAnswer: z.string().min(1, 'Vui lòng chọn đáp án đúng!'),
|
|
7
|
-
isPreFilled: z.boolean().optional(),
|
|
8
|
-
});
|
|
9
|
-
// Main form validation schema for Label the Picture
|
|
10
|
-
export const labelThePictureFormSchema = z.object({
|
|
11
|
-
imageUrl: z.string().min(1, 'Vui lòng upload hình ảnh!'),
|
|
12
|
-
wordBank: z
|
|
13
|
-
.array(z.string().min(1, 'Vui lòng nhập nội dung từ!'))
|
|
14
|
-
.min(1, 'Vui lòng thêm ít nhất một từ vào word bank!')
|
|
15
|
-
.refine((words) => words.every((word) => word.trim() !== ''), { message: 'Tất cả các từ trong word bank phải có giá trị!' }),
|
|
16
|
-
labels: z
|
|
17
|
-
.array(labelPositionSchema)
|
|
18
|
-
.min(1, 'Vui lòng thêm ít nhất một vị trí cần điền!'),
|
|
19
|
-
});
|
|
1
|
+
import{z as n}from"zod";const t=n.object({id:n.string(),number:n.number().min(1,"Số thứ tự phải >= 1"),correctAnswer:n.string().min(1,"Vui lòng chọn đáp án đúng!"),isPreFilled:n.boolean().optional()});export const labelThePictureFormSchema=n.object({imageUrl:n.string().min(1,"Vui lòng upload hình ảnh!"),wordBank:n.array(n.string().min(1,"Vui lòng nhập nội dung từ!")).min(1,"Vui lòng thêm ít nhất một từ vào word bank!").refine(n=>n.every(n=>""!==n.trim()),{message:"Tất cả các từ trong word bank phải có giá trị!"}),labels:n.array(t).min(1,"Vui lòng thêm ít nhất một vị trí cần điền!")});
|
|
@@ -1,46 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
/**
|
|
3
|
-
* LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP validation schema.
|
|
4
|
-
* Single question format:
|
|
5
|
-
* - options: at least 2 options, each with image (required) and label (optional)
|
|
6
|
-
* - audioUrl: audio file for the question
|
|
7
|
-
* - correctAnswer: index of the correct option
|
|
8
|
-
*/
|
|
9
|
-
export const listenAndChooseFromAnswerGroupOptionSchema = z.object({
|
|
10
|
-
id: z.string(),
|
|
11
|
-
imageUrl: z.string().min(1, 'Vui lòng upload ảnh cho đáp án!'),
|
|
12
|
-
label: z.string().optional(),
|
|
13
|
-
});
|
|
14
|
-
export const listenAndChooseFromAnswerGroupFormSchema = z.object({
|
|
15
|
-
options: z
|
|
16
|
-
.array(listenAndChooseFromAnswerGroupOptionSchema)
|
|
17
|
-
.min(2, 'Cần tối thiểu 2 đáp án trong nhóm!'),
|
|
18
|
-
audioUrl: z.string().min(1, 'Vui lòng upload file audio!'),
|
|
19
|
-
correctAnswer: z.string().min(1, 'Vui lòng chọn đáp án đúng!').refine((val) => val !== '-1', {
|
|
20
|
-
message: 'Vui lòng chọn đáp án đúng!',
|
|
21
|
-
}),
|
|
22
|
-
points: z
|
|
23
|
-
.union([z.string(), z.number()])
|
|
24
|
-
.transform((val, ctx) => {
|
|
25
|
-
if (val === '' || val === undefined || val === null) {
|
|
26
|
-
ctx.addIssue({
|
|
27
|
-
code: z.ZodIssueCode.custom,
|
|
28
|
-
message: 'Vui lòng nhập điểm!',
|
|
29
|
-
});
|
|
30
|
-
return z.NEVER;
|
|
31
|
-
}
|
|
32
|
-
const num = typeof val === 'string' ? parseInt(val, 10) : val;
|
|
33
|
-
if (isNaN(num)) {
|
|
34
|
-
ctx.addIssue({
|
|
35
|
-
code: z.ZodIssueCode.custom,
|
|
36
|
-
message: 'Vui lòng nhập điểm hợp lệ!',
|
|
37
|
-
});
|
|
38
|
-
return z.NEVER;
|
|
39
|
-
}
|
|
40
|
-
return num;
|
|
41
|
-
})
|
|
42
|
-
.pipe(z.number().min(1, 'Điểm phải >= 1'))
|
|
43
|
-
.optional(),
|
|
44
|
-
explanation: z.string().optional(),
|
|
45
|
-
isBackup: z.boolean().optional(),
|
|
46
|
-
});
|
|
1
|
+
import{z as n}from"zod";export const listenAndChooseFromAnswerGroupOptionSchema=n.object({id:n.string(),imageUrl:n.string().min(1,"Vui lòng upload ảnh cho đáp án!"),label:n.string().optional()});export const listenAndChooseFromAnswerGroupFormSchema=n.object({options:n.array(listenAndChooseFromAnswerGroupOptionSchema).min(2,"Cần tối thiểu 2 đáp án trong nhóm!"),audioUrl:n.string().min(1,"Vui lòng upload file audio!"),correctAnswer:n.string().min(1,"Vui lòng chọn đáp án đúng!").refine(n=>"-1"!==n,{message:"Vui lòng chọn đáp án đúng!"}),points:n.union([n.string(),n.number()]).transform((o,i)=>{if(""===o||null==o)return i.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm!"}),n.NEVER;const e="string"==typeof o?parseInt(o,10):o;return isNaN(e)?(i.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm hợp lệ!"}),n.NEVER):e}).pipe(n.number().min(1,"Điểm phải >= 1")).optional(),explanation:n.string().optional(),isBackup:n.boolean().optional()});
|
|
@@ -1,21 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
// Scene object node schema
|
|
3
|
-
const sceneObjectNodeSchema = z.object({
|
|
4
|
-
id: z.string(),
|
|
5
|
-
imageUrl: z.string().min(1, 'Vui lòng upload hình ảnh đối tượng!'),
|
|
6
|
-
label: z.string().min(1, 'Vui lòng nhập tên đối tượng!'),
|
|
7
|
-
x: z.number().min(0).max(100),
|
|
8
|
-
y: z.number().min(0).max(100),
|
|
9
|
-
width: z.number().min(1, 'Chiều rộng phải lớn hơn 0!').max(100),
|
|
10
|
-
height: z.number().min(1, 'Chiều cao phải lớn hơn 0!').max(100),
|
|
11
|
-
});
|
|
12
|
-
// Main form validation schema for Listen and Choose Objects in Scene
|
|
13
|
-
export const listenAndChooseObjectsInSceneFormSchema = z.object({
|
|
14
|
-
audioUrl: z.string().min(1, 'Vui lòng upload file audio!'),
|
|
15
|
-
backgroundImageUrl: z.string().min(1, 'Vui lòng upload ảnh nền!'),
|
|
16
|
-
objectNodes: z
|
|
17
|
-
.array(sceneObjectNodeSchema)
|
|
18
|
-
.min(2, 'Vui lòng thêm ít nhất 2 đối tượng!'),
|
|
19
|
-
correctObjectId: z.string().min(1, 'Vui lòng chọn đáp án đúng!'),
|
|
20
|
-
isBackup: z.boolean().optional(),
|
|
21
|
-
});
|
|
1
|
+
import{z as n}from"zod";const i=n.object({id:n.string(),imageUrl:n.string().min(1,"Vui lòng upload hình ảnh đối tượng!"),label:n.string().min(1,"Vui lòng nhập tên đối tượng!"),x:n.number().min(0).max(100),y:n.number().min(0).max(100),width:n.number().min(1,"Chiều rộng phải lớn hơn 0!").max(100),height:n.number().min(1,"Chiều cao phải lớn hơn 0!").max(100)});export const listenAndChooseObjectsInSceneFormSchema=n.object({audioUrl:n.string().min(1,"Vui lòng upload file audio!"),backgroundImageUrl:n.string().min(1,"Vui lòng upload ảnh nền!"),objectNodes:n.array(i).min(2,"Vui lòng thêm ít nhất 2 đối tượng!"),correctObjectId:n.string().min(1,"Vui lòng chọn đáp án đúng!"),isBackup:n.boolean().optional()});
|
|
@@ -1,34 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
// Drag object node schema (sidebar objects - no position)
|
|
3
|
-
const dragObjectNodeSchema = z.object({
|
|
4
|
-
id: z.string(),
|
|
5
|
-
imageUrl: z.string().min(1, 'Vui lòng upload hình ảnh đối tượng!'),
|
|
6
|
-
label: z.string().min(1, 'Vui lòng nhập tên đối tượng!'),
|
|
7
|
-
});
|
|
8
|
-
// Drop zone schema (positioned on scene)
|
|
9
|
-
const dropZoneSchema = z.object({
|
|
10
|
-
id: z.string(),
|
|
11
|
-
label: z.string().min(1, 'Vui lòng nhập nhãn vùng thả!'),
|
|
12
|
-
x: z.number().min(0).max(100),
|
|
13
|
-
y: z.number().min(0).max(100),
|
|
14
|
-
width: z.number().min(1, 'Chiều rộng phải lớn hơn 0!').max(100),
|
|
15
|
-
height: z.number().min(1, 'Chiều cao phải lớn hơn 0!').max(100),
|
|
16
|
-
});
|
|
17
|
-
// Main form validation schema for Listen and Drag Objects into Scene
|
|
18
|
-
export const listenAndDragObjectsIntoSceneFormSchema = z.object({
|
|
19
|
-
audioUrl: z.string().min(1, 'Vui lòng upload file audio!'),
|
|
20
|
-
backgroundImageUrl: z.string().min(1, 'Vui lòng upload ảnh nền!'),
|
|
21
|
-
objectNodes: z
|
|
22
|
-
.array(dragObjectNodeSchema)
|
|
23
|
-
.min(2, 'Vui lòng thêm ít nhất 2 đối tượng!'),
|
|
24
|
-
dropZones: z
|
|
25
|
-
.array(dropZoneSchema)
|
|
26
|
-
.min(1, 'Vui lòng thêm ít nhất 1 vùng thả!'),
|
|
27
|
-
correctObjectId: z.string().min(1, 'Vui lòng chọn đối tượng đúng!').refine((val) => val !== '-1', {
|
|
28
|
-
message: 'Vui lòng chọn đối tượng đúng!',
|
|
29
|
-
}),
|
|
30
|
-
correctDropZoneId: z.string().min(1, 'Vui lòng chọn vùng thả đúng!').refine((val) => val !== '-1', {
|
|
31
|
-
message: 'Vui lòng chọn vùng thả đúng!',
|
|
32
|
-
}),
|
|
33
|
-
isBackup: z.boolean().optional(),
|
|
34
|
-
});
|
|
1
|
+
import{z as n}from"zod";const i=n.object({id:n.string(),imageUrl:n.string().min(1,"Vui lòng upload hình ảnh đối tượng!"),label:n.string().min(1,"Vui lòng nhập tên đối tượng!")}),t=n.object({id:n.string(),label:n.string().min(1,"Vui lòng nhập nhãn vùng thả!"),x:n.number().min(0).max(100),y:n.number().min(0).max(100),width:n.number().min(1,"Chiều rộng phải lớn hơn 0!").max(100),height:n.number().min(1,"Chiều cao phải lớn hơn 0!").max(100)});export const listenAndDragObjectsIntoSceneFormSchema=n.object({audioUrl:n.string().min(1,"Vui lòng upload file audio!"),backgroundImageUrl:n.string().min(1,"Vui lòng upload ảnh nền!"),objectNodes:n.array(i).min(2,"Vui lòng thêm ít nhất 2 đối tượng!"),dropZones:n.array(t).min(1,"Vui lòng thêm ít nhất 1 vùng thả!"),correctObjectId:n.string().min(1,"Vui lòng chọn đối tượng đúng!").refine(n=>"-1"!==n,{message:"Vui lòng chọn đối tượng đúng!"}),correctDropZoneId:n.string().min(1,"Vui lòng chọn vùng thả đúng!").refine(n=>"-1"!==n,{message:"Vui lòng chọn vùng thả đúng!"}),isBackup:n.boolean().optional()});
|
|
@@ -1,40 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export const listenAndSpeakAnswerFormSchema = z.object({
|
|
3
|
-
audioUrl: z.string().min(1, 'Vui lòng upload file audio!'),
|
|
4
|
-
expectedAnswers: z
|
|
5
|
-
.array(z.string().min(1, 'Vui lòng nhập đáp án!'))
|
|
6
|
-
.min(1, 'Cần tối thiểu 1 đáp án!')
|
|
7
|
-
.refine((options) => options.filter((opt) => opt.trim() !== '').length >= 1, { message: 'Cần tối thiểu 1 đáp án hợp lệ!' }),
|
|
8
|
-
explanation: z.string().optional(),
|
|
9
|
-
points: z
|
|
10
|
-
.union([z.string(), z.number()])
|
|
11
|
-
.transform((val, ctx) => {
|
|
12
|
-
if (val === '' || val === undefined || val === null) {
|
|
13
|
-
ctx.addIssue({
|
|
14
|
-
code: z.ZodIssueCode.custom,
|
|
15
|
-
message: 'Vui lòng nhập điểm!',
|
|
16
|
-
});
|
|
17
|
-
return z.NEVER;
|
|
18
|
-
}
|
|
19
|
-
const num = typeof val === 'string' ? parseInt(val, 10) : val;
|
|
20
|
-
if (isNaN(num)) {
|
|
21
|
-
ctx.addIssue({
|
|
22
|
-
code: z.ZodIssueCode.custom,
|
|
23
|
-
message: 'Vui lòng nhập điểm hợp lệ!',
|
|
24
|
-
});
|
|
25
|
-
return z.NEVER;
|
|
26
|
-
}
|
|
27
|
-
return num;
|
|
28
|
-
})
|
|
29
|
-
.pipe(z.number().min(1, 'Điểm số phải >= 1')),
|
|
30
|
-
backgroundImageUrl: z.string().optional(),
|
|
31
|
-
arrowNode: z
|
|
32
|
-
.object({
|
|
33
|
-
x: z.number(),
|
|
34
|
-
y: z.number(),
|
|
35
|
-
width: z.number(),
|
|
36
|
-
height: z.number(),
|
|
37
|
-
})
|
|
38
|
-
.optional(),
|
|
39
|
-
isBackup: z.boolean().optional(),
|
|
40
|
-
});
|
|
1
|
+
import{z as n}from"zod";export const listenAndSpeakAnswerFormSchema=n.object({audioUrl:n.string().min(1,"Vui lòng upload file audio!"),expectedAnswers:n.array(n.string().min(1,"Vui lòng nhập đáp án!")).min(1,"Cần tối thiểu 1 đáp án!").refine(n=>n.filter(n=>""!==n.trim()).length>=1,{message:"Cần tối thiểu 1 đáp án hợp lệ!"}),explanation:n.string().optional(),points:n.union([n.string(),n.number()]).transform((e,i)=>{if(""===e||null==e)return i.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm!"}),n.NEVER;const o="string"==typeof e?parseInt(e,10):e;return isNaN(o)?(i.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm hợp lệ!"}),n.NEVER):o}).pipe(n.number().min(1,"Điểm số phải >= 1")),backgroundImageUrl:n.string().optional(),arrowNode:n.object({x:n.number(),y:n.number(),width:n.number(),height:n.number()}).optional(),isBackup:n.boolean().optional()});
|
|
@@ -1,45 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
// Shared config for point validation
|
|
3
|
-
const pointsValidation = z
|
|
4
|
-
.union([z.string(), z.number()])
|
|
5
|
-
.transform((val, ctx) => {
|
|
6
|
-
if (val === '' || val === undefined || val === null) {
|
|
7
|
-
ctx.addIssue({
|
|
8
|
-
code: z.ZodIssueCode.custom,
|
|
9
|
-
message: 'Vui lòng nhập điểm!',
|
|
10
|
-
});
|
|
11
|
-
return z.NEVER;
|
|
12
|
-
}
|
|
13
|
-
const num = typeof val === 'string' ? parseInt(val, 10) : val;
|
|
14
|
-
if (isNaN(num)) {
|
|
15
|
-
ctx.addIssue({
|
|
16
|
-
code: z.ZodIssueCode.custom,
|
|
17
|
-
message: 'Điểm không hợp lệ!',
|
|
18
|
-
});
|
|
19
|
-
return z.NEVER;
|
|
20
|
-
}
|
|
21
|
-
return num;
|
|
22
|
-
})
|
|
23
|
-
.pipe(z.number().min(1, 'Điểm tối thiểu là 1'));
|
|
24
|
-
// Shared validation for the group (Only the first question handles this validation if group doesn't exist yet)
|
|
25
|
-
export const compareImagesGroupSchema = z.object({
|
|
26
|
-
candidateImageUrl: z.string().min(1, "Vui lòng tải lên Candidate's Copy Image!"),
|
|
27
|
-
examinerImageUrl: z.string().min(1, "Vui lòng tải lên Examiner's Copy Image!"),
|
|
28
|
-
});
|
|
29
|
-
// Validation for the actual question form data
|
|
30
|
-
export const listenAndSpeakCompareImagesSchema = z.object({
|
|
31
|
-
// Group fields (only visible/required if it's the first question)
|
|
32
|
-
candidateImageUrl: z.string().optional(),
|
|
33
|
-
examinerImageUrl: z.string().optional(),
|
|
34
|
-
// Individual fields
|
|
35
|
-
audioUrl: z.string({
|
|
36
|
-
message: "Vui lòng tải lên File Audio câu hỏi!",
|
|
37
|
-
}).min(1, 'Vui lòng tải lên File Audio câu hỏi!'),
|
|
38
|
-
expectedAnswers: z
|
|
39
|
-
.array(z.string().min(1, 'Vui lòng điền đáp án!'))
|
|
40
|
-
.min(1, 'Phải có ít nhất 1 đáp án mong muốn!')
|
|
41
|
-
.refine((options) => options.filter((opt) => opt.trim() !== '').length >= 1, { message: 'Phải điền ít nhất 1 đáp án hợp lệ!' }),
|
|
42
|
-
explanation: z.string().optional(),
|
|
43
|
-
points: pointsValidation,
|
|
44
|
-
isBackup: z.boolean().optional(),
|
|
45
|
-
});
|
|
1
|
+
import{z as n}from"zod";const i=n.union([n.string(),n.number()]).transform((i,e)=>{if(""===i||null==i)return e.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm!"}),n.NEVER;const t="string"==typeof i?parseInt(i,10):i;return isNaN(t)?(e.addIssue({code:n.ZodIssueCode.custom,message:"Điểm không hợp lệ!"}),n.NEVER):t}).pipe(n.number().min(1,"Điểm tối thiểu là 1"));export const compareImagesGroupSchema=n.object({candidateImageUrl:n.string().min(1,"Vui lòng tải lên Candidate's Copy Image!"),examinerImageUrl:n.string().min(1,"Vui lòng tải lên Examiner's Copy Image!")});export const listenAndSpeakCompareImagesSchema=n.object({candidateImageUrl:n.string().optional(),examinerImageUrl:n.string().optional(),audioUrl:n.string({message:"Vui lòng tải lên File Audio câu hỏi!"}).min(1,"Vui lòng tải lên File Audio câu hỏi!"),expectedAnswers:n.array(n.string().min(1,"Vui lòng điền đáp án!")).min(1,"Phải có ít nhất 1 đáp án mong muốn!").refine(n=>n.filter(n=>""!==n.trim()).length>=1,{message:"Phải điền ít nhất 1 đáp án hợp lệ!"}),explanation:n.string().optional(),points:i,isBackup:n.boolean().optional()});
|
|
@@ -1,19 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export const groupImageItemSchema = z.object({
|
|
3
|
-
id: z.string(),
|
|
4
|
-
url: z.string().min(1, 'Vui lòng cung cấp link hình ảnh'),
|
|
5
|
-
label: z.string().min(1, 'Vui lòng nhập nhãn cho hình ảnh'),
|
|
6
|
-
});
|
|
7
|
-
export const listenAndSpeakImageGroupFormSchema = z.object({
|
|
8
|
-
audioUrl: z.string({
|
|
9
|
-
message: 'Vui lòng tải lên file âm thanh',
|
|
10
|
-
}).min(1, 'Vui lòng tải lên file âm thanh'),
|
|
11
|
-
expectedAnswers: z.array(z.string().min(1, 'Câu trả lời không được để trống'))
|
|
12
|
-
.min(1, 'Cần ít nhất một câu trả lời khả thi'),
|
|
13
|
-
explanation: z.string().optional(),
|
|
14
|
-
points: z.number().min(0.1, 'Điểm số phải lớn hơn 0'),
|
|
15
|
-
activeImageId: z.string().min(1, 'Vui lòng chọn hình ảnh cần hỏi').optional(),
|
|
16
|
-
groupImages: z.array(groupImageItemSchema).min(1, 'Vui lòng thêm ít nhất một hình ảnh').optional(),
|
|
17
|
-
groupNumber: z.number().optional(),
|
|
18
|
-
isBackup: z.boolean().optional(),
|
|
19
|
-
});
|
|
1
|
+
import{z as n}from"zod";export const groupImageItemSchema=n.object({id:n.string(),url:n.string().min(1,"Vui lòng cung cấp link hình ảnh"),label:n.string().min(1,"Vui lòng nhập nhãn cho hình ảnh")});export const listenAndSpeakImageGroupFormSchema=n.object({audioUrl:n.string({message:"Vui lòng tải lên file âm thanh"}).min(1,"Vui lòng tải lên file âm thanh"),expectedAnswers:n.array(n.string().min(1,"Câu trả lời không được để trống")).min(1,"Cần ít nhất một câu trả lời khả thi"),explanation:n.string().optional(),points:n.number().min(.1,"Điểm số phải lớn hơn 0"),activeImageId:n.string().min(1,"Vui lòng chọn hình ảnh cần hỏi").optional(),groupImages:n.array(groupImageItemSchema).min(1,"Vui lòng thêm ít nhất một hình ảnh").optional(),groupNumber:n.number().optional(),isBackup:n.boolean().optional()});
|
|
@@ -1,39 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export const infoExchangeFieldSchema = z.object({
|
|
3
|
-
key: z.string().min(1, 'Câu hỏi không được để trống'),
|
|
4
|
-
answer: z.string().min(1, 'Đáp án không được để trống'),
|
|
5
|
-
});
|
|
6
|
-
export const listenAndSpeakInfoExchangeSchema = z.object({
|
|
7
|
-
// Group data
|
|
8
|
-
title: z.string().min(1, 'Vui lòng nhập tiêu đề (VD: Katy\'s house)'),
|
|
9
|
-
imageUrl: z.string().optional(),
|
|
10
|
-
fields: z.array(infoExchangeFieldSchema).optional(), // DEPRECATED - backward compat
|
|
11
|
-
candidateFields: z
|
|
12
|
-
.array(infoExchangeFieldSchema)
|
|
13
|
-
.min(1, 'Vui lòng thêm ít nhất 1 dòng cho Candidate\'s Copy'),
|
|
14
|
-
examinerFields: z
|
|
15
|
-
.array(infoExchangeFieldSchema)
|
|
16
|
-
.min(1, 'Vui lòng thêm ít nhất 1 dòng cho Examiner\'s Copy'),
|
|
17
|
-
// Per-question data
|
|
18
|
-
audioUrl: z.string().min(1, 'Vui lòng tải lên file audio câu hỏi'),
|
|
19
|
-
expectedAnswers: z
|
|
20
|
-
.array(z.string().min(1, 'Đáp án không được để trống'))
|
|
21
|
-
.min(1, 'Vui lòng nhập ít nhất 1 đáp án'),
|
|
22
|
-
// Common
|
|
23
|
-
explanation: z.string().optional(),
|
|
24
|
-
points: z
|
|
25
|
-
.union([z.string(), z.number()])
|
|
26
|
-
.transform((val, ctx) => {
|
|
27
|
-
if (val === '' || val === undefined || val === null) {
|
|
28
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Vui lòng nhập điểm' });
|
|
29
|
-
return z.NEVER;
|
|
30
|
-
}
|
|
31
|
-
const num = typeof val === 'string' ? parseFloat(val) : val;
|
|
32
|
-
if (isNaN(num)) {
|
|
33
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Điểm phải là số hợp lệ' });
|
|
34
|
-
return z.NEVER;
|
|
35
|
-
}
|
|
36
|
-
return num;
|
|
37
|
-
})
|
|
38
|
-
.pipe(z.number().min(0.5, 'Điểm phải >= 0.5')),
|
|
39
|
-
});
|
|
1
|
+
import{z as n}from"zod";export const infoExchangeFieldSchema=n.object({key:n.string().min(1,"Câu hỏi không được để trống"),answer:n.string().min(1,"Đáp án không được để trống")});export const listenAndSpeakInfoExchangeSchema=n.object({title:n.string().min(1,"Vui lòng nhập tiêu đề (VD: Katy's house)"),imageUrl:n.string().optional(),fields:n.array(infoExchangeFieldSchema).optional(),candidateFields:n.array(infoExchangeFieldSchema).min(1,"Vui lòng thêm ít nhất 1 dòng cho Candidate's Copy"),examinerFields:n.array(infoExchangeFieldSchema).min(1,"Vui lòng thêm ít nhất 1 dòng cho Examiner's Copy"),audioUrl:n.string().min(1,"Vui lòng tải lên file audio câu hỏi"),expectedAnswers:n.array(n.string().min(1,"Đáp án không được để trống")).min(1,"Vui lòng nhập ít nhất 1 đáp án"),explanation:n.string().optional(),points:n.union([n.string(),n.number()]).transform((i,e)=>{if(""===i||null==i)return e.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm"}),n.NEVER;const t="string"==typeof i?parseFloat(i):i;return isNaN(t)?(e.addIssue({code:n.ZodIssueCode.custom,message:"Điểm phải là số hợp lệ"}),n.NEVER):t}).pipe(n.number().min(.5,"Điểm phải >= 0.5"))});
|
|
@@ -1,32 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export const listenAndSpeakOddOneOutSchema = z.object({
|
|
3
|
-
audioUrl: z.string().min(1, 'Vui lòng tải lên file audio câu hỏi'),
|
|
4
|
-
images: z
|
|
5
|
-
.array(z.string().min(1))
|
|
6
|
-
.length(4, 'Phải có đúng 4 hình ảnh')
|
|
7
|
-
.refine((imgs) => imgs.filter((img) => img.trim() !== '').length === 4, { message: 'Vui lòng tải đủ 4 hình ảnh' }),
|
|
8
|
-
oddOneOutIndex: z
|
|
9
|
-
.number({ message: 'Vui lòng chọn hình khác loại' })
|
|
10
|
-
.min(0, 'Vui lòng chọn hình khác loại')
|
|
11
|
-
.max(3, 'Chỉ số hình ảnh không hợp lệ'),
|
|
12
|
-
expectedAnswers: z
|
|
13
|
-
.array(z.string().min(1, 'Đáp án không được để trống'))
|
|
14
|
-
.min(1, 'Vui lòng nhập ít nhất 1 đáp án mong muốn'),
|
|
15
|
-
explanation: z.string().optional(),
|
|
16
|
-
points: z
|
|
17
|
-
.union([z.string(), z.number()])
|
|
18
|
-
.transform((val, ctx) => {
|
|
19
|
-
if (val === '' || val === undefined || val === null) {
|
|
20
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Vui lòng nhập điểm' });
|
|
21
|
-
return z.NEVER;
|
|
22
|
-
}
|
|
23
|
-
const num = typeof val === 'string' ? parseFloat(val) : val;
|
|
24
|
-
if (isNaN(num)) {
|
|
25
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Điểm phải là số hợp lệ' });
|
|
26
|
-
return z.NEVER;
|
|
27
|
-
}
|
|
28
|
-
return num;
|
|
29
|
-
})
|
|
30
|
-
.pipe(z.number().min(0.5, 'Điểm phải >= 0.5')),
|
|
31
|
-
isBackup: z.boolean().optional(),
|
|
32
|
-
});
|
|
1
|
+
import{z as n}from"zod";export const listenAndSpeakOddOneOutSchema=n.object({audioUrl:n.string().min(1,"Vui lòng tải lên file audio câu hỏi"),images:n.array(n.string().min(1)).length(4,"Phải có đúng 4 hình ảnh").refine(n=>4===n.filter(n=>""!==n.trim()).length,{message:"Vui lòng tải đủ 4 hình ảnh"}),oddOneOutIndex:n.number({message:"Vui lòng chọn hình khác loại"}).min(0,"Vui lòng chọn hình khác loại").max(3,"Chỉ số hình ảnh không hợp lệ"),expectedAnswers:n.array(n.string().min(1,"Đáp án không được để trống")).min(1,"Vui lòng nhập ít nhất 1 đáp án mong muốn"),explanation:n.string().optional(),points:n.union([n.string(),n.number()]).transform((i,e)=>{if(""===i||null==i)return e.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm"}),n.NEVER;const s="string"==typeof i?parseFloat(i):i;return isNaN(s)?(e.addIssue({code:n.ZodIssueCode.custom,message:"Điểm phải là số hợp lệ"}),n.NEVER):s}).pipe(n.number().min(.5,"Điểm phải >= 0.5")),isBackup:n.boolean().optional()});
|
|
@@ -1,19 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export const listenAndSpeakQuestionListFormSchema = z.object({
|
|
3
|
-
audioUrl: z.string({
|
|
4
|
-
message: 'Vui lòng tải lên file âm thanh',
|
|
5
|
-
}).min(1, 'Vui lòng tải lên file âm thanh'),
|
|
6
|
-
activeQuestionId: z.string({
|
|
7
|
-
message: 'Vui lòng chọn câu hỏi',
|
|
8
|
-
}).min(1, 'Vui lòng chọn câu hỏi'),
|
|
9
|
-
expectedAnswers: z.array(z.string().min(1, 'Vui lòng nhập đáp án')).min(1, 'Cần ít nhất 1 đáp án'),
|
|
10
|
-
points: z.number().min(1, 'Điểm phải lớn hơn 0').default(1),
|
|
11
|
-
explanation: z.string().optional().default(''),
|
|
12
|
-
isBackup: z.boolean().optional(),
|
|
13
|
-
});
|
|
14
|
-
export const listenAndSpeakQuestionGroupSchema = z.object({
|
|
15
|
-
questions: z.array(z.object({
|
|
16
|
-
id: z.string(),
|
|
17
|
-
text: z.string().min(1, 'Vui lòng nhập nội dung câu hỏi'),
|
|
18
|
-
})).min(1, 'Cần ít nhất một câu hỏi'),
|
|
19
|
-
});
|
|
1
|
+
import{z as n}from"zod";export const listenAndSpeakQuestionListFormSchema=n.object({audioUrl:n.string({message:"Vui lòng tải lên file âm thanh"}).min(1,"Vui lòng tải lên file âm thanh"),activeQuestionId:n.string({message:"Vui lòng chọn câu hỏi"}).min(1,"Vui lòng chọn câu hỏi"),expectedAnswers:n.array(n.string().min(1,"Vui lòng nhập đáp án")).min(1,"Cần ít nhất 1 đáp án"),points:n.number().min(1,"Điểm phải lớn hơn 0").default(1),explanation:n.string().optional().default(""),isBackup:n.boolean().optional()});export const listenAndSpeakQuestionGroupSchema=n.object({questions:n.array(n.object({id:n.string(),text:n.string().min(1,"Vui lòng nhập nội dung câu hỏi")})).min(1,"Cần ít nhất một câu hỏi")});
|
|
@@ -1,28 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export const listenAndSpeakWithStoryImagesSchema = z.object({
|
|
3
|
-
audioUrl: z.string().min(1, 'Vui lòng tải lên file audio câu hỏi'),
|
|
4
|
-
images: z
|
|
5
|
-
.array(z.string().min(1))
|
|
6
|
-
.min(1, 'Vui lòng tải lên ít nhất 1 hình ảnh')
|
|
7
|
-
.refine((imgs) => imgs.filter((img) => img.trim() !== '').length >= 1, { message: 'Phải có ít nhất 1 hình ảnh hợp lệ' }),
|
|
8
|
-
expectedAnswers: z
|
|
9
|
-
.array(z.string().min(1, 'Đáp án không được để trống'))
|
|
10
|
-
.min(1, 'Vui lòng nhập ít nhất 1 đáp án mong muốn'),
|
|
11
|
-
explanation: z.string().optional(),
|
|
12
|
-
points: z
|
|
13
|
-
.union([z.string(), z.number()])
|
|
14
|
-
.transform((val, ctx) => {
|
|
15
|
-
if (val === '' || val === undefined || val === null) {
|
|
16
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Vui lòng nhập điểm' });
|
|
17
|
-
return z.NEVER;
|
|
18
|
-
}
|
|
19
|
-
const num = typeof val === 'string' ? parseFloat(val) : val;
|
|
20
|
-
if (isNaN(num)) {
|
|
21
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Điểm phải là số hợp lệ' });
|
|
22
|
-
return z.NEVER;
|
|
23
|
-
}
|
|
24
|
-
return num;
|
|
25
|
-
})
|
|
26
|
-
.pipe(z.number().min(0.5, 'Điểm phải >= 0.5')),
|
|
27
|
-
isBackup: z.boolean().optional(),
|
|
28
|
-
});
|
|
1
|
+
import{z as n}from"zod";export const listenAndSpeakWithStoryImagesSchema=n.object({audioUrl:n.string().min(1,"Vui lòng tải lên file audio câu hỏi"),images:n.array(n.string().min(1)).min(1,"Vui lòng tải lên ít nhất 1 hình ảnh").refine(n=>n.filter(n=>""!==n.trim()).length>=1,{message:"Phải có ít nhất 1 hình ảnh hợp lệ"}),expectedAnswers:n.array(n.string().min(1,"Đáp án không được để trống")).min(1,"Vui lòng nhập ít nhất 1 đáp án mong muốn"),explanation:n.string().optional(),points:n.union([n.string(),n.number()]).transform((i,e)=>{if(""===i||null==i)return e.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm"}),n.NEVER;const t="string"==typeof i?parseFloat(i):i;return isNaN(t)?(e.addIssue({code:n.ZodIssueCode.custom,message:"Điểm phải là số hợp lệ"}),n.NEVER):t}).pipe(n.number().min(.5,"Điểm phải >= 0.5")),isBackup:n.boolean().optional()});
|
|
@@ -1,12 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
// Main form validation schema - NEW flat structure
|
|
3
|
-
// Same as LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER but without question field
|
|
4
|
-
export const lookPictureChooseCorrectAnswerFormSchema = z.object({
|
|
5
|
-
imageUrl: z.string().min(1, 'Vui lòng upload hình ảnh!'),
|
|
6
|
-
options: z
|
|
7
|
-
.array(z.string().min(1, 'Vui lòng nhập nội dung đáp án!'))
|
|
8
|
-
.min(2, 'Phải có ít nhất 2 lựa chọn!'),
|
|
9
|
-
correctAnswer: z.number(),
|
|
10
|
-
explanation: z.string().optional(),
|
|
11
|
-
points: z.number().min(0.5).optional(),
|
|
12
|
-
});
|
|
1
|
+
import{z as n}from"zod";export const lookPictureChooseCorrectAnswerFormSchema=n.object({imageUrl:n.string().min(1,"Vui lòng upload hình ảnh!"),options:n.array(n.string().min(1,"Vui lòng nhập nội dung đáp án!")).min(2,"Phải có ít nhất 2 lựa chọn!"),correctAnswer:n.number(),explanation:n.string().optional(),points:n.number().min(.5).optional()});
|
|
@@ -1,47 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
/**
|
|
3
|
-
* Validation schema for LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER form
|
|
4
|
-
*
|
|
5
|
-
* Based on API payload structure:
|
|
6
|
-
* - content: { imageUrl, question, options: string[] }
|
|
7
|
-
* - correct_answer: { answer: number }
|
|
8
|
-
*/
|
|
9
|
-
export const lookPictureFillBlankChooseAnswerFormSchema = z.object({
|
|
10
|
-
imageUrl: z.string().min(1, 'Vui lòng upload hình ảnh!'),
|
|
11
|
-
question: z
|
|
12
|
-
.string()
|
|
13
|
-
.min(1, 'Vui lòng nhập nội dung câu hỏi!')
|
|
14
|
-
.refine((val) => val.includes('{0}'), {
|
|
15
|
-
message: 'Câu hỏi phải chứa {0} để đánh dấu chỗ trống',
|
|
16
|
-
})
|
|
17
|
-
.refine((val) => (val.match(/\{0\}/g) || []).length <= 1, {
|
|
18
|
-
message: 'Chỉ được có một {0} trong câu hỏi',
|
|
19
|
-
}),
|
|
20
|
-
options: z
|
|
21
|
-
.array(z.string().min(1, 'Vui lòng nhập đáp án!'))
|
|
22
|
-
.min(2, 'Phải có ít nhất 2 đáp án!')
|
|
23
|
-
.refine((options) => options.filter((opt) => opt.trim() !== '').length >= 2, { message: 'Phải có ít nhất 2 đáp án được điền!' }),
|
|
24
|
-
correctAnswer: z.number().min(0, 'Vui lòng chọn đáp án đúng!'),
|
|
25
|
-
explanation: z.string().optional(),
|
|
26
|
-
points: z
|
|
27
|
-
.union([z.string(), z.number()])
|
|
28
|
-
.transform((val, ctx) => {
|
|
29
|
-
if (val === '' || val === undefined || val === null) {
|
|
30
|
-
ctx.addIssue({
|
|
31
|
-
code: z.ZodIssueCode.custom,
|
|
32
|
-
message: 'Vui lòng nhập điểm số!',
|
|
33
|
-
});
|
|
34
|
-
return z.NEVER;
|
|
35
|
-
}
|
|
36
|
-
const num = typeof val === 'string' ? parseInt(val, 10) : val;
|
|
37
|
-
if (isNaN(num)) {
|
|
38
|
-
ctx.addIssue({
|
|
39
|
-
code: z.ZodIssueCode.custom,
|
|
40
|
-
message: 'Vui lòng nhập điểm số hợp lệ!',
|
|
41
|
-
});
|
|
42
|
-
return z.NEVER;
|
|
43
|
-
}
|
|
44
|
-
return num;
|
|
45
|
-
})
|
|
46
|
-
.pipe(z.number().min(1, 'Điểm số phải >= 1')),
|
|
47
|
-
});
|
|
1
|
+
import{z as n}from"zod";export const lookPictureFillBlankChooseAnswerFormSchema=n.object({imageUrl:n.string().min(1,"Vui lòng upload hình ảnh!"),question:n.string().min(1,"Vui lòng nhập nội dung câu hỏi!").refine(n=>n.includes("{0}"),{message:"Câu hỏi phải chứa {0} để đánh dấu chỗ trống"}).refine(n=>(n.match(/\{0\}/g)||[]).length<=1,{message:"Chỉ được có một {0} trong câu hỏi"}),options:n.array(n.string().min(1,"Vui lòng nhập đáp án!")).min(2,"Phải có ít nhất 2 đáp án!").refine(n=>n.filter(n=>""!==n.trim()).length>=2,{message:"Phải có ít nhất 2 đáp án được điền!"}),correctAnswer:n.number().min(0,"Vui lòng chọn đáp án đúng!"),explanation:n.string().optional(),points:n.union([n.string(),n.number()]).transform((i,e)=>{if(""===i||null==i)return e.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm số!"}),n.NEVER;const s="string"==typeof i?parseInt(i,10):i;return isNaN(s)?(e.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm số hợp lệ!"}),n.NEVER):s}).pipe(n.number().min(1,"Điểm số phải >= 1"))});
|
|
@@ -1,29 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
// Color node schema
|
|
3
|
-
const colorNodeSchema = z.object({
|
|
4
|
-
id: z.string(),
|
|
5
|
-
color: z.string().min(1, 'Vui lòng chọn màu!'),
|
|
6
|
-
label: z.string().optional(), // Optional label (e.g. "Tom", "Apple") for IMAGE_OBJECT_MATCHING
|
|
7
|
-
});
|
|
8
|
-
// Color region node schema
|
|
9
|
-
const colorRegionNodeSchema = z.object({
|
|
10
|
-
id: z.string(),
|
|
11
|
-
x: z.number(),
|
|
12
|
-
y: z.number(),
|
|
13
|
-
width: z.number().optional(),
|
|
14
|
-
height: z.number().optional(),
|
|
15
|
-
color: z.string().optional(),
|
|
16
|
-
linkedColorNodeId: z.string().optional(),
|
|
17
|
-
});
|
|
18
|
-
// Main form validation schema for Read and Color Objects
|
|
19
|
-
export const readAndColorObjectsFormSchema = z.object({
|
|
20
|
-
backgroundImageUrl: z.string().min(1, 'Vui lòng upload ảnh nền!'),
|
|
21
|
-
colorNodes: z.array(colorNodeSchema).min(1, 'Vui lòng thêm ít nhất một màu!'),
|
|
22
|
-
regionNodes: z
|
|
23
|
-
.array(colorRegionNodeSchema)
|
|
24
|
-
.min(1, 'Vui lòng thêm ít nhất một vùng tô màu!')
|
|
25
|
-
.refine((regions) => regions.every((r) => r.linkedColorNodeId && r.linkedColorNodeId.trim() !== ''), { message: 'Tất cả các vùng phải được liên kết với màu!' }),
|
|
26
|
-
instructions: z
|
|
27
|
-
.array(z.string().min(1, 'Vui lòng nhập hướng dẫn!'))
|
|
28
|
-
.min(1, 'Vui lòng thêm ít nhất một hướng dẫn!'),
|
|
29
|
-
});
|
|
1
|
+
import{z as n}from"zod";const o=n.object({id:n.string(),color:n.string().min(1,"Vui lòng chọn màu!"),label:n.string().optional()}),i=n.object({id:n.string(),x:n.number(),y:n.number(),width:n.number().optional(),height:n.number().optional(),color:n.string().optional(),linkedColorNodeId:n.string().optional()});export const readAndColorObjectsFormSchema=n.object({backgroundImageUrl:n.string().min(1,"Vui lòng upload ảnh nền!"),colorNodes:n.array(o).min(1,"Vui lòng thêm ít nhất một màu!"),regionNodes:n.array(i).min(1,"Vui lòng thêm ít nhất một vùng tô màu!").refine(n=>n.every(n=>n.linkedColorNodeId&&""!==n.linkedColorNodeId.trim()),{message:"Tất cả các vùng phải được liên kết với màu!"}),instructions:n.array(n.string().min(1,"Vui lòng nhập hướng dẫn!")).min(1,"Vui lòng thêm ít nhất một hướng dẫn!")});
|
|
@@ -1,28 +1 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export const readDisplayedContentFormSchema = z.object({
|
|
3
|
-
referenceText: z
|
|
4
|
-
.string()
|
|
5
|
-
.min(1, 'Vui lòng nhập đoạn văn bản cần đọc!'),
|
|
6
|
-
explanation: z.string().optional(),
|
|
7
|
-
points: z
|
|
8
|
-
.union([z.string(), z.number()])
|
|
9
|
-
.transform((val, ctx) => {
|
|
10
|
-
if (val === '' || val === undefined || val === null) {
|
|
11
|
-
ctx.addIssue({
|
|
12
|
-
code: z.ZodIssueCode.custom,
|
|
13
|
-
message: 'Vui lòng nhập điểm!',
|
|
14
|
-
});
|
|
15
|
-
return z.NEVER;
|
|
16
|
-
}
|
|
17
|
-
const num = typeof val === 'string' ? parseInt(val, 10) : val;
|
|
18
|
-
if (isNaN(num)) {
|
|
19
|
-
ctx.addIssue({
|
|
20
|
-
code: z.ZodIssueCode.custom,
|
|
21
|
-
message: 'Vui lòng nhập điểm hợp lệ!',
|
|
22
|
-
});
|
|
23
|
-
return z.NEVER;
|
|
24
|
-
}
|
|
25
|
-
return num;
|
|
26
|
-
})
|
|
27
|
-
.pipe(z.number().min(1, 'Điểm số phải >= 1')),
|
|
28
|
-
});
|
|
1
|
+
import{z as n}from"zod";export const readDisplayedContentFormSchema=n.object({referenceText:n.string().min(1,"Vui lòng nhập đoạn văn bản cần đọc!"),explanation:n.string().optional(),points:n.union([n.string(),n.number()]).transform((e,s)=>{if(""===e||null==e)return s.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm!"}),n.NEVER;const o="string"==typeof e?parseInt(e,10):e;return isNaN(o)?(s.addIssue({code:n.ZodIssueCode.custom,message:"Vui lòng nhập điểm hợp lệ!"}),n.NEVER):o}).pipe(n.number().min(1,"Điểm số phải >= 1"))});
|