@tinyweb_dev/oe-exam-sdk 0.1.1 → 0.1.3
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.d.ts +1 -1
- package/dist/api/exam-questions.js +1 -98
- package/dist/api/exam-questions.types.d.ts +45 -1
- 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/CompletionDialog.d.ts +8 -0
- package/dist/components/exams/CompletionDialog.js +1 -0
- package/dist/components/exams/CreateExamInfoForm.d.ts +4 -1
- package/dist/components/exams/CreateExamInfoForm.js +1 -35
- package/dist/components/exams/CreateExamPageContainer.d.ts +3 -0
- package/dist/components/exams/CreateExamPageContainer.js +1 -103
- package/dist/components/exams/ExamCreator.d.ts +9 -3
- package/dist/components/exams/ExamCreator.js +1 -89
- package/dist/components/exams/ExamPartTabs.d.ts +1 -1
- package/dist/components/exams/ExamPartTabs.js +1 -40
- package/dist/components/exams/ExamPreviewDialog.d.ts +20 -0
- package/dist/components/exams/ExamPreviewDialog.js +1 -0
- package/dist/components/exams/ExamQuestionGrid.d.ts +1 -1
- package/dist/components/exams/ExamQuestionGrid.js +1 -18
- package/dist/components/exams/ExamQuestionsPageContainer.d.ts +3 -0
- package/dist/components/exams/ExamQuestionsPageContainer.js +1 -90
- package/dist/components/exams/ExamViewDialog.d.ts +24 -0
- package/dist/components/exams/ExamViewDialog.js +1 -0
- package/dist/components/exams/PartAudioUploader.js +1 -9
- package/dist/components/exams/TemplateSelector.js +1 -66
- package/dist/components/exams/UnsavedChangesDialog.d.ts +8 -0
- package/dist/components/exams/UnsavedChangesDialog.js +1 -0
- package/dist/components/exams/api.d.ts +1 -1
- package/dist/components/exams/api.js +1 -3
- package/dist/components/exams/create-exam.texts.d.ts +58 -0
- package/dist/components/exams/create-exam.texts.js +1 -0
- package/dist/components/exams/create-exam.validation.js +1 -18
- package/dist/components/exams/exam-question-status.d.ts +9 -0
- package/dist/components/exams/exam-question-status.js +1 -0
- package/dist/components/exams/exam-template.utils.js +1 -18
- package/dist/components/exams/index.d.ts +11 -1
- 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.d.ts +3 -3
- 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.d.ts +3 -0
- 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-reverse-transform.d.ts +6 -0
- package/dist/shared/lib/utils/question-reverse-transform.js +1 -0
- package/dist/shared/lib/utils/question-transform-types.js +1 -1
- package/dist/shared/lib/utils/question-transform-validation.d.ts +6 -0
- package/dist/shared/lib/utils/question-transform-validation.js +1 -0
- package/dist/shared/lib/utils/question-transform.d.ts +6 -2
- package/dist/shared/lib/utils/question-transform.js +1 -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 +12 -3
package/dist/shared/lib/utils.js
CHANGED
|
@@ -1,168 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { twMerge } from 'tailwind-merge';
|
|
3
|
-
/**
|
|
4
|
-
* Merge Tailwind CSS classes with clsx
|
|
5
|
-
*/
|
|
6
|
-
export function cn(...inputs) {
|
|
7
|
-
return twMerge(clsx(inputs));
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Format date to readable string
|
|
11
|
-
*/
|
|
12
|
-
export function formatDate(date, format = 'DD/MM/YYYY') {
|
|
13
|
-
const d = new Date(date);
|
|
14
|
-
const day = String(d.getDate()).padStart(2, '0');
|
|
15
|
-
const month = String(d.getMonth() + 1).padStart(2, '0');
|
|
16
|
-
const year = d.getFullYear();
|
|
17
|
-
const hours = String(d.getHours()).padStart(2, '0');
|
|
18
|
-
const minutes = String(d.getMinutes()).padStart(2, '0');
|
|
19
|
-
return format
|
|
20
|
-
.replace('DD', day)
|
|
21
|
-
.replace('MM', month)
|
|
22
|
-
.replace('YYYY', String(year))
|
|
23
|
-
.replace('HH', hours)
|
|
24
|
-
.replace('mm', minutes);
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Format duration in seconds to readable string
|
|
28
|
-
*/
|
|
29
|
-
export function formatDuration(seconds) {
|
|
30
|
-
const hours = Math.floor(seconds / 3600);
|
|
31
|
-
const minutes = Math.floor((seconds % 3600) / 60);
|
|
32
|
-
const secs = seconds % 60;
|
|
33
|
-
if (hours > 0) {
|
|
34
|
-
return `${hours}h ${minutes}m ${secs}s`;
|
|
35
|
-
}
|
|
36
|
-
if (minutes > 0) {
|
|
37
|
-
return `${minutes}m ${secs}s`;
|
|
38
|
-
}
|
|
39
|
-
return `${secs}s`;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Debounce function
|
|
43
|
-
*/
|
|
44
|
-
export function debounce(func, delay) {
|
|
45
|
-
let timeoutId;
|
|
46
|
-
return (...args) => {
|
|
47
|
-
clearTimeout(timeoutId);
|
|
48
|
-
timeoutId = setTimeout(() => func(...args), delay);
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Truncate string
|
|
53
|
-
*/
|
|
54
|
-
export function truncate(str, length) {
|
|
55
|
-
if (str.length <= length)
|
|
56
|
-
return str;
|
|
57
|
-
return `${str.slice(0, length)}...`;
|
|
58
|
-
}
|
|
59
|
-
/**
|
|
60
|
-
* Get initials from name
|
|
61
|
-
*/
|
|
62
|
-
export function getInitials(name) {
|
|
63
|
-
return name
|
|
64
|
-
.split(' ')
|
|
65
|
-
.map((n) => n[0])
|
|
66
|
-
.join('')
|
|
67
|
-
.toUpperCase()
|
|
68
|
-
.slice(0, 2);
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Format number with commas
|
|
72
|
-
*/
|
|
73
|
-
export function formatNumber(num) {
|
|
74
|
-
return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Calculate percentage
|
|
78
|
-
*/
|
|
79
|
-
export function calculatePercentage(value, total) {
|
|
80
|
-
if (total === 0)
|
|
81
|
-
return 0;
|
|
82
|
-
return Math.round((value / total) * 100);
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Format date/time to Vietnam timezone (UTC+7)
|
|
86
|
-
* @param date - ISO date string or Date object
|
|
87
|
-
* @param format - Output format (default: 'DD/MM/YYYY HH:mm')
|
|
88
|
-
* @returns Formatted date string in UTC+7
|
|
89
|
-
*/
|
|
90
|
-
export function formatDateTimeVN(date, format = 'DD/MM/YYYY HH:mm') {
|
|
91
|
-
if (!date)
|
|
92
|
-
return '';
|
|
93
|
-
try {
|
|
94
|
-
const d = new Date(date);
|
|
95
|
-
if (isNaN(d.getTime()))
|
|
96
|
-
return 'Invalid Date';
|
|
97
|
-
// Convert to Vietnam timezone (UTC+7)
|
|
98
|
-
const vietnamTime = new Date(d.getTime() + (7 * 60 * 60 * 1000));
|
|
99
|
-
const day = String(vietnamTime.getUTCDate()).padStart(2, '0');
|
|
100
|
-
const month = String(vietnamTime.getUTCMonth() + 1).padStart(2, '0');
|
|
101
|
-
const year = vietnamTime.getUTCFullYear();
|
|
102
|
-
const hours = String(vietnamTime.getUTCHours()).padStart(2, '0');
|
|
103
|
-
const minutes = String(vietnamTime.getUTCMinutes()).padStart(2, '0');
|
|
104
|
-
return format
|
|
105
|
-
.replace('DD', day)
|
|
106
|
-
.replace('MM', month)
|
|
107
|
-
.replace('YYYY', String(year))
|
|
108
|
-
.replace('HH', hours)
|
|
109
|
-
.replace('mm', minutes);
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
return 'Invalid Date';
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
/**
|
|
116
|
-
* Format ISO datetime to time only in Vietnam timezone (UTC+7)
|
|
117
|
-
* @param date - ISO date string or Date object
|
|
118
|
-
* @returns Time string in HH:mm format (UTC+7)
|
|
119
|
-
*/
|
|
120
|
-
export function formatTimeVN(date) {
|
|
121
|
-
return formatDateTimeVN(date, 'HH:mm');
|
|
122
|
-
}
|
|
123
|
-
/**
|
|
124
|
-
* Format date only in Vietnam timezone (UTC+7)
|
|
125
|
-
* @param date - ISO date string or Date object
|
|
126
|
-
* @returns Date string in DD/MM/YYYY format (UTC+7)
|
|
127
|
-
*/
|
|
128
|
-
export function formatDateVN(date) {
|
|
129
|
-
return formatDateTimeVN(date, 'DD/MM/YYYY');
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Extract date part from ISO string in Vietnam timezone (for date input)
|
|
133
|
-
* @param date - ISO date string or Date object
|
|
134
|
-
* @returns Date string in YYYY-MM-DD format (for HTML date input)
|
|
135
|
-
*/
|
|
136
|
-
export function getDateInputValue(date) {
|
|
137
|
-
if (!date)
|
|
138
|
-
return '';
|
|
139
|
-
try {
|
|
140
|
-
const d = new Date(date);
|
|
141
|
-
if (isNaN(d.getTime()))
|
|
142
|
-
return '';
|
|
143
|
-
// Convert to Vietnam timezone (UTC+7)
|
|
144
|
-
const vietnamTime = new Date(d.getTime() + (7 * 60 * 60 * 1000));
|
|
145
|
-
const year = vietnamTime.getUTCFullYear();
|
|
146
|
-
const month = String(vietnamTime.getUTCMonth() + 1).padStart(2, '0');
|
|
147
|
-
const day = String(vietnamTime.getUTCDate()).padStart(2, '0');
|
|
148
|
-
return `${year}-${month}-${day}`;
|
|
149
|
-
}
|
|
150
|
-
catch {
|
|
151
|
-
return '';
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Extract time part from ISO string in Vietnam timezone (for time input)
|
|
156
|
-
* @param date - ISO date string or Date object
|
|
157
|
-
* @returns Time string in HH:mm format (for HTML time input)
|
|
158
|
-
*/
|
|
159
|
-
export function getTimeInputValue(date) {
|
|
160
|
-
return formatTimeVN(date);
|
|
161
|
-
}
|
|
162
|
-
/**
|
|
163
|
-
* Remove undefined, null, and empty string values from an object.
|
|
164
|
-
* Useful for cleaning API payloads before sending to backend.
|
|
165
|
-
*/
|
|
166
|
-
export function cleanPayload(payload) {
|
|
167
|
-
return Object.fromEntries(Object.entries(payload).filter(([, v]) => v !== undefined && v !== '' && v !== null));
|
|
168
|
-
}
|
|
1
|
+
import{clsx as t}from"clsx";import{twMerge as e}from"tailwind-merge";export function cn(...r){return e(t(r))}export function formatDate(t,e="DD/MM/YYYY"){const r=new Date(t),n=String(r.getDate()).padStart(2,"0"),a=String(r.getMonth()+1).padStart(2,"0"),o=r.getFullYear(),i=String(r.getHours()).padStart(2,"0"),u=String(r.getMinutes()).padStart(2,"0");return e.replace("DD",n).replace("MM",a).replace("YYYY",String(o)).replace("HH",i).replace("mm",u)}export function formatDuration(t){const e=Math.floor(t/3600),r=Math.floor(t%3600/60),n=t%60;return e>0?`${e}h ${r}m ${n}s`:r>0?`${r}m ${n}s`:`${n}s`}export function debounce(t,e){let r;return(...n)=>{clearTimeout(r),r=setTimeout(()=>t(...n),e)}}export function truncate(t,e){return t.length<=e?t:`${t.slice(0,e)}...`}export function getInitials(t){return t.split(" ").map(t=>t[0]).join("").toUpperCase().slice(0,2)}export function formatNumber(t){return t.toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")}export function calculatePercentage(t,e){return 0===e?0:Math.round(t/e*100)}export function formatDateTimeVN(t,e="DD/MM/YYYY HH:mm"){if(!t)return"";try{const r=new Date(t);if(isNaN(r.getTime()))return"Invalid Date";const n=new Date(r.getTime()+252e5),a=String(n.getUTCDate()).padStart(2,"0"),o=String(n.getUTCMonth()+1).padStart(2,"0"),i=n.getUTCFullYear(),u=String(n.getUTCHours()).padStart(2,"0"),c=String(n.getUTCMinutes()).padStart(2,"0");return e.replace("DD",a).replace("MM",o).replace("YYYY",String(i)).replace("HH",u).replace("mm",c)}catch{return"Invalid Date"}}export function formatTimeVN(t){return formatDateTimeVN(t,"HH:mm")}export function formatDateVN(t){return formatDateTimeVN(t,"DD/MM/YYYY")}export function getDateInputValue(t){if(!t)return"";try{const e=new Date(t);if(isNaN(e.getTime()))return"";const r=new Date(e.getTime()+252e5),n=r.getUTCFullYear(),a=String(r.getUTCMonth()+1).padStart(2,"0");return`${n}-${a}-${String(r.getUTCDate()).padStart(2,"0")}`}catch{return""}}export function getTimeInputValue(t){return formatTimeVN(t)}export function cleanPayload(t){return Object.fromEntries(Object.entries(t).filter(([,t])=>void 0!==t&&""!==t&&null!==t))}
|
|
@@ -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")});
|