@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,37 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Card, CardContent } from '../../../../components/ui/card';
|
|
4
|
-
import { Input } from '../../../../components/ui/input';
|
|
5
|
-
import { Label } from '../../../../components/ui/label';
|
|
6
|
-
import { CheckCircle2, XCircle, BookOpen } from 'lucide-react';
|
|
7
|
-
import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
|
|
8
|
-
import { useState } from 'react';
|
|
9
|
-
export function AnswerTheQuestionClient({ questionData, onSubmit, isReviewMode = false, userAnswer, autoFillCorrectAnswers = false, explanation, }) {
|
|
10
|
-
const [answer, setAnswer] = useState(userAnswer || '');
|
|
11
|
-
const { previewUrl: imagePreviewUrl } = usePresignedFileUrl(questionData.imageUrl || '');
|
|
12
|
-
if (!questionData.question) {
|
|
13
|
-
return (_jsx(Card, { children: _jsx(CardContent, { className: "p-6", children: _jsx("p", { className: "text-gray-500", children: "Kh\u00F4ng c\u00F3 d\u1EEF li\u1EC7u c\u00E2u h\u1ECFi" }) }) }));
|
|
14
|
-
}
|
|
15
|
-
const isExample = questionData.isExample || false;
|
|
16
|
-
const displayedAnswer = autoFillCorrectAnswers || isExample
|
|
17
|
-
? questionData.correctAnswer
|
|
18
|
-
: answer;
|
|
19
|
-
const isCorrect = isReviewMode && userAnswer !== undefined
|
|
20
|
-
? userAnswer.trim().toLowerCase() === questionData.correctAnswer.trim().toLowerCase()
|
|
21
|
-
: undefined;
|
|
22
|
-
return (_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-teal-400 to-cyan-500" }), _jsxs(CardContent, { className: "p-6 space-y-6", children: [(questionData.title || questionData.groupContent) && (_jsxs("div", { className: "rounded-xl border border-indigo-200 bg-gradient-to-r from-indigo-50 to-blue-50 p-4 space-y-2", children: [questionData.title && (_jsx("h3", { className: "text-base font-bold text-indigo-900", children: questionData.title })), questionData.groupContent && (_jsx("p", { className: "text-sm text-indigo-700 leading-relaxed whitespace-pre-wrap", children: questionData.groupContent }))] })), (imagePreviewUrl || questionData.imageUrl) && (_jsx("div", { className: "flex justify-center", children: _jsx("div", { className: "relative max-w-lg overflow-hidden rounded-xl border border-gray-200 bg-gray-50 shadow-sm", children: _jsx("img", { src: imagePreviewUrl || questionData.imageUrl, alt: "Question", className: "h-auto max-h-80 w-full object-contain" }) }) })), _jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-5", children: [isExample && (_jsxs("div", { className: "mb-2 inline-flex items-center gap-1.5 rounded-full bg-amber-100 border border-amber-300 px-3 py-1", children: [_jsx(BookOpen, { className: "h-3.5 w-3.5 text-amber-600" }), _jsx("span", { className: "text-xs font-semibold text-amber-700 uppercase tracking-wide", children: "Example" })] })), _jsx(Label, { className: "text-base font-semibold text-gray-800 mb-3 block", children: questionData.question }), questionData.suggestedAnswers && questionData.suggestedAnswers.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2 mt-2 mb-1", children: questionData.suggestedAnswers.map((suggestion, index) => (_jsx("button", { type: "button", disabled: isReviewMode || isExample, onClick: () => {
|
|
23
|
-
if (!isReviewMode && !isExample) {
|
|
24
|
-
setAnswer(suggestion);
|
|
25
|
-
onSubmit?.(suggestion);
|
|
26
|
-
}
|
|
27
|
-
}, className: `inline-flex items-center rounded-full border px-3 py-1 text-sm font-medium transition-colors ${answer === suggestion
|
|
28
|
-
? 'border-teal-400 bg-teal-50 text-teal-700 ring-1 ring-teal-200'
|
|
29
|
-
: 'border-gray-200 bg-white text-gray-600 hover:border-teal-300 hover:bg-teal-50/50 hover:text-teal-600'} ${isReviewMode || isExample ? 'cursor-not-allowed opacity-60' : 'cursor-pointer'}`, children: suggestion }, index))) })), _jsxs("div", { className: "flex items-center gap-3 mt-3", children: [questionData.prefixText && (_jsx("span", { className: "text-base font-medium text-gray-700", children: questionData.prefixText })), _jsx(Input, { value: displayedAnswer, onChange: (e) => {
|
|
30
|
-
setAnswer(e.target.value);
|
|
31
|
-
onSubmit?.(e.target.value);
|
|
32
|
-
}, placeholder: "Vi\u1EBFt \u0111\u00E1p \u00E1n...", disabled: isReviewMode || isExample, className: `max-w-xs border-2 text-lg font-medium ${isReviewMode
|
|
33
|
-
? isCorrect
|
|
34
|
-
? 'border-green-400 bg-green-50 text-green-700'
|
|
35
|
-
: 'border-red-400 bg-red-50 text-red-700'
|
|
36
|
-
: 'border-teal-300 focus:border-teal-500 focus:ring-teal-200'}` }), questionData.suffixText && (_jsx("span", { className: "text-base font-medium text-gray-700", children: questionData.suffixText })), isReviewMode && isCorrect !== undefined && (_jsx("span", { className: "flex items-center gap-1", children: isCorrect ? (_jsx(CheckCircle2, { className: "h-5 w-5 text-green-500" })) : (_jsx(XCircle, { className: "h-5 w-5 text-red-500" })) }))] }), isReviewMode && isCorrect === false && (_jsxs("div", { className: "mt-3 flex items-center gap-2 rounded-lg bg-green-50 border border-green-200 px-4 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), _jsxs("span", { className: "text-sm text-green-700", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng: ", _jsx("strong", { children: questionData.correctAnswer })] })] })), autoFillCorrectAnswers && (_jsxs("div", { className: "mt-3 flex items-center gap-2 rounded-lg bg-teal-50 border border-teal-200 px-4 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-teal-600" }), _jsxs("span", { className: "text-sm text-teal-700", children: ["\u0110\u00E1p \u00E1n: ", _jsx("strong", { children: questionData.correctAnswer })] })] }))] }), explanation && isReviewMode && (_jsxs("div", { className: "rounded-xl border border-blue-200 bg-blue-50 p-4", children: [_jsx("p", { className: "text-sm font-semibold text-blue-800 mb-1", children: "Gi\u1EA3i th\u00EDch:" }), _jsx("p", { className: "text-sm text-blue-700", children: explanation })] }))] })] }));
|
|
37
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{Card as t,CardContent as a}from"../../../../components/ui/card";import{Input as s}from"../../../../components/ui/input";import{Label as l}from"../../../../components/ui/label";import{CheckCircle2 as n,XCircle as o,BookOpen as i}from"lucide-react";import{usePresignedFileUrl as d}from"../../../../shared/lib/hooks";import{useState as c}from"react";export function AnswerTheQuestionClient({questionData:m,onSubmit:g,isReviewMode:x=!1,userAnswer:b,autoFillCorrectAnswers:p=!1,explanation:u}){const[h,f]=c(b||""),{previewUrl:N}=d(m.imageUrl||"");if(!m.question)return e(t,{children:e(a,{className:"p-6",children:e("p",{className:"text-gray-500",children:"Không có dữ liệu câu hỏi"})})});const w=m.isExample||!1,v=p||w?m.correctAnswer:h,y=x&&void 0!==b?b.trim().toLowerCase()===m.correctAnswer.trim().toLowerCase():void 0;return r(t,{className:"overflow-hidden border-0 bg-white shadow-lg",children:[e("div",{className:"h-1 bg-gradient-to-r from-teal-400 to-cyan-500"}),r(a,{className:"p-6 space-y-6",children:[(m.title||m.groupContent)&&r("div",{className:"rounded-xl border border-indigo-200 bg-gradient-to-r from-indigo-50 to-blue-50 p-4 space-y-2",children:[m.title&&e("h3",{className:"text-base font-bold text-indigo-900",children:m.title}),m.groupContent&&e("p",{className:"text-sm text-indigo-700 leading-relaxed whitespace-pre-wrap",children:m.groupContent})]}),(N||m.imageUrl)&&e("div",{className:"flex justify-center",children:e("div",{className:"relative max-w-lg overflow-hidden rounded-xl border border-gray-200 bg-gray-50 shadow-sm",children:e("img",{src:N||m.imageUrl,alt:"Question",className:"h-auto max-h-80 w-full object-contain"})})}),r("div",{className:"rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-5",children:[w&&r("div",{className:"mb-2 inline-flex items-center gap-1.5 rounded-full bg-amber-100 border border-amber-300 px-3 py-1",children:[e(i,{className:"h-3.5 w-3.5 text-amber-600"}),e("span",{className:"text-xs font-semibold text-amber-700 uppercase tracking-wide",children:"Example"})]}),e(l,{className:"text-base font-semibold text-gray-800 mb-3 block",children:m.question}),m.suggestedAnswers&&m.suggestedAnswers.length>0&&e("div",{className:"flex flex-wrap gap-2 mt-2 mb-1",children:m.suggestedAnswers.map((r,t)=>e("button",{type:"button",disabled:x||w,onClick:()=>{x||w||(f(r),g?.(r))},className:`inline-flex items-center rounded-full border px-3 py-1 text-sm font-medium transition-colors ${h===r?"border-teal-400 bg-teal-50 text-teal-700 ring-1 ring-teal-200":"border-gray-200 bg-white text-gray-600 hover:border-teal-300 hover:bg-teal-50/50 hover:text-teal-600"} ${x||w?"cursor-not-allowed opacity-60":"cursor-pointer"}`,children:r},t))}),r("div",{className:"flex items-center gap-3 mt-3",children:[m.prefixText&&e("span",{className:"text-base font-medium text-gray-700",children:m.prefixText}),e(s,{value:v,onChange:e=>{f(e.target.value),g?.(e.target.value)},placeholder:"Viết đáp án...",disabled:x||w,className:"max-w-xs border-2 text-lg font-medium "+(x?y?"border-green-400 bg-green-50 text-green-700":"border-red-400 bg-red-50 text-red-700":"border-teal-300 focus:border-teal-500 focus:ring-teal-200")}),m.suffixText&&e("span",{className:"text-base font-medium text-gray-700",children:m.suffixText}),x&&void 0!==y&&e("span",{className:"flex items-center gap-1",children:y?e(n,{className:"h-5 w-5 text-green-500"}):e(o,{className:"h-5 w-5 text-red-500"})})]}),x&&!1===y&&r("div",{className:"mt-3 flex items-center gap-2 rounded-lg bg-green-50 border border-green-200 px-4 py-2",children:[e(n,{className:"h-4 w-4 text-green-600"}),r("span",{className:"text-sm text-green-700",children:["Đáp án đúng: ",e("strong",{children:m.correctAnswer})]})]}),p&&r("div",{className:"mt-3 flex items-center gap-2 rounded-lg bg-teal-50 border border-teal-200 px-4 py-2",children:[e(n,{className:"h-4 w-4 text-teal-600"}),r("span",{className:"text-sm text-teal-700",children:["Đáp án: ",e("strong",{children:m.correctAnswer})]})]})]}),u&&x&&r("div",{className:"rounded-xl border border-blue-200 bg-blue-50 p-4",children:[e("p",{className:"text-sm font-semibold text-blue-800 mb-1",children:"Giải thích:"}),e("p",{className:"text-sm text-blue-700",children:u})]})]})]})}
|
|
@@ -1,351 +1 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useRef, useEffect } from 'react';
|
|
4
|
-
import { Button } from '../../../../components/ui/button';
|
|
5
|
-
import { Label } from '../../../../components/ui/label';
|
|
6
|
-
import { Input } from '../../../../components/ui/input';
|
|
7
|
-
import { Textarea } from '../../../../components/ui/textarea';
|
|
8
|
-
import { PointsInput } from '../../../../components/ui/points-input';
|
|
9
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
|
|
10
|
-
import { Pencil, Eye, HelpCircle, BookOpen, Plus, X, ListChecks, MessageSquare } from 'lucide-react';
|
|
11
|
-
import { Checkbox } from '../../../../components/ui/checkbox';
|
|
12
|
-
import { AnswerTheQuestionClient } from './AnswerTheQuestionClient';
|
|
13
|
-
import { useDebouncedCallback } from '../../../../shared/lib/hooks';
|
|
14
|
-
import { BasicQuestionGroupCard } from '../../groups/BasicQuestionGroupCard';
|
|
15
|
-
import { useBasicQuestionGroup, getGroupNumberForIndex } from '../../_shared/hooks/useBasicQuestionGroup';
|
|
16
|
-
/**
|
|
17
|
-
* AnswerTheQuestionCreator - Single Question Mode
|
|
18
|
-
*
|
|
19
|
-
* This component handles ONE question at a time. Navigation between questions
|
|
20
|
-
* is handled by the parent ExamCreator component.
|
|
21
|
-
*
|
|
22
|
-
* Shared data within group:
|
|
23
|
-
* - imageUrl: string (the question image, shared between questions in same group)
|
|
24
|
-
* - title: string (the group title, shared between questions in same group)
|
|
25
|
-
*
|
|
26
|
-
* Per-question data:
|
|
27
|
-
* - question: string (the question text)
|
|
28
|
-
* - correctAnswer: string (the answer - a name or number)
|
|
29
|
-
* - suffixText: string (optional text after the input, e.g. "School")
|
|
30
|
-
* - prefixText: string (optional text before the input)
|
|
31
|
-
*/
|
|
32
|
-
function AnswerTheQuestionContent({ initialData, onSave, onSaveToQuestionBank, onCancel, onChange, onUnsavedChangesChange, externalErrors, groups, questionIndexInPart = 0, partId = '', validationRef, suggestedAnswers: templateSuggestedAnswers, groupConfig, hasSuggestedAnswers, headerExtra, }) {
|
|
33
|
-
// Create a default single question
|
|
34
|
-
const createDefaultQuestion = () => {
|
|
35
|
-
const groupNumber = getGroupNumberForIndex(questionIndexInPart, groups);
|
|
36
|
-
return {
|
|
37
|
-
id: `sq-${Date.now()}-${Math.random()}`,
|
|
38
|
-
question: '',
|
|
39
|
-
correctAnswer: '',
|
|
40
|
-
suffixText: '',
|
|
41
|
-
prefixText: '',
|
|
42
|
-
points: 1,
|
|
43
|
-
groupNumber,
|
|
44
|
-
isExample: false,
|
|
45
|
-
suggestedAnswers: templateSuggestedAnswers || [],
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
// Initialize from initialData or create new
|
|
49
|
-
const initializeQuestion = () => {
|
|
50
|
-
// Flat format from register.tsx transformInitialData
|
|
51
|
-
if (initialData?.question) {
|
|
52
|
-
return {
|
|
53
|
-
id: `sq-${Date.now()}-imported`,
|
|
54
|
-
question: initialData.question,
|
|
55
|
-
correctAnswer: initialData.correctAnswer || '',
|
|
56
|
-
suffixText: initialData.suffixText || '',
|
|
57
|
-
prefixText: initialData.prefixText || '',
|
|
58
|
-
points: initialData.points || 1,
|
|
59
|
-
groupNumber: getGroupNumberForIndex(questionIndexInPart, groups),
|
|
60
|
-
isExample: initialData.isExample || false,
|
|
61
|
-
suggestedAnswers: initialData.suggestedAnswers?.length ? initialData.suggestedAnswers : (templateSuggestedAnswers || []),
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
return createDefaultQuestion();
|
|
65
|
-
};
|
|
66
|
-
// State for current question
|
|
67
|
-
const [currentQuestion, setCurrentQuestion] = useState(initializeQuestion);
|
|
68
|
-
// ============ GROUP (shared title / content / image) ============
|
|
69
|
-
const { isGrouped, currentGroupNumber: hookGroupNumber, groupTitle, groupSubtitle, groupContent, imageUrl, setGroupTitle, setGroupSubtitle, setGroupContent, setImageUrl, } = useBasicQuestionGroup({
|
|
70
|
-
partId,
|
|
71
|
-
groups,
|
|
72
|
-
questionIndexInPart,
|
|
73
|
-
initialData: {
|
|
74
|
-
title: (() => {
|
|
75
|
-
const content = initialData?.content;
|
|
76
|
-
const answer = initialData?.answer;
|
|
77
|
-
const qgPayload = initialData?.questionGroup?.payload;
|
|
78
|
-
return answer?.title || content?.passageTitle || qgPayload?.title || initialData?.title || '';
|
|
79
|
-
})(),
|
|
80
|
-
subtitle: (() => {
|
|
81
|
-
const content = initialData?.content;
|
|
82
|
-
const qgPayload = initialData?.questionGroup?.payload;
|
|
83
|
-
return content?.passageSubtitle || qgPayload?.subtitle || '';
|
|
84
|
-
})(),
|
|
85
|
-
content: (() => {
|
|
86
|
-
const content = initialData?.content;
|
|
87
|
-
const qgPayload = initialData?.questionGroup?.payload;
|
|
88
|
-
return initialData?.groupContent || qgPayload?.content || content?.passage || '';
|
|
89
|
-
})(),
|
|
90
|
-
imageUrl: (() => {
|
|
91
|
-
const content = initialData?.content;
|
|
92
|
-
const answer = initialData?.answer;
|
|
93
|
-
const qgPayload = initialData?.questionGroup?.payload;
|
|
94
|
-
return answer?.imageUrl || content?.imageUrl || qgPayload?.imageUrl || initialData?.imageUrl || '';
|
|
95
|
-
})(),
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
// UI State
|
|
99
|
-
const [isClientMode, setIsClientMode] = useState(false);
|
|
100
|
-
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
|
101
|
-
const [explanation, setExplanation] = useState(initialData?.explanation || '');
|
|
102
|
-
// Inline validation errors
|
|
103
|
-
const [validationErrors, setValidationErrors] = useState({});
|
|
104
|
-
// Store the last saved state for comparison
|
|
105
|
-
const savedStateRef = useRef('');
|
|
106
|
-
// Refs for change tracking
|
|
107
|
-
const previousInitialDataRef = useRef(initialData);
|
|
108
|
-
const previousQuestionIndexRef = useRef(questionIndexInPart);
|
|
109
|
-
const isInitializingRef = useRef(false);
|
|
110
|
-
const hasInitializedRef = useRef(false);
|
|
111
|
-
const onChangeRef = useRef(onChange);
|
|
112
|
-
const previousPayloadRef = useRef('');
|
|
113
|
-
// Current group number – prefer the hook's derived value (from groups config),
|
|
114
|
-
// fall back to the question's own stored groupNumber
|
|
115
|
-
const currentGroupNumber = hookGroupNumber || currentQuestion.groupNumber || 1;
|
|
116
|
-
// Ref for onUnsavedChangesChange callback
|
|
117
|
-
const onUnsavedChangesChangeRef = useRef(onUnsavedChangesChange);
|
|
118
|
-
useEffect(() => {
|
|
119
|
-
onUnsavedChangesChangeRef.current = onUnsavedChangesChange;
|
|
120
|
-
}, [onUnsavedChangesChange]);
|
|
121
|
-
// Update onChange ref
|
|
122
|
-
useEffect(() => {
|
|
123
|
-
onChangeRef.current = onChange;
|
|
124
|
-
}, [onChange]);
|
|
125
|
-
// Debounced onChange callback
|
|
126
|
-
const debouncedOnChange = useDebouncedCallback((data) => {
|
|
127
|
-
onChangeRef.current?.(data);
|
|
128
|
-
}, 300);
|
|
129
|
-
// Helper function to create current state string for comparison
|
|
130
|
-
const getCurrentStateString = () => {
|
|
131
|
-
return JSON.stringify({
|
|
132
|
-
imageUrl,
|
|
133
|
-
title: groupTitle,
|
|
134
|
-
subtitle: groupSubtitle,
|
|
135
|
-
question: currentQuestion.question,
|
|
136
|
-
correctAnswer: currentQuestion.correctAnswer,
|
|
137
|
-
suffixText: currentQuestion.suffixText,
|
|
138
|
-
prefixText: currentQuestion.prefixText,
|
|
139
|
-
points: currentQuestion.points,
|
|
140
|
-
isExample: currentQuestion.isExample,
|
|
141
|
-
suggestedAnswers: currentQuestion.suggestedAnswers,
|
|
142
|
-
});
|
|
143
|
-
};
|
|
144
|
-
// Initialize saved state when questionIndexInPart changes (navigation)
|
|
145
|
-
useEffect(() => {
|
|
146
|
-
if (isInitializingRef.current)
|
|
147
|
-
return;
|
|
148
|
-
const timer = setTimeout(() => {
|
|
149
|
-
savedStateRef.current = getCurrentStateString();
|
|
150
|
-
setHasUnsavedChanges(false);
|
|
151
|
-
}, 150);
|
|
152
|
-
return () => clearTimeout(timer);
|
|
153
|
-
}, [questionIndexInPart]);
|
|
154
|
-
// Detect unsaved changes
|
|
155
|
-
useEffect(() => {
|
|
156
|
-
if (isInitializingRef.current)
|
|
157
|
-
return;
|
|
158
|
-
if (!savedStateRef.current)
|
|
159
|
-
return;
|
|
160
|
-
const currentState = getCurrentStateString();
|
|
161
|
-
const hasChanges = currentState !== savedStateRef.current;
|
|
162
|
-
if (hasChanges !== hasUnsavedChanges) {
|
|
163
|
-
setHasUnsavedChanges(hasChanges);
|
|
164
|
-
onUnsavedChangesChangeRef.current?.(hasChanges);
|
|
165
|
-
}
|
|
166
|
-
}, [imageUrl, currentQuestion, hasUnsavedChanges]);
|
|
167
|
-
// Expose validation function via ref
|
|
168
|
-
useEffect(() => {
|
|
169
|
-
if (validationRef) {
|
|
170
|
-
validationRef.current = {
|
|
171
|
-
triggerValidation: async () => {
|
|
172
|
-
const errors = {};
|
|
173
|
-
let isValid = true;
|
|
174
|
-
if (!currentQuestion.question || currentQuestion.question.trim() === '') {
|
|
175
|
-
errors.question = 'Vui lòng nhập câu hỏi';
|
|
176
|
-
isValid = false;
|
|
177
|
-
}
|
|
178
|
-
if (!currentQuestion.correctAnswer || currentQuestion.correctAnswer.trim() === '') {
|
|
179
|
-
errors.correctAnswer = 'Vui lòng nhập đáp án';
|
|
180
|
-
isValid = false;
|
|
181
|
-
}
|
|
182
|
-
setValidationErrors(errors);
|
|
183
|
-
return isValid;
|
|
184
|
-
},
|
|
185
|
-
getFormData: () => ({
|
|
186
|
-
imageUrl,
|
|
187
|
-
title: groupTitle,
|
|
188
|
-
subtitle: groupSubtitle,
|
|
189
|
-
question: currentQuestion.question,
|
|
190
|
-
correctAnswer: currentQuestion.correctAnswer,
|
|
191
|
-
suffixText: currentQuestion.suffixText,
|
|
192
|
-
prefixText: currentQuestion.prefixText,
|
|
193
|
-
points: currentQuestion.points,
|
|
194
|
-
groupNumber: currentQuestion.groupNumber,
|
|
195
|
-
isExample: currentQuestion.isExample,
|
|
196
|
-
suggestedAnswers: currentQuestion.suggestedAnswers,
|
|
197
|
-
explanation,
|
|
198
|
-
groups,
|
|
199
|
-
}),
|
|
200
|
-
};
|
|
201
|
-
}
|
|
202
|
-
}, [validationRef, currentQuestion, imageUrl, groupTitle, groupSubtitle, explanation, groups]);
|
|
203
|
-
// Sync from initialData when navigation or first init
|
|
204
|
-
// Note: shared group data (title/content/image) is managed by useBasicQuestionGroup hook.
|
|
205
|
-
// This effect handles question-specific fields and explanation only.
|
|
206
|
-
useEffect(() => {
|
|
207
|
-
const questionIndexChanged = questionIndexInPart !== previousQuestionIndexRef.current;
|
|
208
|
-
const hasValidInitialData = !!initialData?.question;
|
|
209
|
-
const shouldSyncForFirstInit = !hasInitializedRef.current && hasValidInitialData;
|
|
210
|
-
const shouldSyncForNavigation = questionIndexChanged;
|
|
211
|
-
if (!shouldSyncForFirstInit && !shouldSyncForNavigation) {
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
isInitializingRef.current = true;
|
|
215
|
-
previousInitialDataRef.current = initialData;
|
|
216
|
-
previousQuestionIndexRef.current = questionIndexInPart;
|
|
217
|
-
hasInitializedRef.current = true;
|
|
218
|
-
const groupNum = getGroupNumberForIndex(questionIndexInPart, groups);
|
|
219
|
-
// Initialize question
|
|
220
|
-
setCurrentQuestion({
|
|
221
|
-
...initializeQuestion(),
|
|
222
|
-
groupNumber: groupNum,
|
|
223
|
-
});
|
|
224
|
-
// Set explanation
|
|
225
|
-
setExplanation(initialData?.explanation || '');
|
|
226
|
-
setTimeout(() => {
|
|
227
|
-
isInitializingRef.current = false;
|
|
228
|
-
}, 100);
|
|
229
|
-
}, [initialData, groups, questionIndexInPart]);
|
|
230
|
-
// Call onChange when data changes
|
|
231
|
-
useEffect(() => {
|
|
232
|
-
if (isInitializingRef.current)
|
|
233
|
-
return;
|
|
234
|
-
const payload = {
|
|
235
|
-
question: currentQuestion.question,
|
|
236
|
-
correctAnswer: currentQuestion.correctAnswer,
|
|
237
|
-
suffixText: currentQuestion.suffixText,
|
|
238
|
-
prefixText: currentQuestion.prefixText,
|
|
239
|
-
points: currentQuestion.points || 1,
|
|
240
|
-
groupNumber: currentQuestion.groupNumber,
|
|
241
|
-
isExample: currentQuestion.isExample,
|
|
242
|
-
suggestedAnswers: currentQuestion.suggestedAnswers,
|
|
243
|
-
imageUrl,
|
|
244
|
-
title: groupTitle,
|
|
245
|
-
subtitle: groupSubtitle,
|
|
246
|
-
groupContent,
|
|
247
|
-
explanation,
|
|
248
|
-
groups,
|
|
249
|
-
};
|
|
250
|
-
const payloadString = JSON.stringify(payload);
|
|
251
|
-
if (previousPayloadRef.current !== payloadString) {
|
|
252
|
-
previousPayloadRef.current = payloadString;
|
|
253
|
-
debouncedOnChange(payload);
|
|
254
|
-
}
|
|
255
|
-
}, [imageUrl, groupTitle, groupSubtitle, groupContent, explanation, currentQuestion, groups, debouncedOnChange]);
|
|
256
|
-
// Update current question field
|
|
257
|
-
const updateCurrentQuestion = (field, value) => {
|
|
258
|
-
setCurrentQuestion((prev) => ({ ...prev, [field]: value }));
|
|
259
|
-
// Clear validation errors
|
|
260
|
-
if (field === 'question' && validationErrors.question) {
|
|
261
|
-
setValidationErrors(prev => ({ ...prev, question: undefined }));
|
|
262
|
-
}
|
|
263
|
-
if (field === 'correctAnswer' && validationErrors.correctAnswer) {
|
|
264
|
-
setValidationErrors(prev => ({ ...prev, correctAnswer: undefined }));
|
|
265
|
-
}
|
|
266
|
-
};
|
|
267
|
-
// Validate all fields
|
|
268
|
-
const validate = () => {
|
|
269
|
-
const errors = {};
|
|
270
|
-
let isValid = true;
|
|
271
|
-
if (!currentQuestion.question || currentQuestion.question.trim() === '') {
|
|
272
|
-
errors.question = 'Vui lòng nhập câu hỏi';
|
|
273
|
-
isValid = false;
|
|
274
|
-
}
|
|
275
|
-
if (!currentQuestion.correctAnswer || currentQuestion.correctAnswer.trim() === '') {
|
|
276
|
-
errors.correctAnswer = 'Vui lòng nhập đáp án';
|
|
277
|
-
isValid = false;
|
|
278
|
-
}
|
|
279
|
-
setValidationErrors(errors);
|
|
280
|
-
return isValid;
|
|
281
|
-
};
|
|
282
|
-
// Handle save
|
|
283
|
-
const handleSave = () => {
|
|
284
|
-
if (!validate())
|
|
285
|
-
return;
|
|
286
|
-
const payload = {
|
|
287
|
-
question: currentQuestion.question,
|
|
288
|
-
correctAnswer: currentQuestion.correctAnswer,
|
|
289
|
-
suffixText: currentQuestion.suffixText,
|
|
290
|
-
prefixText: currentQuestion.prefixText,
|
|
291
|
-
points: currentQuestion.points || 1,
|
|
292
|
-
groupNumber: currentQuestion.groupNumber,
|
|
293
|
-
isExample: currentQuestion.isExample,
|
|
294
|
-
suggestedAnswers: currentQuestion.suggestedAnswers,
|
|
295
|
-
imageUrl,
|
|
296
|
-
title: groupTitle,
|
|
297
|
-
subtitle: groupSubtitle,
|
|
298
|
-
explanation,
|
|
299
|
-
groups,
|
|
300
|
-
};
|
|
301
|
-
// Update saved state
|
|
302
|
-
savedStateRef.current = getCurrentStateString();
|
|
303
|
-
setHasUnsavedChanges(false);
|
|
304
|
-
onSave?.(payload);
|
|
305
|
-
};
|
|
306
|
-
// Client mode preview
|
|
307
|
-
if (isClientMode) {
|
|
308
|
-
const questionData = {
|
|
309
|
-
question: currentQuestion.question,
|
|
310
|
-
correctAnswer: currentQuestion.correctAnswer,
|
|
311
|
-
suffixText: currentQuestion.suffixText,
|
|
312
|
-
prefixText: currentQuestion.prefixText,
|
|
313
|
-
points: currentQuestion.points,
|
|
314
|
-
groupNumber: currentQuestion.groupNumber,
|
|
315
|
-
isExample: currentQuestion.isExample,
|
|
316
|
-
suggestedAnswers: currentQuestion.suggestedAnswers,
|
|
317
|
-
imageUrl,
|
|
318
|
-
title: groupTitle,
|
|
319
|
-
subtitle: groupSubtitle,
|
|
320
|
-
};
|
|
321
|
-
return (_jsxs("div", { className: "space-y-4", children: [_jsx(Card, { children: _jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Answer the question (Preview)" }), _jsx("p", { className: "mt-1 text-sm text-gray-500", children: "Ch\u1EBF \u0111\u1ED9 xem tr\u01B0\u1EDBc - Hi\u1EC3n th\u1ECB \u0111\u00E1p \u00E1n \u0111\u00FAng" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(false), children: [_jsx(Pencil, { className: "mr-2 h-4 w-4" }), "Quay l\u1EA1i Edit Mode"] })] }) }) }), _jsx(AnswerTheQuestionClient, { questionData: questionData, isReviewMode: true, autoFillCorrectAnswers: true, explanation: explanation })] }));
|
|
322
|
-
}
|
|
323
|
-
// Creator UI
|
|
324
|
-
return (_jsxs("div", { className: "space-y-4", children: [isGrouped && (_jsx(BasicQuestionGroupCard, { groupNumber: currentGroupNumber, title: groupTitle, subtitle: groupSubtitle, content: groupContent, imageUrl: imageUrl, onTitleChange: setGroupTitle, onSubtitleChange: setGroupSubtitle, onContentChange: setGroupContent, onImageChange: setImageUrl, groupConfig: groupConfig, uploadIdPrefix: `answer-the-question-${partId ?? 'group'}` })), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50", children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-teal-500 to-cyan-600 shadow-sm", children: _jsx(HelpCircle, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-base font-bold text-gray-900", children: "\u0110\u1ECDc c\u00E2u h\u1ECFi v\u00E0 vi\u1EBFt \u0111\u00E1p \u00E1n" }), _jsx("p", { className: "text-xs text-gray-500", children: "Nghe - \u0110\u1ECDc c\u00E2u h\u1ECFi v\u00E0 vi\u1EBFt t\u00EAn ho\u1EB7c s\u1ED1" })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [headerExtra, _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), disabled: !currentQuestion.question, className: "gap-2 border-teal-200 text-teal-600 hover:bg-teal-50 hover:text-teal-700", children: [_jsx(Eye, { className: "h-4 w-4" }), "Xem tr\u01B0\u1EDBc"] })] })] }) }), _jsxs(CardContent, { className: "space-y-4 px-4 pb-4", children: [_jsxs("div", { className: "flex items-center gap-6", children: [_jsx(PointsInput, { value: currentQuestion.points || 1, onChange: (e) => updateCurrentQuestion('points', parseFloat(e.target.value) || 1) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Checkbox, { id: "isExample", checked: currentQuestion.isExample || false, onCheckedChange: (checked) => updateCurrentQuestion('isExample', !!checked) }), _jsx(Label, { htmlFor: "isExample", className: "flex items-center gap-1.5 cursor-pointer text-sm font-medium text-gray-700", children: "\u0110\u00E1nh d\u1EA5u l\u00E0 c\u00E2u h\u1ECFi Example" })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "question", className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-4 w-4 text-teal-500" }), "C\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "question", value: currentQuestion.question, onChange: (e) => updateCurrentQuestion('question', e.target.value), placeholder: "Nh\u1EADp c\u00E2u h\u1ECFi... (vd: How many dolls has Lucy got?)", className: `border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200 ${validationErrors.question ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.question && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.question }))] }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "correctAnswer", className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(HelpCircle, { className: "h-4 w-4 text-green-500" }), "\u0110\u00E1p \u00E1n \u0111\u00FAng ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "correctAnswer", value: currentQuestion.correctAnswer, onChange: (e) => updateCurrentQuestion('correctAnswer', e.target.value), placeholder: "Nh\u1EADp \u0111\u00E1p \u00E1n... (vd: 5 ho\u1EB7c Buster)", className: `border-gray-200 bg-white transition-all focus:border-green-300 focus:ring-green-200 ${validationErrors.correctAnswer ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.correctAnswer && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.correctAnswer }))] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "prefixText", className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-4 w-4 text-gray-400" }), "Text tr\u01B0\u1EDBc \u00F4 tr\u1EA3 l\u1EDDi ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(t\u00F9y ch\u1ECDn)" })] }), _jsx(Input, { id: "prefixText", value: currentQuestion.prefixText || '', onChange: (e) => updateCurrentQuestion('prefixText', e.target.value), placeholder: "vd: There are", className: "border-gray-200 bg-white transition-all focus:border-gray-300 focus:ring-gray-200" })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "suffixText", className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-4 w-4 text-gray-400" }), "Text sau \u00F4 tr\u1EA3 l\u1EDDi ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(t\u00F9y ch\u1ECDn)" })] }), _jsx(Input, { id: "suffixText", value: currentQuestion.suffixText || '', onChange: (e) => updateCurrentQuestion('suffixText', e.target.value), placeholder: "vd: school", className: "border-gray-200 bg-white transition-all focus:border-gray-300 focus:ring-gray-200" })] })] }), hasSuggestedAnswers !== false && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(ListChecks, { className: "h-4 w-4 text-purple-500" }), "\u0110\u00E1p \u00E1n g\u1EE3i \u00FD ", _jsx("span", { className: "text-sm font-normal text-gray-500", children: "(t\u00F9y ch\u1ECDn)" })] }), _jsxs("div", { className: "rounded-xl border border-purple-200 bg-gradient-to-r from-purple-50 to-violet-50 p-4 space-y-3", children: [currentQuestion.suggestedAnswers && currentQuestion.suggestedAnswers.length > 0 && (_jsx("div", { className: "flex flex-wrap gap-2", children: currentQuestion.suggestedAnswers.map((suggestion, index) => (_jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-full bg-white border border-purple-200 px-3 py-1 text-sm font-medium text-purple-700 shadow-sm", children: [suggestion, _jsx("button", { type: "button", onClick: () => {
|
|
325
|
-
const updated = [...(currentQuestion.suggestedAnswers || [])];
|
|
326
|
-
updated.splice(index, 1);
|
|
327
|
-
updateCurrentQuestion('suggestedAnswers', updated);
|
|
328
|
-
}, className: "ml-0.5 rounded-full p-0.5 text-purple-400 hover:bg-purple-100 hover:text-purple-600 transition-colors", children: _jsx(X, { className: "h-3 w-3" }) })] }, index))) })), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { id: "newSuggestedAnswer", placeholder: "Nh\u1EADp \u0111\u00E1p \u00E1n g\u1EE3i \u00FD...", className: "flex-1 border-purple-200 bg-white focus:border-purple-400 focus:ring-purple-200", onKeyDown: (e) => {
|
|
329
|
-
if (e.key === 'Enter') {
|
|
330
|
-
e.preventDefault();
|
|
331
|
-
const input = e.currentTarget;
|
|
332
|
-
const value = input.value.trim();
|
|
333
|
-
if (value) {
|
|
334
|
-
const current = currentQuestion.suggestedAnswers || [];
|
|
335
|
-
updateCurrentQuestion('suggestedAnswers', [...current, value]);
|
|
336
|
-
input.value = '';
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
} }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", className: "gap-1 border-purple-300 text-purple-600 hover:bg-purple-100 hover:text-purple-700", onClick: () => {
|
|
340
|
-
const input = document.getElementById('newSuggestedAnswer');
|
|
341
|
-
const value = input?.value.trim();
|
|
342
|
-
if (value) {
|
|
343
|
-
const current = currentQuestion.suggestedAnswers || [];
|
|
344
|
-
updateCurrentQuestion('suggestedAnswers', [...current, value]);
|
|
345
|
-
input.value = '';
|
|
346
|
-
}
|
|
347
|
-
}, children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm"] })] }), _jsx("p", { className: "text-xs text-purple-500", children: "Nh\u1EADp \u0111\u00E1p \u00E1n g\u1EE3i \u00FD r\u1ED3i nh\u1EA5n Enter ho\u1EB7c n\u00FAt Th\u00EAm" })] })] })), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100", children: _jsx("span", { className: "text-lg", children: "\u274C" }) }), _jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("ul", { className: "mt-1 list-inside list-disc text-red-700", children: externalErrors.map((error, index) => (_jsx("li", { children: error }, index))) })] })] }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50", children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm", children: _jsx(BookOpen, { className: "h-3.5 w-3.5 text-white" }) }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-base font-semibold text-gray-800", children: "Gi\u1EA3i th\u00EDch" }), _jsx("span", { className: "inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-600", children: "Kh\u00F4ng b\u1EAFt bu\u1ED9c" })] }), _jsx("p", { className: "text-xs text-gray-500 mt-0.5", children: "Gi\u1EA3i th\u00EDch cho \u0111\u00E1p \u00E1n \u0111\u00FAng \u2014 hi\u1EC3n th\u1ECB sau khi h\u1ECDc sinh ho\u00E0n th\u00E0nh c\u00E2u h\u1ECFi" })] })] }) }), _jsx(CardContent, { className: "px-4 pb-4", children: _jsx(Textarea, { id: "explanation", value: explanation, onChange: (e) => setExplanation(e.target.value), placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0... v\u00EC...", rows: 3, className: "min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none" }) })] })] }));
|
|
348
|
-
}
|
|
349
|
-
export function AnswerTheQuestionCreator(props) {
|
|
350
|
-
return _jsx(AnswerTheQuestionContent, { ...props });
|
|
351
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useState as r,useRef as s,useEffect as n}from"react";import{Button as i}from"../../../../components/ui/button";import{Label as a}from"../../../../components/ui/label";import{Input as o}from"../../../../components/ui/input";import{Textarea as l}from"../../../../components/ui/textarea";import{PointsInput as c}from"../../../../components/ui/points-input";import{Card as u,CardContent as d,CardHeader as m,CardTitle as p}from"../../../../components/ui/card";import{Pencil as g,Eye as x,HelpCircle as h,BookOpen as f,Plus as b,X as w,ListChecks as N,MessageSquare as v}from"lucide-react";import{Checkbox as y}from"../../../../components/ui/checkbox";import{AnswerTheQuestionClient as A}from"./AnswerTheQuestionClient";import{useDebouncedCallback as T}from"../../../../shared/lib/hooks";import{BasicQuestionGroupCard as q}from"../../groups/BasicQuestionGroupCard";import{useBasicQuestionGroup as C,getGroupNumberForIndex as E}from"../../_shared/hooks/useBasicQuestionGroup";function k({initialData:k,onSave:G,onSaveToQuestionBank:U,onCancel:S,onChange:I,onUnsavedChangesChange:D,externalErrors:j,groups:F,questionIndexInPart:Q=0,partId:B="",validationRef:z,suggestedAnswers:P,groupConfig:V,hasSuggestedAnswers:$,headerExtra:M}){const H=()=>k?.question?{id:`sq-${Date.now()}-imported`,question:k.question,correctAnswer:k.correctAnswer||"",suffixText:k.suffixText||"",prefixText:k.prefixText||"",points:k.points||1,groupNumber:E(Q,F),isExample:k.isExample||!1,suggestedAnswers:k.suggestedAnswers?.length?k.suggestedAnswers:P||[]}:(()=>{const e=E(Q,F);return{id:`sq-${Date.now()}-${Math.random()}`,question:"",correctAnswer:"",suffixText:"",prefixText:"",points:1,groupNumber:e,isExample:!1,suggestedAnswers:P||[]}})(),[J,K]=r(H),{isGrouped:L,currentGroupNumber:O,groupTitle:R,groupSubtitle:X,groupContent:_,imageUrl:W,setGroupTitle:Y,setGroupSubtitle:Z,setGroupContent:ee,setImageUrl:te}=C({partId:B,groups:F,questionIndexInPart:Q,initialData:{title:(()=>{const e=k?.content,t=k?.answer,r=k?.questionGroup?.payload;return t?.title||e?.passageTitle||r?.title||k?.title||""})(),subtitle:(()=>{const e=k?.content,t=k?.questionGroup?.payload;return e?.passageSubtitle||t?.subtitle||""})(),content:(()=>{const e=k?.content,t=k?.questionGroup?.payload;return k?.groupContent||t?.content||e?.passage||""})(),imageUrl:(()=>{const e=k?.content,t=k?.answer,r=k?.questionGroup?.payload;return t?.imageUrl||e?.imageUrl||r?.imageUrl||k?.imageUrl||""})()}}),[re,se]=r(!1),[ne,ie]=r(!1),[ae,oe]=r(k?.explanation||""),[le,ce]=r({}),ue=s(""),de=s(k),me=s(Q),pe=s(!1),ge=s(!1),xe=s(I),he=s(""),fe=O||J.groupNumber||1,be=s(D);n(()=>{be.current=D},[D]),n(()=>{xe.current=I},[I]);const we=T(e=>{xe.current?.(e)},300),Ne=()=>JSON.stringify({imageUrl:W,title:R,subtitle:X,question:J.question,correctAnswer:J.correctAnswer,suffixText:J.suffixText,prefixText:J.prefixText,points:J.points,isExample:J.isExample,suggestedAnswers:J.suggestedAnswers});n(()=>{if(pe.current)return;const e=setTimeout(()=>{ue.current=Ne(),ie(!1)},150);return()=>clearTimeout(e)},[Q]),n(()=>{if(pe.current)return;if(!ue.current)return;const e=Ne()!==ue.current;e!==ne&&(ie(e),be.current?.(e))},[W,J,ne]),n(()=>{z&&(z.current={triggerValidation:async()=>{const e={};let t=!0;return J.question&&""!==J.question.trim()||(e.question="Vui lòng nhập câu hỏi",t=!1),J.correctAnswer&&""!==J.correctAnswer.trim()||(e.correctAnswer="Vui lòng nhập đáp án",t=!1),ce(e),t},getFormData:()=>({imageUrl:W,title:R,subtitle:X,question:J.question,correctAnswer:J.correctAnswer,suffixText:J.suffixText,prefixText:J.prefixText,points:J.points,groupNumber:J.groupNumber,isExample:J.isExample,suggestedAnswers:J.suggestedAnswers,explanation:ae,groups:F})})},[z,J,W,R,X,ae,F]),n(()=>{const e=Q!==me.current,t=!!k?.question;if(!(!ge.current&&t)&&!e)return;pe.current=!0,de.current=k,me.current=Q,ge.current=!0;const r=E(Q,F);K({...H(),groupNumber:r}),oe(k?.explanation||""),setTimeout(()=>{pe.current=!1},100)},[k,F,Q]),n(()=>{if(pe.current)return;const e={question:J.question,correctAnswer:J.correctAnswer,suffixText:J.suffixText,prefixText:J.prefixText,points:J.points||1,groupNumber:J.groupNumber,isExample:J.isExample,suggestedAnswers:J.suggestedAnswers,imageUrl:W,title:R,subtitle:X,groupContent:_,explanation:ae,groups:F},t=JSON.stringify(e);he.current!==t&&(he.current=t,we(e))},[W,R,X,_,ae,J,F,we]);const ve=(e,t)=>{K(r=>({...r,[e]:t})),"question"===e&&le.question&&ce(e=>({...e,question:void 0})),"correctAnswer"===e&&le.correctAnswer&&ce(e=>({...e,correctAnswer:void 0}))};if(re){const r={question:J.question,correctAnswer:J.correctAnswer,suffixText:J.suffixText,prefixText:J.prefixText,points:J.points,groupNumber:J.groupNumber,isExample:J.isExample,suggestedAnswers:J.suggestedAnswers,imageUrl:W,title:R,subtitle:X};return t("div",{className:"space-y-4",children:[e(u,{children:e(m,{children:t("div",{className:"flex items-center justify-between",children:[t("div",{children:[e(p,{children:"Answer the question (Preview)"}),e("p",{className:"mt-1 text-sm text-gray-500",children:"Chế độ xem trước - Hiển thị đáp án đúng"})]}),t(i,{variant:"outline",size:"sm",onClick:()=>se(!1),children:[e(g,{className:"mr-2 h-4 w-4"}),"Quay lại Edit Mode"]})]})})}),e(A,{questionData:r,isReviewMode:!0,autoFillCorrectAnswers:!0,explanation:ae})]})}return t("div",{className:"space-y-4",children:[L&&e(q,{groupNumber:fe,title:R,subtitle:X,content:_,imageUrl:W,onTitleChange:Y,onSubtitleChange:Z,onContentChange:ee,onImageChange:te,groupConfig:V,uploadIdPrefix:`answer-the-question-${B??"group"}`}),t(u,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-blue-100/50",children:[e(m,{className:"px-4 py-3",children:t("div",{className:"flex items-center justify-between",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-teal-500 to-cyan-600 shadow-sm",children:e(h,{className:"h-4 w-4 text-white"})}),t("div",{children:[e(p,{className:"text-base font-bold text-gray-900",children:"Đọc câu hỏi và viết đáp án"}),e("p",{className:"text-xs text-gray-500",children:"Nghe - Đọc câu hỏi và viết tên hoặc số"})]})]}),t("div",{className:"flex items-center gap-2",children:[M,t(i,{variant:"outline",size:"sm",onClick:()=>se(!0),disabled:!J.question,className:"gap-2 border-teal-200 text-teal-600 hover:bg-teal-50 hover:text-teal-700",children:[e(x,{className:"h-4 w-4"}),"Xem trước"]})]})]})}),t(d,{className:"space-y-4 px-4 pb-4",children:[t("div",{className:"flex items-center gap-6",children:[e(c,{value:J.points||1,onChange:e=>ve("points",parseFloat(e.target.value)||1)}),t("div",{className:"flex items-center gap-2",children:[e(y,{id:"isExample",checked:J.isExample||!1,onCheckedChange:e=>ve("isExample",!!e)}),e(a,{htmlFor:"isExample",className:"flex items-center gap-1.5 cursor-pointer text-sm font-medium text-gray-700",children:"Đánh dấu là câu hỏi Example"})]})]}),t("div",{className:"space-y-2",children:[t(a,{htmlFor:"question",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(v,{className:"h-4 w-4 text-teal-500"}),"Câu hỏi ",e("span",{className:"text-red-500",children:"*"})]}),e(o,{id:"question",value:J.question,onChange:e=>ve("question",e.target.value),placeholder:"Nhập câu hỏi... (vd: How many dolls has Lucy got?)",className:"border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200 "+(le.question?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),le.question&&e("p",{className:"text-sm text-red-600",children:le.question})]}),t("div",{className:"space-y-2",children:[t(a,{htmlFor:"correctAnswer",className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(h,{className:"h-4 w-4 text-green-500"}),"Đáp án đúng ",e("span",{className:"text-red-500",children:"*"})]}),e(o,{id:"correctAnswer",value:J.correctAnswer,onChange:e=>ve("correctAnswer",e.target.value),placeholder:"Nhập đáp án... (vd: 5 hoặc Buster)",className:"border-gray-200 bg-white transition-all focus:border-green-300 focus:ring-green-200 "+(le.correctAnswer?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),le.correctAnswer&&e("p",{className:"text-sm text-red-600",children:le.correctAnswer})]}),t("div",{className:"grid grid-cols-2 gap-4",children:[t("div",{className:"space-y-2",children:[t(a,{htmlFor:"prefixText",className:"flex items-center gap-2 text-sm font-semibold text-gray-800",children:[e(v,{className:"h-4 w-4 text-gray-400"}),"Text trước ô trả lời ",e("span",{className:"text-xs font-normal text-gray-500",children:"(tùy chọn)"})]}),e(o,{id:"prefixText",value:J.prefixText||"",onChange:e=>ve("prefixText",e.target.value),placeholder:"vd: There are",className:"border-gray-200 bg-white transition-all focus:border-gray-300 focus:ring-gray-200"})]}),t("div",{className:"space-y-2",children:[t(a,{htmlFor:"suffixText",className:"flex items-center gap-2 text-sm font-semibold text-gray-800",children:[e(v,{className:"h-4 w-4 text-gray-400"}),"Text sau ô trả lời ",e("span",{className:"text-xs font-normal text-gray-500",children:"(tùy chọn)"})]}),e(o,{id:"suffixText",value:J.suffixText||"",onChange:e=>ve("suffixText",e.target.value),placeholder:"vd: school",className:"border-gray-200 bg-white transition-all focus:border-gray-300 focus:ring-gray-200"})]})]}),!1!==$&&t("div",{className:"space-y-2",children:[t(a,{className:"flex items-center gap-2 text-base font-semibold text-gray-800",children:[e(N,{className:"h-4 w-4 text-purple-500"}),"Đáp án gợi ý ",e("span",{className:"text-sm font-normal text-gray-500",children:"(tùy chọn)"})]}),t("div",{className:"rounded-xl border border-purple-200 bg-gradient-to-r from-purple-50 to-violet-50 p-4 space-y-3",children:[J.suggestedAnswers&&J.suggestedAnswers.length>0&&e("div",{className:"flex flex-wrap gap-2",children:J.suggestedAnswers.map((r,s)=>t("span",{className:"inline-flex items-center gap-1.5 rounded-full bg-white border border-purple-200 px-3 py-1 text-sm font-medium text-purple-700 shadow-sm",children:[r,e("button",{type:"button",onClick:()=>{const e=[...J.suggestedAnswers||[]];e.splice(s,1),ve("suggestedAnswers",e)},className:"ml-0.5 rounded-full p-0.5 text-purple-400 hover:bg-purple-100 hover:text-purple-600 transition-colors",children:e(w,{className:"h-3 w-3"})})]},s))}),t("div",{className:"flex items-center gap-2",children:[e(o,{id:"newSuggestedAnswer",placeholder:"Nhập đáp án gợi ý...",className:"flex-1 border-purple-200 bg-white focus:border-purple-400 focus:ring-purple-200",onKeyDown:e=>{if("Enter"===e.key){e.preventDefault();const t=e.currentTarget,r=t.value.trim();if(r){const e=J.suggestedAnswers||[];ve("suggestedAnswers",[...e,r]),t.value=""}}}}),t(i,{type:"button",variant:"outline",size:"sm",className:"gap-1 border-purple-300 text-purple-600 hover:bg-purple-100 hover:text-purple-700",onClick:()=>{const e=document.getElementById("newSuggestedAnswer"),t=e?.value.trim();if(t){const r=J.suggestedAnswers||[];ve("suggestedAnswers",[...r,t]),e.value=""}},children:[e(b,{className:"h-3.5 w-3.5"}),"Thêm"]})]}),e("p",{className:"text-xs text-purple-500",children:"Nhập đáp án gợi ý rồi nhấn Enter hoặc nút Thêm"})]})]}),j&&j.length>0&&t("div",{className:"flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4",children:[e("div",{className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100",children:e("span",{className:"text-lg",children:"❌"})}),t("div",{className:"text-sm",children:[e("p",{className:"font-semibold text-red-800",children:"Lỗi:"}),e("ul",{className:"mt-1 list-inside list-disc text-red-700",children:j.map((t,r)=>e("li",{children:t},r))})]})]})]})]}),t(u,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50",children:[e(m,{className:"px-4 py-3",children:t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm",children:e(f,{className:"h-3.5 w-3.5 text-white"})}),t("div",{children:[t("div",{className:"flex items-center gap-2",children:[e(p,{className:"text-base font-semibold text-gray-800",children:"Giải thích"}),e("span",{className:"inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-600",children:"Không bắt buộc"})]}),e("p",{className:"text-xs text-gray-500 mt-0.5",children:"Giải thích cho đáp án đúng — hiển thị sau khi học sinh hoàn thành câu hỏi"})]})]})}),e(d,{className:"px-4 pb-4",children:e(l,{id:"explanation",value:ae,onChange:e=>oe(e.target.value),placeholder:"VD: Câu trả lời đúng là... vì...",rows:3,className:"min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none"})})]})]})}export function AnswerTheQuestionCreator(t){return e(k,{...t})}
|
|
@@ -1,67 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
export const answerTheQuestionRegistration = {
|
|
3
|
-
component: AnswerTheQuestionCreator,
|
|
4
|
-
transformInitialData: (initialData) => {
|
|
5
|
-
const content = initialData?.content || {};
|
|
6
|
-
const answer = initialData?.answer || {};
|
|
7
|
-
const explanation = initialData?.explanation || answer?.explanation || '';
|
|
8
|
-
const apiPoints = initialData?.total_points
|
|
9
|
-
? parseFloat(initialData.total_points)
|
|
10
|
-
: (initialData?.points || 1);
|
|
11
|
-
// Extract imageUrl, title, and content from questionGroup.payload (API format) or fallback
|
|
12
|
-
const questionGroup = initialData?.questionGroup || {};
|
|
13
|
-
const groupPayload = questionGroup?.payload || {};
|
|
14
|
-
const imageUrl = groupPayload?.imageUrl || answer?.imageUrl || content?.imageUrl || '';
|
|
15
|
-
const groupTitle = groupPayload?.title || answer?.groupTitle || '';
|
|
16
|
-
const groupContent = groupPayload?.content || answer?.groupContent || content?.passage || '';
|
|
17
|
-
const groupNumber = questionGroup?.groupNumber || 1;
|
|
18
|
-
// Flat format matching AnswerTheQuestionData
|
|
19
|
-
// Supports both API format (content.question, answer.answer)
|
|
20
|
-
// and internal onChange format (answer.question, answer.correctAnswer)
|
|
21
|
-
return {
|
|
22
|
-
question: content?.question || answer?.question || '',
|
|
23
|
-
correctAnswer: answer?.answer || answer?.correctAnswer || '',
|
|
24
|
-
suffixText: content?.suffixText || answer?.suffixText || '',
|
|
25
|
-
prefixText: content?.prefixText || answer?.prefixText || '',
|
|
26
|
-
points: answer?.points || apiPoints,
|
|
27
|
-
groupNumber: answer?.groupNumber || groupNumber,
|
|
28
|
-
isExample: initialData?.isExample || answer?.isExample || false,
|
|
29
|
-
suggestedAnswers: initialData?.suggestedAnswers || answer?.suggestedAnswers || [],
|
|
30
|
-
imageUrl,
|
|
31
|
-
title: groupTitle,
|
|
32
|
-
groupContent,
|
|
33
|
-
explanation,
|
|
34
|
-
groups: answer?.groups || initialData?.groups,
|
|
35
|
-
};
|
|
36
|
-
},
|
|
37
|
-
getExtraProps: (ctx) => ({
|
|
38
|
-
groups: ctx.groups,
|
|
39
|
-
questionIndexInPart: ctx.questionIndexInPart,
|
|
40
|
-
partId: ctx.partId,
|
|
41
|
-
validationRef: ctx.validationRef,
|
|
42
|
-
onUnsavedChangesChange: ctx.onUnsavedChangesChange,
|
|
43
|
-
suggestedAnswers: ctx.suggestedAnswers,
|
|
44
|
-
hasSuggestedAnswers: ctx.hasSuggestedAnswers,
|
|
45
|
-
groupConfig: ctx.groupConfig,
|
|
46
|
-
}),
|
|
47
|
-
wrapOnSave: (data, ctx) => ({
|
|
48
|
-
type: ctx.questionType,
|
|
49
|
-
content: ctx.state.content,
|
|
50
|
-
points: data?.points ?? ctx.state.points ?? 1,
|
|
51
|
-
level: ctx.state.level,
|
|
52
|
-
difficulty: ctx.state.difficulty,
|
|
53
|
-
skill: ctx.state.skill,
|
|
54
|
-
answer: data,
|
|
55
|
-
explanation: data?.explanation || '',
|
|
56
|
-
}),
|
|
57
|
-
wrapOnChange: (data, ctx) => ({
|
|
58
|
-
type: ctx.questionType,
|
|
59
|
-
content: ctx.state.content,
|
|
60
|
-
points: data?.points ?? ctx.state.points ?? 1,
|
|
61
|
-
level: ctx.state.level,
|
|
62
|
-
difficulty: ctx.state.difficulty,
|
|
63
|
-
skill: ctx.state.skill,
|
|
64
|
-
answer: data,
|
|
65
|
-
explanation: data?.explanation || '',
|
|
66
|
-
}),
|
|
67
|
-
};
|
|
1
|
+
import{AnswerTheQuestionCreator as e}from"./AnswerTheQuestionCreator";export const answerTheQuestionRegistration={component:e,transformInitialData:e=>{const t=e?.content||{},s=e?.answer||{},n=e?.explanation||s?.explanation||"",o=e?.total_points?parseFloat(e.total_points):e?.points||1,a=e?.questionGroup||{},i=a?.payload||{},r=i?.imageUrl||s?.imageUrl||t?.imageUrl||"",p=i?.title||s?.groupTitle||"",l=i?.content||s?.groupContent||t?.passage||"",u=a?.groupNumber||1;return{question:t?.question||s?.question||"",correctAnswer:s?.answer||s?.correctAnswer||"",suffixText:t?.suffixText||s?.suffixText||"",prefixText:t?.prefixText||s?.prefixText||"",points:s?.points||o,groupNumber:s?.groupNumber||u,isExample:e?.isExample||s?.isExample||!1,suggestedAnswers:e?.suggestedAnswers||s?.suggestedAnswers||[],imageUrl:r,title:p,groupContent:l,explanation:n,groups:s?.groups||e?.groups}},getExtraProps:e=>({groups:e.groups,questionIndexInPart:e.questionIndexInPart,partId:e.partId,validationRef:e.validationRef,onUnsavedChangesChange:e.onUnsavedChangesChange,suggestedAnswers:e.suggestedAnswers,hasSuggestedAnswers:e.hasSuggestedAnswers,groupConfig:e.groupConfig}),wrapOnSave:(e,t)=>({type:t.questionType,content:t.state.content,points:e?.points??t.state.points??1,level:t.state.level,difficulty:t.state.difficulty,skill:t.state.skill,answer:e,explanation:e?.explanation||""}),wrapOnChange:(e,t)=>({type:t.questionType,content:t.state.content,points:e?.points??t.state.points??1,level:t.state.level,difficulty:t.state.difficulty,skill:t.state.skill,answer:e,explanation:e?.explanation||""})};
|
|
@@ -1,36 +1 @@
|
|
|
1
|
-
export const transformAnswerTheQuestion =
|
|
2
|
-
const answerData = question.answer;
|
|
3
|
-
if (!answerData?.question) {
|
|
4
|
-
return { apiContent: {}, apiCorrectAnswer: {} };
|
|
5
|
-
}
|
|
6
|
-
const apiContent = {
|
|
7
|
-
question: answerData.question,
|
|
8
|
-
};
|
|
9
|
-
if (answerData.suffixText) {
|
|
10
|
-
apiContent.suffixText = answerData.suffixText;
|
|
11
|
-
}
|
|
12
|
-
if (answerData.prefixText) {
|
|
13
|
-
apiContent.prefixText = answerData.prefixText;
|
|
14
|
-
}
|
|
15
|
-
// Build basicGroupData when any group field is present (title, imageUrl, groupContent)
|
|
16
|
-
const hasGroupData = !!(answerData.imageUrl || answerData.title || answerData.subtitle || answerData.groupContent);
|
|
17
|
-
const basicGroupData = hasGroupData
|
|
18
|
-
? {
|
|
19
|
-
title: answerData.title || '',
|
|
20
|
-
subtitle: answerData.subtitle || '',
|
|
21
|
-
content: answerData.groupContent || '',
|
|
22
|
-
imageUrl: answerData.imageUrl || '',
|
|
23
|
-
}
|
|
24
|
-
: undefined;
|
|
25
|
-
return {
|
|
26
|
-
apiContent,
|
|
27
|
-
apiCorrectAnswer: {
|
|
28
|
-
answer: answerData.correctAnswer,
|
|
29
|
-
},
|
|
30
|
-
totalPoints: answerData.points || 1,
|
|
31
|
-
basicGroupData,
|
|
32
|
-
groupNumber: answerData.groupNumber || 1,
|
|
33
|
-
isExample: answerData.isExample || false,
|
|
34
|
-
suggestedAnswers: (answerData.suggestedAnswers && answerData.suggestedAnswers.length > 0) ? answerData.suggestedAnswers : undefined,
|
|
35
|
-
};
|
|
36
|
-
};
|
|
1
|
+
export const transformAnswerTheQuestion=e=>{const t=e.answer;if(!t?.question)return{apiContent:{},apiCorrectAnswer:{}};const s={question:t.question};t.suffixText&&(s.suffixText=t.suffixText),t.prefixText&&(s.prefixText=t.prefixText);const r=!!(t.imageUrl||t.title||t.subtitle||t.groupContent)?{title:t.title||"",subtitle:t.subtitle||"",content:t.groupContent||"",imageUrl:t.imageUrl||""}:void 0;return{apiContent:s,apiCorrectAnswer:{answer:t.correctAnswer},totalPoints:t.points||1,basicGroupData:r,groupNumber:t.groupNumber||1,isExample:t.isExample||!1,suggestedAnswers:t.suggestedAnswers&&t.suggestedAnswers.length>0?t.suggestedAnswers:void 0}};
|