@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,230 +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 { Input } from '../../../../components/ui/input';
|
|
6
|
-
import { Textarea } from '../../../../components/ui/textarea';
|
|
7
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
|
|
8
|
-
import { PointsInput } from '../../../../components/ui/points-input';
|
|
9
|
-
import { RadioGroup } from '../../../../components/ui/radio-group';
|
|
10
|
-
import { Pencil, Eye, Plus, Trash2, Image, CheckCircle2, Lightbulb, BookOpen } from 'lucide-react';
|
|
11
|
-
import { FileUpload } from '../../../../components/ui/file-upload';
|
|
12
|
-
import { ImagePreview } from '../../../../components/ui/image-preview';
|
|
13
|
-
import { usePresignedFileUrl, useDebouncedCallback } from '../../../../shared/lib/hooks';
|
|
14
|
-
import { LookPictureChooseCorrectAnswerClient } from './LookPictureChooseCorrectAnswerClient';
|
|
15
|
-
import { useReactHookForm } from '../../../../shared/lib/hooks/useReactHookForm';
|
|
16
|
-
import { lookPictureChooseCorrectAnswerFormSchema, } from '../../../../shared/lib/validation/look-picture-choose-correct-answer.validation';
|
|
17
|
-
function LookPictureChooseCorrectAnswerContent({ initialData, onSave, onCancel, onChange, onUnsavedChangesChange, optionCount = 2, validationRef, }) {
|
|
18
|
-
// Helper to normalize options - ensure they are always string[]
|
|
19
|
-
const normalizeOptions = (opts, defaultCount) => {
|
|
20
|
-
if (!opts || opts.length === 0) {
|
|
21
|
-
return Array.from({ length: defaultCount }, () => '');
|
|
22
|
-
}
|
|
23
|
-
// Options should always be string[] - convert each to string for safety
|
|
24
|
-
return opts.map((opt) => String(opt || ''));
|
|
25
|
-
};
|
|
26
|
-
// Use react-hook-form with Zod validation
|
|
27
|
-
// initialData can be in API format (content.imageUrl, correctAnswer.answer) or flat format
|
|
28
|
-
const apiData = initialData;
|
|
29
|
-
const rawOptions = apiData?.content?.options || apiData?.options || [];
|
|
30
|
-
const form = useReactHookForm({
|
|
31
|
-
defaultValues: {
|
|
32
|
-
// Support both API format (content.imageUrl) and flat format (imageUrl)
|
|
33
|
-
imageUrl: apiData?.content?.imageUrl || apiData?.imageUrl || '',
|
|
34
|
-
// Use normalizeOptions to ensure options are always string[]
|
|
35
|
-
options: normalizeOptions(rawOptions, optionCount),
|
|
36
|
-
// Support both API format (correctAnswer.answer) and flat format (correctAnswer as number)
|
|
37
|
-
correctAnswer: typeof apiData?.correctAnswer === 'object'
|
|
38
|
-
? (apiData?.correctAnswer?.answer ?? -1)
|
|
39
|
-
: (apiData?.correctAnswer ?? -1),
|
|
40
|
-
explanation: apiData?.explanation || '',
|
|
41
|
-
points: apiData?.points ?? 1,
|
|
42
|
-
},
|
|
43
|
-
zodSchema: lookPictureChooseCorrectAnswerFormSchema,
|
|
44
|
-
mode: 'onBlur',
|
|
45
|
-
});
|
|
46
|
-
// UI State
|
|
47
|
-
const [isClientMode, setIsClientMode] = useState(false);
|
|
48
|
-
const [imagePreviewUrl, setImagePreviewUrl] = useState('');
|
|
49
|
-
// Track previous initialData to detect changes
|
|
50
|
-
const previousInitialDataRef = useRef(initialData);
|
|
51
|
-
const isInitializingRef = useRef(false);
|
|
52
|
-
const onChangeRef = useRef(onChange);
|
|
53
|
-
const previousPayloadRef = useRef('');
|
|
54
|
-
// Update onChange ref when it changes
|
|
55
|
-
useEffect(() => {
|
|
56
|
-
onChangeRef.current = onChange;
|
|
57
|
-
}, [onChange]);
|
|
58
|
-
// Debounced onChange callback to prevent excessive re-renders
|
|
59
|
-
const debouncedOnChange = useDebouncedCallback((data) => {
|
|
60
|
-
onChangeRef.current?.(data);
|
|
61
|
-
}, 300);
|
|
62
|
-
// Watch form values - MUST be declared before useEffects that use them
|
|
63
|
-
const watchedImageUrl = form.watch('imageUrl');
|
|
64
|
-
const watchedOptions = form.watch('options');
|
|
65
|
-
const watchedCorrectAnswer = form.watch('correctAnswer');
|
|
66
|
-
const watchedExplanation = form.watch('explanation');
|
|
67
|
-
const watchedPoints = form.watch('points');
|
|
68
|
-
// Custom hooks for presigned URL
|
|
69
|
-
const { previewUrl: generatedPreviewUrl } = usePresignedFileUrl(watchedImageUrl);
|
|
70
|
-
const displayPreviewUrl = imagePreviewUrl || generatedPreviewUrl || '';
|
|
71
|
-
// Expose validation function and getFormData via ref
|
|
72
|
-
useEffect(() => {
|
|
73
|
-
if (validationRef) {
|
|
74
|
-
validationRef.current = {
|
|
75
|
-
triggerValidation: async () => {
|
|
76
|
-
const result = await form.trigger();
|
|
77
|
-
// Also check correctAnswer
|
|
78
|
-
if (watchedCorrectAnswer === -1) {
|
|
79
|
-
form.setError('correctAnswer', { message: 'Vui lòng chọn đáp án đúng!' });
|
|
80
|
-
return false;
|
|
81
|
-
}
|
|
82
|
-
return result;
|
|
83
|
-
},
|
|
84
|
-
// Return current form data immediately (not debounced)
|
|
85
|
-
getFormData: () => ({
|
|
86
|
-
imageUrl: watchedImageUrl,
|
|
87
|
-
options: watchedOptions,
|
|
88
|
-
correctAnswer: watchedCorrectAnswer,
|
|
89
|
-
explanation: watchedExplanation,
|
|
90
|
-
points: watchedPoints,
|
|
91
|
-
}),
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
}, [validationRef, form, watchedCorrectAnswer, watchedImageUrl, watchedOptions, watchedExplanation, watchedPoints]);
|
|
95
|
-
// Sync state from initialData when it changes from EXTERNAL sources only
|
|
96
|
-
useEffect(() => {
|
|
97
|
-
const apiData = initialData;
|
|
98
|
-
const normalizedInitialData = {
|
|
99
|
-
imageUrl: apiData?.content?.imageUrl || apiData?.imageUrl || '',
|
|
100
|
-
options: normalizeOptions(apiData?.content?.options || apiData?.options || [], optionCount),
|
|
101
|
-
correctAnswer: typeof apiData?.correctAnswer === 'object'
|
|
102
|
-
? (apiData?.correctAnswer?.answer ?? -1)
|
|
103
|
-
: (apiData?.correctAnswer ?? -1),
|
|
104
|
-
explanation: apiData?.explanation || '',
|
|
105
|
-
points: apiData?.points ?? 1,
|
|
106
|
-
};
|
|
107
|
-
const initialDataChanged = JSON.stringify(previousInitialDataRef.current) !== JSON.stringify(initialData);
|
|
108
|
-
if (!initialDataChanged) {
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
previousInitialDataRef.current = initialData;
|
|
112
|
-
const currentFormData = {
|
|
113
|
-
imageUrl: watchedImageUrl,
|
|
114
|
-
options: watchedOptions,
|
|
115
|
-
correctAnswer: watchedCorrectAnswer,
|
|
116
|
-
explanation: watchedExplanation,
|
|
117
|
-
points: watchedPoints,
|
|
118
|
-
};
|
|
119
|
-
const isEchoFromOnChange = currentFormData.imageUrl === normalizedInitialData.imageUrl &&
|
|
120
|
-
currentFormData.correctAnswer === normalizedInitialData.correctAnswer &&
|
|
121
|
-
currentFormData.points === normalizedInitialData.points &&
|
|
122
|
-
currentFormData.explanation === normalizedInitialData.explanation &&
|
|
123
|
-
JSON.stringify(currentFormData.options) === JSON.stringify(normalizedInitialData.options);
|
|
124
|
-
const wasJustSaved = initialData?.isCompleted === true;
|
|
125
|
-
if (isEchoFromOnChange && wasJustSaved) {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
const shouldReset = !isEchoFromOnChange &&
|
|
129
|
-
normalizedInitialData.correctAnswer !== currentFormData.correctAnswer;
|
|
130
|
-
if (shouldReset && normalizedInitialData.correctAnswer !== -1) {
|
|
131
|
-
isInitializingRef.current = true;
|
|
132
|
-
form.reset(normalizedInitialData);
|
|
133
|
-
setTimeout(() => {
|
|
134
|
-
isInitializingRef.current = false;
|
|
135
|
-
}, 150);
|
|
136
|
-
}
|
|
137
|
-
}, [initialData, optionCount, form, watchedImageUrl, watchedOptions, watchedCorrectAnswer, watchedExplanation, watchedPoints]);
|
|
138
|
-
// Convert to payload format and call onChange
|
|
139
|
-
const watchedOptionsString = JSON.stringify(watchedOptions);
|
|
140
|
-
useEffect(() => {
|
|
141
|
-
if (isInitializingRef.current) {
|
|
142
|
-
const currentPayload = JSON.stringify({
|
|
143
|
-
imageUrl: watchedImageUrl,
|
|
144
|
-
options: watchedOptions,
|
|
145
|
-
correctAnswer: watchedCorrectAnswer,
|
|
146
|
-
explanation: watchedExplanation,
|
|
147
|
-
points: watchedPoints,
|
|
148
|
-
});
|
|
149
|
-
previousPayloadRef.current = currentPayload;
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
const payload = {
|
|
153
|
-
imageUrl: watchedImageUrl,
|
|
154
|
-
options: watchedOptions,
|
|
155
|
-
correctAnswer: watchedCorrectAnswer,
|
|
156
|
-
explanation: watchedExplanation,
|
|
157
|
-
points: watchedPoints,
|
|
158
|
-
};
|
|
159
|
-
const payloadString = JSON.stringify(payload);
|
|
160
|
-
if (previousPayloadRef.current !== payloadString) {
|
|
161
|
-
previousPayloadRef.current = payloadString;
|
|
162
|
-
debouncedOnChange(payload);
|
|
163
|
-
}
|
|
164
|
-
}, [
|
|
165
|
-
watchedImageUrl,
|
|
166
|
-
watchedOptionsString,
|
|
167
|
-
watchedCorrectAnswer,
|
|
168
|
-
watchedExplanation,
|
|
169
|
-
watchedPoints,
|
|
170
|
-
]);
|
|
171
|
-
// Handle save with validation
|
|
172
|
-
const handleSave = form.handleSubmit((data) => {
|
|
173
|
-
if (data.correctAnswer === -1 || data.correctAnswer >= data.options.length) {
|
|
174
|
-
form.setError('correctAnswer', { message: 'Vui lòng chọn đáp án đúng!' });
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
onSave?.(data);
|
|
178
|
-
});
|
|
179
|
-
// Client mode preview
|
|
180
|
-
if (isClientMode) {
|
|
181
|
-
const questionData = {
|
|
182
|
-
subQuestions: [{
|
|
183
|
-
id: 'preview-sq',
|
|
184
|
-
imageUrl: displayPreviewUrl || watchedImageUrl,
|
|
185
|
-
question: '',
|
|
186
|
-
options: watchedOptions.map((text, index) => ({
|
|
187
|
-
id: `opt-${index}`,
|
|
188
|
-
text,
|
|
189
|
-
isCorrect: index === watchedCorrectAnswer,
|
|
190
|
-
})),
|
|
191
|
-
}],
|
|
192
|
-
};
|
|
193
|
-
const correctAnswers = watchedCorrectAnswer !== -1
|
|
194
|
-
? { 'preview-sq': `opt-${watchedCorrectAnswer}` }
|
|
195
|
-
: {};
|
|
196
|
-
return (_jsx("div", { className: "space-y-6", children: _jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx(CardTitle, { children: "Nh\u00ECn tranh, ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng (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(CardContent, { children: _jsx(LookPictureChooseCorrectAnswerClient, { questionData: questionData, isReviewMode: true, userAnswer: correctAnswers, explanation: watchedExplanation }) })] }) }));
|
|
197
|
-
}
|
|
198
|
-
return (_jsxs("div", { className: "space-y-6", children: [_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-cyan-100/50", children: [_jsx("div", { className: "h-1.5 bg-gradient-to-r from-cyan-500 via-teal-500 to-emerald-500" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-cyan-500 to-teal-600 shadow-md", children: _jsx(Image, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-xl font-bold text-gray-900", children: "Nh\u00ECn tranh, ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng" }), _jsx("p", { className: "text-sm text-gray-500", children: "T\u1EA1o c\u00E2u h\u1ECFi v\u1EDBi h\u00ECnh \u1EA3nh v\u00E0 c\u00E1c l\u1EF1a ch\u1ECDn \u0111\u00E1p \u00E1n" })] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setIsClientMode(true), 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-6", children: [_jsx("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: _jsx(PointsInput, { defaultValue: watchedPoints, onChange: (e) => form.setValue('points', Number(e.target.value) || 1) }) }), _jsxs("div", { className: "rounded-xl border-2 border-teal-100 bg-gradient-to-r from-teal-50/30 to-cyan-50/30 p-5", children: [_jsxs("div", { className: "mb-4 flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-teal-100", children: _jsx(Image, { className: "h-3.5 w-3.5 text-teal-600" }) }), _jsx("span", { className: "text-sm font-semibold text-teal-800", children: "H\u00ECnh \u1EA3nh c\u00E2u h\u1ECFi" })] }), _jsx(FileUpload, { id: "image-upload", label: "H\u00ECnh \u1EA3nh", accept: "image/*", value: watchedImageUrl, onChange: (url) => form.setValue('imageUrl', url, { shouldValidate: true }), onPresignedUrlChange: (presignedUrl) => setImagePreviewUrl(presignedUrl), maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", required: true, autoUpload: true, prefix: "questions" }), form.hasError('imageUrl') && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: form.formState.errors.imageUrl?.message })), displayPreviewUrl && (_jsx(ImagePreview, { src: displayPreviewUrl, alt: "Preview for question", className: "mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200" }))] })] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-indigo-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-indigo-400 to-purple-400" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100", children: _jsx(Lightbulb, { className: "h-3.5 w-3.5 text-indigo-600" }) }), _jsxs(CardTitle, { className: "text-lg font-semibold text-gray-800", children: ["C\u00E1c l\u1EF1a ch\u1ECDn ", _jsxs("span", { className: "text-sm font-normal text-gray-500", children: ["(", watchedOptions.length, " \u0111\u00E1p \u00E1n)"] })] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => {
|
|
199
|
-
const currentOptions = form.getValues('options');
|
|
200
|
-
form.setValue('options', [...currentOptions, '']);
|
|
201
|
-
}, className: "gap-2 border-green-200 text-green-600 hover:bg-green-50 hover:text-green-700", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm \u0111\u00E1p \u00E1n"] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsx(RadioGroup, { value: watchedCorrectAnswer.toString(), onValueChange: (value) => {
|
|
202
|
-
form.setValue('correctAnswer', Number(value), { shouldValidate: true });
|
|
203
|
-
}, children: watchedOptions.map((option, index) => {
|
|
204
|
-
const optionError = form.formState.errors.options?.[index];
|
|
205
|
-
const isCorrect = watchedCorrectAnswer === index;
|
|
206
|
-
const optionLetter = String.fromCharCode(65 + index);
|
|
207
|
-
return (_jsx("div", { className: `group relative rounded-xl border-2 p-4 transition-all duration-200 ${isCorrect
|
|
208
|
-
? 'border-green-300 bg-gradient-to-r from-green-50 to-emerald-50 shadow-md shadow-green-100'
|
|
209
|
-
: 'border-gray-200 bg-white hover:border-gray-300 hover:shadow-sm'}`, children: _jsxs("div", { className: "flex items-start gap-4", children: [_jsx("div", { className: `flex h-10 w-10 flex-shrink-0 cursor-pointer items-center justify-center rounded-xl font-bold transition-all ${isCorrect
|
|
210
|
-
? 'bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md'
|
|
211
|
-
: 'bg-gray-100 text-gray-600 hover:bg-indigo-100 hover:text-indigo-600'}`, onClick: () => form.setValue('correctAnswer', index, { shouldValidate: true }), children: optionLetter }), _jsxs("div", { className: "flex-1 space-y-2", children: [_jsx(Input, { id: `option-input-${index}`, ...form.register(`options.${index}`), placeholder: `Nhập đáp án ${optionLetter}...`, className: `border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${optionError ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), optionError && (_jsx("p", { className: "text-sm text-red-600", children: optionError.message })), isCorrect && (_jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-full bg-green-100 px-3 py-1 text-xs font-semibold text-green-700", children: [_jsx(CheckCircle2, { className: "h-3.5 w-3.5" }), "\u0110\u00E1p \u00E1n \u0111\u00FAng"] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [!isCorrect && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => form.setValue('correctAnswer', index, { shouldValidate: true }), className: "text-gray-400 hover:text-green-600", title: "Ch\u1ECDn l\u00E0m \u0111\u00E1p \u00E1n \u0111\u00FAng", children: _jsx(CheckCircle2, { className: "h-4 w-4" }) })), watchedOptions.length > 2 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => {
|
|
212
|
-
const currentOptions = form.getValues('options');
|
|
213
|
-
const newOptions = currentOptions.filter((_, i) => i !== index);
|
|
214
|
-
form.setValue('options', newOptions);
|
|
215
|
-
// Adjust correctAnswer if needed
|
|
216
|
-
const currentCorrect = form.getValues('correctAnswer');
|
|
217
|
-
if (currentCorrect === index) {
|
|
218
|
-
form.setValue('correctAnswer', -1);
|
|
219
|
-
}
|
|
220
|
-
else if (currentCorrect > index) {
|
|
221
|
-
form.setValue('correctAnswer', currentCorrect - 1);
|
|
222
|
-
}
|
|
223
|
-
}, className: "text-red-500 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] })] }) }, index));
|
|
224
|
-
}) }), watchedCorrectAnswer === -1 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-amber-100", children: _jsx("span", { className: "text-lg", children: "\u26A0\uFE0F" }) }), _jsxs("div", { className: "text-sm text-amber-800", children: [_jsx("p", { className: "font-semibold", children: "Ch\u01B0a ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng" }), _jsx("p", { className: "mt-0.5 text-amber-700", children: "Click v\u00E0o \u00F4 ch\u1EEF c\u00E1i ho\u1EB7c icon \u2713 \u0111\u1EC3 ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng" })] })] })), form.hasError('correctAnswer') && (_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" }) }), _jsx("div", { className: "text-sm text-red-600", children: form.formState.errors.correctAnswer?.message })] }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-amber-400 to-orange-400" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm", children: _jsx(BookOpen, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-lg 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-sm 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, { children: _jsx(Textarea, { id: "explanation", ...form.register('explanation'), 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" }) })] })] }));
|
|
225
|
-
}
|
|
226
|
-
export function LookPictureChooseCorrectAnswerCreator(props) {
|
|
227
|
-
return _jsx(LookPictureChooseCorrectAnswerContent, { ...props });
|
|
228
|
-
}
|
|
229
|
-
// Legacy export for backward compatibility
|
|
230
|
-
export const LookPictureChooseCorrectAnswer = LookPictureChooseCorrectAnswerCreator;
|
|
1
|
+
"use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{useState as t,useRef as n,useEffect as o}from"react";import{Button as a}from"../../../../components/ui/button";import{Input as s}from"../../../../components/ui/input";import{Textarea as i}from"../../../../components/ui/textarea";import{Card as c,CardContent as l,CardHeader as d,CardTitle as m}from"../../../../components/ui/card";import{PointsInput as h}from"../../../../components/ui/points-input";import{RadioGroup as g}from"../../../../components/ui/radio-group";import{Pencil as p,Eye as u,Plus as f,Trash2 as x,Image as b,CheckCircle2 as w,Lightbulb as N,BookOpen as v}from"lucide-react";import{FileUpload as y}from"../../../../components/ui/file-upload";import{ImagePreview as A}from"../../../../components/ui/image-preview";import{usePresignedFileUrl as C,useDebouncedCallback as U}from"../../../../shared/lib/hooks";import{LookPictureChooseCorrectAnswerClient as V}from"./LookPictureChooseCorrectAnswerClient";import{useReactHookForm as k}from"../../../../shared/lib/hooks/useReactHookForm";import{lookPictureChooseCorrectAnswerFormSchema as S}from"../../../../shared/lib/validation/look-picture-choose-correct-answer.validation";function j({initialData:j,onSave:z,onCancel:q,onChange:J,onUnsavedChangesChange:O,optionCount:P=2,validationRef:E}){const L=(e,r)=>e&&0!==e.length?e.map(e=>String(e||"")):Array.from({length:r},()=>""),$=j,D=$?.content?.options||$?.options||[],H=k({defaultValues:{imageUrl:$?.content?.imageUrl||$?.imageUrl||"",options:L(D,P),correctAnswer:"object"==typeof $?.correctAnswer?$?.correctAnswer?.answer??-1:$?.correctAnswer??-1,explanation:$?.explanation||"",points:$?.points??1},zodSchema:S,mode:"onBlur"}),[R,T]=t(!1),[F,G]=t(""),M=n(j),Q=n(!1),B=n(J),K=n("");o(()=>{B.current=J},[J]);const X=U(e=>{B.current?.(e)},300),I=H.watch("imageUrl"),W=H.watch("options"),Y=H.watch("correctAnswer"),Z=H.watch("explanation"),_=H.watch("points"),{previewUrl:ee}=C(I),re=F||ee||"";o(()=>{E&&(E.current={triggerValidation:async()=>{const e=await H.trigger();return-1===Y?(H.setError("correctAnswer",{message:"Vui lòng chọn đáp án đúng!"}),!1):e},getFormData:()=>({imageUrl:I,options:W,correctAnswer:Y,explanation:Z,points:_})})},[E,H,Y,I,W,Z,_]),o(()=>{const e=j,r={imageUrl:e?.content?.imageUrl||e?.imageUrl||"",options:L(e?.content?.options||e?.options||[],P),correctAnswer:"object"==typeof e?.correctAnswer?e?.correctAnswer?.answer??-1:e?.correctAnswer??-1,explanation:e?.explanation||"",points:e?.points??1};if(!(JSON.stringify(M.current)!==JSON.stringify(j)))return;M.current=j;const t={imageUrl:I,options:W,correctAnswer:Y,explanation:Z,points:_},n=t.imageUrl===r.imageUrl&&t.correctAnswer===r.correctAnswer&&t.points===r.points&&t.explanation===r.explanation&&JSON.stringify(t.options)===JSON.stringify(r.options);if(n&&!0===j?.isCompleted)return;!n&&r.correctAnswer!==t.correctAnswer&&-1!==r.correctAnswer&&(Q.current=!0,H.reset(r),setTimeout(()=>{Q.current=!1},150))},[j,P,H,I,W,Y,Z,_]);const te=JSON.stringify(W);o(()=>{if(Q.current){const e=JSON.stringify({imageUrl:I,options:W,correctAnswer:Y,explanation:Z,points:_});return void(K.current=e)}const e={imageUrl:I,options:W,correctAnswer:Y,explanation:Z,points:_},r=JSON.stringify(e);K.current!==r&&(K.current=r,X(e))},[I,te,Y,Z,_]);H.handleSubmit(e=>{-1===e.correctAnswer||e.correctAnswer>=e.options.length?H.setError("correctAnswer",{message:"Vui lòng chọn đáp án đúng!"}):z?.(e)});if(R){const t={subQuestions:[{id:"preview-sq",imageUrl:re||I,question:"",options:W.map((e,r)=>({id:`opt-${r}`,text:e,isCorrect:r===Y}))}]},n=-1!==Y?{"preview-sq":`opt-${Y}`}:{};return e("div",{className:"space-y-6",children:r(c,{children:[e(d,{children:r("div",{className:"flex items-center justify-between",children:[r("div",{children:[e(m,{children:"Nhìn tranh, chọn đáp án đúng (Preview)"}),e("p",{className:"mt-1 text-sm text-gray-500",children:"Chế độ xem trước - Hiển thị đáp án đúng"})]}),r(a,{variant:"outline",size:"sm",onClick:()=>T(!1),children:[e(p,{className:"mr-2 h-4 w-4"}),"Quay lại Edit Mode"]})]})}),e(l,{children:e(V,{questionData:t,isReviewMode:!0,userAnswer:n,explanation:Z})})]})})}return r("div",{className:"space-y-6",children:[r(c,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-cyan-100/50",children:[e("div",{className:"h-1.5 bg-gradient-to-r from-cyan-500 via-teal-500 to-emerald-500"}),e(d,{className:"pb-4",children:r("div",{className:"flex items-center justify-between",children:[r("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-cyan-500 to-teal-600 shadow-md",children:e(b,{className:"h-5 w-5 text-white"})}),r("div",{children:[e(m,{className:"text-xl font-bold text-gray-900",children:"Nhìn tranh, chọn đáp án đúng"}),e("p",{className:"text-sm text-gray-500",children:"Tạo câu hỏi với hình ảnh và các lựa chọn đáp án"})]})]}),r(a,{variant:"outline",size:"sm",onClick:()=>T(!0),className:"gap-2 border-teal-200 text-teal-600 hover:bg-teal-50 hover:text-teal-700",children:[e(u,{className:"h-4 w-4"}),"Xem trước"]})]})}),r(l,{className:"space-y-6",children:[e("div",{className:"rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4",children:e(h,{defaultValue:_,onChange:e=>H.setValue("points",Number(e.target.value)||1)})}),r("div",{className:"rounded-xl border-2 border-teal-100 bg-gradient-to-r from-teal-50/30 to-cyan-50/30 p-5",children:[r("div",{className:"mb-4 flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-teal-100",children:e(b,{className:"h-3.5 w-3.5 text-teal-600"})}),e("span",{className:"text-sm font-semibold text-teal-800",children:"Hình ảnh câu hỏi"})]}),e(y,{id:"image-upload",label:"Hình ảnh",accept:"image/*",value:I,onChange:e=>H.setValue("imageUrl",e,{shouldValidate:!0}),onPresignedUrlChange:e=>G(e),maxSize:5,placeholder:"Upload ảnh hoặc paste URL",required:!0,autoUpload:!0,prefix:"questions"}),H.hasError("imageUrl")&&e("p",{className:"mt-1 text-sm text-red-600",children:H.formState.errors.imageUrl?.message}),re&&e(A,{src:re,alt:"Preview for question",className:"mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200"})]})]})]}),r(c,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-indigo-100/50",children:[e("div",{className:"h-1 bg-gradient-to-r from-indigo-400 to-purple-400"}),e(d,{className:"pb-4",children:r("div",{className:"flex items-center justify-between",children:[r("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100",children:e(N,{className:"h-3.5 w-3.5 text-indigo-600"})}),r(m,{className:"text-lg font-semibold text-gray-800",children:["Các lựa chọn ",r("span",{className:"text-sm font-normal text-gray-500",children:["(",W.length," đáp án)"]})]})]}),r(a,{variant:"outline",size:"sm",onClick:()=>{const e=H.getValues("options");H.setValue("options",[...e,""])},className:"gap-2 border-green-200 text-green-600 hover:bg-green-50 hover:text-green-700",children:[e(f,{className:"h-4 w-4"}),"Thêm đáp án"]})]})}),r(l,{className:"space-y-4",children:[e(g,{value:Y.toString(),onValueChange:e=>{H.setValue("correctAnswer",Number(e),{shouldValidate:!0})},children:W.map((t,n)=>{const o=H.formState.errors.options?.[n],i=Y===n,c=String.fromCharCode(65+n);return e("div",{className:"group relative rounded-xl border-2 p-4 transition-all duration-200 "+(i?"border-green-300 bg-gradient-to-r from-green-50 to-emerald-50 shadow-md shadow-green-100":"border-gray-200 bg-white hover:border-gray-300 hover:shadow-sm"),children:r("div",{className:"flex items-start gap-4",children:[e("div",{className:"flex h-10 w-10 flex-shrink-0 cursor-pointer items-center justify-center rounded-xl font-bold transition-all "+(i?"bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md":"bg-gray-100 text-gray-600 hover:bg-indigo-100 hover:text-indigo-600"),onClick:()=>H.setValue("correctAnswer",n,{shouldValidate:!0}),children:c}),r("div",{className:"flex-1 space-y-2",children:[e(s,{id:`option-input-${n}`,...H.register(`options.${n}`),placeholder:`Nhập đáp án ${c}...`,className:"border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 "+(o?"border-red-400 focus:border-red-500 focus:ring-red-200":"")}),o&&e("p",{className:"text-sm text-red-600",children:o.message}),i&&r("span",{className:"inline-flex items-center gap-1.5 rounded-full bg-green-100 px-3 py-1 text-xs font-semibold text-green-700",children:[e(w,{className:"h-3.5 w-3.5"}),"Đáp án đúng"]})]}),r("div",{className:"flex items-center gap-2",children:[!i&&e(a,{type:"button",variant:"ghost",size:"sm",onClick:()=>H.setValue("correctAnswer",n,{shouldValidate:!0}),className:"text-gray-400 hover:text-green-600",title:"Chọn làm đáp án đúng",children:e(w,{className:"h-4 w-4"})}),W.length>2&&e(a,{variant:"ghost",size:"sm",onClick:()=>{const e=H.getValues("options").filter((e,r)=>r!==n);H.setValue("options",e);const r=H.getValues("correctAnswer");r===n?H.setValue("correctAnswer",-1):r>n&&H.setValue("correctAnswer",r-1)},className:"text-red-500 hover:bg-red-50 hover:text-red-600",children:e(x,{className:"h-4 w-4"})})]})]})},n)})}),-1===Y&&r("div",{className:"flex items-start gap-3 rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4",children:[e("div",{className:"flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-amber-100",children:e("span",{className:"text-lg",children:"⚠️"})}),r("div",{className:"text-sm text-amber-800",children:[e("p",{className:"font-semibold",children:"Chưa chọn đáp án đúng"}),e("p",{className:"mt-0.5 text-amber-700",children:"Click vào ô chữ cái hoặc icon ✓ để chọn đáp án đúng"})]})]}),H.hasError("correctAnswer")&&r("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:"❌"})}),e("div",{className:"text-sm text-red-600",children:H.formState.errors.correctAnswer?.message})]})]})]}),r(c,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50",children:[e("div",{className:"h-1 bg-gradient-to-r from-amber-400 to-orange-400"}),e(d,{className:"pb-4",children:r("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-9 w-9 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm",children:e(v,{className:"h-4 w-4 text-white"})}),r("div",{children:[r("div",{className:"flex items-center gap-2",children:[e(m,{className:"text-lg 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-sm 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(l,{children:e(i,{id:"explanation",...H.register("explanation"),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 LookPictureChooseCorrectAnswerCreator(r){return e(j,{...r})}export const LookPictureChooseCorrectAnswer=LookPictureChooseCorrectAnswerCreator;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export*from"./LookPictureChooseCorrectAnswerCreator";
|
|
@@ -1,66 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
export const lookPictureChooseCorrectAnswerRegistration = {
|
|
3
|
-
component: LookPictureChooseCorrectAnswer,
|
|
4
|
-
transformInitialData: (initialData) => {
|
|
5
|
-
let creatorData = undefined;
|
|
6
|
-
const content = initialData?.content;
|
|
7
|
-
const answer = initialData?.answer;
|
|
8
|
-
const apiPoints = initialData?.total_points
|
|
9
|
-
? parseFloat(initialData.total_points)
|
|
10
|
-
: (initialData?.points || answer?.points || 1);
|
|
11
|
-
const normalizeOptions = (opts) => {
|
|
12
|
-
if (!Array.isArray(opts) || opts.length === 0)
|
|
13
|
-
return [];
|
|
14
|
-
return opts.map((opt) => String(opt || ''));
|
|
15
|
-
};
|
|
16
|
-
// Priority 1: Data from reverse transform or store
|
|
17
|
-
if (answer?.imageUrl !== undefined || answer?.options) {
|
|
18
|
-
creatorData = {
|
|
19
|
-
imageUrl: answer.imageUrl || '',
|
|
20
|
-
options: normalizeOptions(answer.options || []),
|
|
21
|
-
correctAnswer: typeof answer.correctAnswer === 'number' ? answer.correctAnswer : -1,
|
|
22
|
-
explanation: answer.explanation || initialData?.explanation || '',
|
|
23
|
-
points: answer.points || apiPoints,
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
// Priority 2: Raw API content format
|
|
27
|
-
else if (content && (content.imageUrl !== undefined || content.options)) {
|
|
28
|
-
let correctAnswer = -1;
|
|
29
|
-
if (typeof answer?.answer === 'number')
|
|
30
|
-
correctAnswer = answer.answer;
|
|
31
|
-
else if (typeof initialData?.correct_answer?.answer === 'number')
|
|
32
|
-
correctAnswer = initialData.correct_answer.answer;
|
|
33
|
-
creatorData = {
|
|
34
|
-
imageUrl: content.imageUrl || '',
|
|
35
|
-
options: normalizeOptions(content.options || []),
|
|
36
|
-
correctAnswer,
|
|
37
|
-
explanation: initialData?.explanation || '',
|
|
38
|
-
points: apiPoints,
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
return creatorData;
|
|
42
|
-
},
|
|
43
|
-
getExtraProps: (ctx) => ({
|
|
44
|
-
optionCount: ctx.optionCount ?? undefined,
|
|
45
|
-
onUnsavedChangesChange: ctx.onUnsavedChangesChange,
|
|
46
|
-
validationRef: ctx.validationRef,
|
|
47
|
-
}),
|
|
48
|
-
wrapOnSave: (data, ctx) => ({
|
|
49
|
-
type: ctx.questionType,
|
|
50
|
-
content: ctx.state.content,
|
|
51
|
-
points: data?.points ?? ctx.state.points ?? 1,
|
|
52
|
-
level: ctx.state.level,
|
|
53
|
-
difficulty: ctx.state.difficulty,
|
|
54
|
-
skill: ctx.state.skill,
|
|
55
|
-
answer: data,
|
|
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
|
-
}),
|
|
66
|
-
};
|
|
1
|
+
import{LookPictureChooseCorrectAnswer as t}from"./LookPictureChooseCorrectAnswerCreator";export const lookPictureChooseCorrectAnswerRegistration={component:t,transformInitialData:t=>{let e;const n=t?.content,o=t?.answer,i=t?.total_points?parseFloat(t.total_points):t?.points||o?.points||1,s=t=>Array.isArray(t)&&0!==t.length?t.map(t=>String(t||"")):[];if(void 0!==o?.imageUrl||o?.options)e={imageUrl:o.imageUrl||"",options:s(o.options||[]),correctAnswer:"number"==typeof o.correctAnswer?o.correctAnswer:-1,explanation:o.explanation||t?.explanation||"",points:o.points||i};else if(n&&(void 0!==n.imageUrl||n.options)){let a=-1;"number"==typeof o?.answer?a=o.answer:"number"==typeof t?.correct_answer?.answer&&(a=t.correct_answer.answer),e={imageUrl:n.imageUrl||"",options:s(n.options||[]),correctAnswer:a,explanation:t?.explanation||"",points:i}}return e},getExtraProps:t=>({optionCount:t.optionCount??void 0,onUnsavedChangesChange:t.onUnsavedChangesChange,validationRef:t.validationRef}),wrapOnSave:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t}),wrapOnChange:(t,e)=>({type:e.questionType,content:e.state.content,points:t?.points??e.state.points??1,level:e.state.level,difficulty:e.state.difficulty,skill:e.state.skill,answer:t})};
|
|
@@ -1,42 +1 @@
|
|
|
1
|
-
|
|
2
|
-
// NEW FORMAT: content { imageUrl, options: string[] }, correct_answer { answer: number }
|
|
3
|
-
export const transformLookPictureChooseCorrectAnswer = (question) => {
|
|
4
|
-
const answerData = question.answer;
|
|
5
|
-
// NEW format: { imageUrl, options: string[], correctAnswer: number, points }
|
|
6
|
-
if (answerData?.imageUrl !== undefined || answerData?.options) {
|
|
7
|
-
const options = (answerData.options || []).map((opt) => typeof opt === 'string' ? opt : (opt.text || ''));
|
|
8
|
-
let correctAnswer = -1;
|
|
9
|
-
if (typeof answerData.correctAnswer === 'number') {
|
|
10
|
-
correctAnswer = answerData.correctAnswer;
|
|
11
|
-
}
|
|
12
|
-
else if (Array.isArray(answerData.options)) {
|
|
13
|
-
correctAnswer = answerData.options.findIndex((opt) => opt.isCorrect === true);
|
|
14
|
-
}
|
|
15
|
-
return {
|
|
16
|
-
apiContent: {
|
|
17
|
-
imageUrl: answerData.imageUrl || '',
|
|
18
|
-
options,
|
|
19
|
-
},
|
|
20
|
-
apiCorrectAnswer: {
|
|
21
|
-
answer: correctAnswer,
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
// LEGACY format with subQuestions - convert to new format
|
|
26
|
-
const legacyData = answerData;
|
|
27
|
-
if (!legacyData?.subQuestions || legacyData.subQuestions.length === 0) {
|
|
28
|
-
return { apiContent: {}, apiCorrectAnswer: {} };
|
|
29
|
-
}
|
|
30
|
-
const sq = legacyData.subQuestions[0];
|
|
31
|
-
const options = (sq.options || []).map((opt) => opt.text || '');
|
|
32
|
-
const correctAnswerIndex = sq.options?.findIndex((opt) => opt.isCorrect) ?? -1;
|
|
33
|
-
return {
|
|
34
|
-
apiContent: {
|
|
35
|
-
imageUrl: sq.imageUrl || '',
|
|
36
|
-
options,
|
|
37
|
-
},
|
|
38
|
-
apiCorrectAnswer: {
|
|
39
|
-
answer: correctAnswerIndex,
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
};
|
|
1
|
+
export const transformLookPictureChooseCorrectAnswer=r=>{const t=r.answer;if(void 0!==t?.imageUrl||t?.options){const r=(t.options||[]).map(r=>"string"==typeof r?r:r.text||"");let o=-1;return"number"==typeof t.correctAnswer?o=t.correctAnswer:Array.isArray(t.options)&&(o=t.options.findIndex(r=>!0===r.isCorrect)),{apiContent:{imageUrl:t.imageUrl||"",options:r},apiCorrectAnswer:{answer:o}}}const o=t;if(!o?.subQuestions||0===o.subQuestions.length)return{apiContent:{},apiCorrectAnswer:{}};const e=o.subQuestions[0],n=(e.options||[]).map(r=>r.text||""),s=e.options?.findIndex(r=>r.isCorrect)??-1;return{apiContent:{imageUrl:e.imageUrl||"",options:n},apiCorrectAnswer:{answer:s}}};
|
|
@@ -1,96 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState, useEffect } from 'react';
|
|
4
|
-
import { Check, X, ImageIcon, Lightbulb } from 'lucide-react';
|
|
5
|
-
import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
|
|
6
|
-
import { cn } from '../../../../shared/lib/utils';
|
|
7
|
-
export function LookPictureFillBlankChooseAnswerClient({ questionData, onSubmit, isReviewMode = false, userAnswer = '', correctAnswer = '', explanation, }) {
|
|
8
|
-
// Handle both flat structure and subQuestions structure
|
|
9
|
-
const flatData = questionData.subQuestions?.[0]
|
|
10
|
-
? {
|
|
11
|
-
imageUrl: questionData.subQuestions[0].imageUrl,
|
|
12
|
-
question: questionData.subQuestions[0].question || '',
|
|
13
|
-
options: questionData.subQuestions[0].options.map(opt => ({ id: opt.id, text: opt.text })),
|
|
14
|
-
}
|
|
15
|
-
: {
|
|
16
|
-
imageUrl: questionData.imageUrl || '',
|
|
17
|
-
question: questionData.question || '',
|
|
18
|
-
options: questionData.options || [],
|
|
19
|
-
};
|
|
20
|
-
// State for selected answer
|
|
21
|
-
const initialAnswer = typeof userAnswer === 'string' ? userAnswer : userAnswer?.[questionData.subQuestions?.[0]?.id || ''] || '';
|
|
22
|
-
const [selectedAnswer, setSelectedAnswer] = useState(initialAnswer);
|
|
23
|
-
// Get presigned URL for image
|
|
24
|
-
const { previewUrl: imagePreviewUrl, isLoading } = usePresignedFileUrl(flatData.imageUrl || '');
|
|
25
|
-
// Check if answer is correct
|
|
26
|
-
const isAnswerCorrect = selectedAnswer === correctAnswer;
|
|
27
|
-
const hasAnswered = selectedAnswer !== '';
|
|
28
|
-
// Sync userAnswer when it changes
|
|
29
|
-
useEffect(() => {
|
|
30
|
-
const answer = typeof userAnswer === 'string' ? userAnswer : userAnswer?.[questionData.subQuestions?.[0]?.id || ''] || '';
|
|
31
|
-
setSelectedAnswer(answer);
|
|
32
|
-
}, [userAnswer, questionData.subQuestions]);
|
|
33
|
-
// Handle answer change
|
|
34
|
-
const handleAnswerChange = (optionId) => {
|
|
35
|
-
if (isReviewMode)
|
|
36
|
-
return;
|
|
37
|
-
setSelectedAnswer(optionId);
|
|
38
|
-
onSubmit?.(optionId);
|
|
39
|
-
};
|
|
40
|
-
// Replace {0} with styled blank for display
|
|
41
|
-
const renderQuestionWithBlank = () => {
|
|
42
|
-
const parts = flatData.question.split('{0}');
|
|
43
|
-
return (_jsx("span", { className: "text-gray-800", children: parts.map((part, idx) => (_jsxs("span", { children: [part, idx < parts.length - 1 && (_jsx("span", { className: "inline-flex items-center mx-1", children: _jsx("span", { className: "inline-block min-w-[80px] border-b-2 border-dashed border-blue-400 bg-blue-50/50 px-3 py-0.5 text-center text-blue-600 font-medium rounded", children: "___________" }) }))] }, idx))) }));
|
|
44
|
-
};
|
|
45
|
-
// Replace {0} with selected answer for preview
|
|
46
|
-
const renderQuestionWithAnswer = (answerText) => {
|
|
47
|
-
const parts = flatData.question.split('{0}');
|
|
48
|
-
return (_jsx("span", { children: parts.map((part, idx) => (_jsxs("span", { children: [part, idx < parts.length - 1 && (_jsx("span", { className: "inline-flex items-center mx-1", children: _jsx("span", { className: "inline-block rounded-md bg-gradient-to-r from-blue-500 to-indigo-500 px-3 py-0.5 text-white font-semibold shadow-sm", children: answerText }) }))] }, idx))) }));
|
|
49
|
-
};
|
|
50
|
-
// Get selected option text
|
|
51
|
-
const getSelectedOptionText = () => {
|
|
52
|
-
const option = flatData.options.find(opt => opt.id === selectedAnswer);
|
|
53
|
-
return option?.text || '';
|
|
54
|
-
};
|
|
55
|
-
// Get correct option text
|
|
56
|
-
const getCorrectOptionText = () => {
|
|
57
|
-
const option = flatData.options.find(opt => opt.id === correctAnswer);
|
|
58
|
-
return option?.text || '';
|
|
59
|
-
};
|
|
60
|
-
return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm transition-all hover:shadow-md", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-5 py-3", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-purple-500 to-indigo-600 text-white shadow-sm", children: _jsx(ImageIcon, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Nh\u00ECn tranh, \u0111i\u1EC1n t\u1EEB v\u00E0o ch\u1ED7 tr\u1ED1ng" })] }), isReviewMode && hasAnswered && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold', isAnswerCorrect
|
|
61
|
-
? 'bg-green-100 text-green-700'
|
|
62
|
-
: 'bg-red-100 text-red-700'), children: [isAnswerCorrect ? _jsx(Check, { className: "h-3.5 w-3.5" }) : _jsx(X, { className: "h-3.5 w-3.5" }), isAnswerCorrect ? 'Đúng' : 'Sai'] }))] }), _jsxs("div", { className: "p-5 space-y-5", children: [(imagePreviewUrl || flatData.imageUrl) && (_jsxs("div", { className: "overflow-hidden rounded-lg border border-gray-200 bg-gradient-to-br from-slate-50 to-gray-100", children: [_jsx("div", { className: "border-b border-gray-100 px-3 py-1.5", children: _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "h-2 w-2 rounded-full bg-red-400" }), _jsx("div", { className: "h-2 w-2 rounded-full bg-yellow-400" }), _jsx("div", { className: "h-2 w-2 rounded-full bg-green-400" }), _jsx("span", { className: "ml-2 text-xs font-medium text-gray-500", children: "H\u00ECnh \u1EA3nh" })] }) }), _jsx("div", { className: "flex items-center justify-center p-4", children: isLoading ? (_jsx("div", { className: "flex h-48 items-center justify-center", children: _jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-purple-500" }) })) : (
|
|
63
|
-
/* eslint-disable-next-line @next/next/no-img-element */
|
|
64
|
-
_jsx("img", { src: imagePreviewUrl || flatData.imageUrl, alt: "Question illustration", className: "max-h-56 w-auto object-contain rounded-md", onError: (e) => {
|
|
65
|
-
const target = e.target;
|
|
66
|
-
target.onerror = null;
|
|
67
|
-
target.src = '/images/placeholder-image.svg';
|
|
68
|
-
} })) })] })), _jsx("div", { className: "rounded-lg bg-gradient-to-r from-gray-50 to-slate-50 p-4 border border-gray-100", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-purple-100 text-purple-600", children: _jsx("span", { className: "text-xs font-bold", children: "?" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("span", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider", children: "C\u00E2u h\u1ECFi" }), _jsx("p", { className: "mt-1 text-base font-medium leading-relaxed", children: renderQuestionWithBlank() })] })] }) }), _jsxs("div", { className: "space-y-2.5", children: [_jsx("span", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider", children: "Ch\u1ECDn \u0111\u00E1p \u00E1n" }), flatData.options.map((option, optIndex) => {
|
|
69
|
-
const isSelected = selectedAnswer === option.id;
|
|
70
|
-
const isCorrectOption = option.id === correctAnswer;
|
|
71
|
-
const letter = String.fromCharCode(65 + optIndex);
|
|
72
|
-
return _jsxs("button", { type: "button", onClick: () => handleAnswerChange(option.id), disabled: isReviewMode, className: cn('group flex w-full items-center gap-3 rounded-lg border-2 p-3.5 text-left transition-all', isReviewMode
|
|
73
|
-
? isCorrectOption
|
|
74
|
-
? 'border-green-400 bg-green-50'
|
|
75
|
-
: isSelected
|
|
76
|
-
? 'border-red-400 bg-red-50'
|
|
77
|
-
: 'border-gray-200 bg-gray-50'
|
|
78
|
-
: isSelected
|
|
79
|
-
? 'border-purple-500 bg-purple-50 shadow-sm ring-2 ring-purple-100'
|
|
80
|
-
: 'border-gray-200 bg-white hover:border-gray-300 hover:bg-gray-50 hover:shadow-sm'), children: [_jsx("div", { className: cn('flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg text-sm font-bold transition-all', isReviewMode
|
|
81
|
-
? isCorrectOption
|
|
82
|
-
? 'bg-green-500 text-white'
|
|
83
|
-
: isSelected
|
|
84
|
-
? 'bg-red-500 text-white'
|
|
85
|
-
: 'bg-gray-200 text-gray-600'
|
|
86
|
-
: isSelected
|
|
87
|
-
? 'bg-gradient-to-br from-purple-500 to-indigo-600 text-white shadow-sm'
|
|
88
|
-
: 'bg-gray-100 text-gray-600 group-hover:bg-gray-200'), children: letter }), _jsx("div", { className: "flex-1", children: _jsx("span", { className: cn('text-sm font-medium', isReviewMode && isCorrectOption
|
|
89
|
-
? 'text-green-800'
|
|
90
|
-
: isReviewMode && isSelected && !isCorrectOption
|
|
91
|
-
? 'text-red-800'
|
|
92
|
-
: 'text-gray-700'), children: option.text }) }), isReviewMode && (isCorrectOption || isSelected) && (_jsx("div", { className: cn('flex h-6 w-6 items-center justify-center rounded-full', isCorrectOption ? 'bg-green-500' : 'bg-red-500'), children: isCorrectOption
|
|
93
|
-
? _jsx(Check, { className: "h-4 w-4 text-white" })
|
|
94
|
-
: _jsx(X, { className: "h-4 w-4 text-white" }) }))] }, option.id);
|
|
95
|
-
})] }), hasAnswered && !isReviewMode && (_jsxs("div", { className: "rounded-lg bg-gradient-to-r from-purple-50 to-indigo-50 p-4 border border-purple-100", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx("div", { className: "flex h-5 w-5 items-center justify-center rounded-full bg-purple-500 text-white", children: _jsx(Check, { className: "h-3 w-3" }) }), _jsx("span", { className: "text-xs font-semibold text-purple-700 uppercase tracking-wider", children: "Preview c\u00E2u tr\u1EA3 l\u1EDDi" })] }), _jsx("p", { className: "text-sm text-gray-800 leading-relaxed", children: renderQuestionWithAnswer(getSelectedOptionText()) })] })), isReviewMode && hasAnswered && !isAnswerCorrect && (_jsxs("div", { className: "rounded-lg bg-gradient-to-r from-green-50 to-emerald-50 p-4 border border-green-200", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx("div", { className: "flex h-5 w-5 items-center justify-center rounded-full bg-green-500 text-white", children: _jsx(Check, { className: "h-3 w-3" }) }), _jsx("span", { className: "text-xs font-semibold text-green-700 uppercase tracking-wider", children: "\u0110\u00E1p \u00E1n \u0111\u00FAng" })] }), _jsx("p", { className: "text-sm text-gray-800 leading-relaxed", children: renderQuestionWithAnswer(getCorrectOptionText()) })] })), !isReviewMode && !hasAnswered && (_jsxs("div", { className: "rounded-lg border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-3 flex items-center gap-2", children: [_jsx("div", { className: "flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full bg-amber-400 text-white", children: _jsx("span", { className: "text-xs font-bold", children: "!" }) }), _jsx("span", { className: "text-sm text-amber-800", children: "Vui l\u00F2ng ch\u1ECDn m\u1ED9t \u0111\u00E1p \u00E1n" })] })), isReviewMode && explanation && (_jsx("div", { className: "rounded-lg bg-gradient-to-r from-blue-50 to-indigo-50 p-4 border border-blue-200", children: _jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-white", children: _jsx(Lightbulb, { className: "h-3.5 w-3.5" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("span", { className: "text-xs font-semibold text-blue-700 uppercase tracking-wider", children: "Gi\u1EA3i th\u00EDch" }), _jsx("p", { className: "mt-1 text-sm text-gray-700 leading-relaxed", children: explanation })] })] }) }))] })] }));
|
|
96
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{useState as t,useEffect as s}from"react";import{Check as i,X as n,ImageIcon as a,Lightbulb as l}from"lucide-react";import{usePresignedFileUrl as d}from"../../../../shared/lib/hooks";import{cn as o}from"../../../../shared/lib/utils";export function LookPictureFillBlankChooseAnswerClient({questionData:c,onSubmit:m,isReviewMode:g=!1,userAnswer:u="",correctAnswer:h="",explanation:p}){const b=c.subQuestions?.[0]?{imageUrl:c.subQuestions[0].imageUrl,question:c.subQuestions[0].question||"",options:c.subQuestions[0].options.map(e=>({id:e.id,text:e.text}))}:{imageUrl:c.imageUrl||"",question:c.question||"",options:c.options||[]},x="string"==typeof u?u:u?.[c.subQuestions?.[0]?.id||""]||"",[f,N]=t(x),{previewUrl:w,isLoading:y}=d(b.imageUrl||""),v=f===h,j=""!==f;s(()=>{const e="string"==typeof u?u:u?.[c.subQuestions?.[0]?.id||""]||"";N(e)},[u,c.subQuestions]);const k=t=>{const s=b.question.split("{0}");return e("span",{children:s.map((i,n)=>r("span",{children:[i,n<s.length-1&&e("span",{className:"inline-flex items-center mx-1",children:e("span",{className:"inline-block rounded-md bg-gradient-to-r from-blue-500 to-indigo-500 px-3 py-0.5 text-white font-semibold shadow-sm",children:t})})]},n))})};return r("div",{className:"overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm transition-all hover:shadow-md",children:[r("div",{className:"flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-5 py-3",children:[r("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-purple-500 to-indigo-600 text-white shadow-sm",children:e(a,{className:"h-4 w-4"})}),e("span",{className:"text-sm font-medium text-gray-700",children:"Nhìn tranh, điền từ vào chỗ trống"})]}),g&&j&&r("div",{className:o("flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold",v?"bg-green-100 text-green-700":"bg-red-100 text-red-700"),children:[e(v?i:n,{className:"h-3.5 w-3.5"}),v?"Đúng":"Sai"]})]}),r("div",{className:"p-5 space-y-5",children:[(w||b.imageUrl)&&r("div",{className:"overflow-hidden rounded-lg border border-gray-200 bg-gradient-to-br from-slate-50 to-gray-100",children:[e("div",{className:"border-b border-gray-100 px-3 py-1.5",children:r("div",{className:"flex items-center gap-1.5",children:[e("div",{className:"h-2 w-2 rounded-full bg-red-400"}),e("div",{className:"h-2 w-2 rounded-full bg-yellow-400"}),e("div",{className:"h-2 w-2 rounded-full bg-green-400"}),e("span",{className:"ml-2 text-xs font-medium text-gray-500",children:"Hình ảnh"})]})}),e("div",{className:"flex items-center justify-center p-4",children:y?e("div",{className:"flex h-48 items-center justify-center",children:e("div",{className:"h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-purple-500"})}):e("img",{src:w||b.imageUrl,alt:"Question illustration",className:"max-h-56 w-auto object-contain rounded-md",onError:e=>{const r=e.target;r.onerror=null,r.src="/images/placeholder-image.svg"}})})]}),e("div",{className:"rounded-lg bg-gradient-to-r from-gray-50 to-slate-50 p-4 border border-gray-100",children:r("div",{className:"flex items-start gap-3",children:[e("div",{className:"flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-purple-100 text-purple-600",children:e("span",{className:"text-xs font-bold",children:"?"})}),r("div",{className:"flex-1",children:[e("span",{className:"text-xs font-medium text-gray-500 uppercase tracking-wider",children:"Câu hỏi"}),e("p",{className:"mt-1 text-base font-medium leading-relaxed",children:(()=>{const t=b.question.split("{0}");return e("span",{className:"text-gray-800",children:t.map((s,i)=>r("span",{children:[s,i<t.length-1&&e("span",{className:"inline-flex items-center mx-1",children:e("span",{className:"inline-block min-w-[80px] border-b-2 border-dashed border-blue-400 bg-blue-50/50 px-3 py-0.5 text-center text-blue-600 font-medium rounded",children:"___________"})})]},i))})})()})]})]})}),r("div",{className:"space-y-2.5",children:[e("span",{className:"text-xs font-medium text-gray-500 uppercase tracking-wider",children:"Chọn đáp án"}),b.options.map((t,s)=>{const a=f===t.id,l=t.id===h,d=String.fromCharCode(65+s);return r("button",{type:"button",onClick:()=>{return e=t.id,void(g||(N(e),m?.(e)));var e},disabled:g,className:o("group flex w-full items-center gap-3 rounded-lg border-2 p-3.5 text-left transition-all",g?l?"border-green-400 bg-green-50":a?"border-red-400 bg-red-50":"border-gray-200 bg-gray-50":a?"border-purple-500 bg-purple-50 shadow-sm ring-2 ring-purple-100":"border-gray-200 bg-white hover:border-gray-300 hover:bg-gray-50 hover:shadow-sm"),children:[e("div",{className:o("flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-lg text-sm font-bold transition-all",g?l?"bg-green-500 text-white":a?"bg-red-500 text-white":"bg-gray-200 text-gray-600":a?"bg-gradient-to-br from-purple-500 to-indigo-600 text-white shadow-sm":"bg-gray-100 text-gray-600 group-hover:bg-gray-200"),children:d}),e("div",{className:"flex-1",children:e("span",{className:o("text-sm font-medium",g&&l?"text-green-800":g&&a&&!l?"text-red-800":"text-gray-700"),children:t.text})}),g&&(l||a)&&e("div",{className:o("flex h-6 w-6 items-center justify-center rounded-full",l?"bg-green-500":"bg-red-500"),children:e(l?i:n,{className:"h-4 w-4 text-white"})})]},t.id)})]}),j&&!g&&r("div",{className:"rounded-lg bg-gradient-to-r from-purple-50 to-indigo-50 p-4 border border-purple-100",children:[r("div",{className:"flex items-center gap-2 mb-2",children:[e("div",{className:"flex h-5 w-5 items-center justify-center rounded-full bg-purple-500 text-white",children:e(i,{className:"h-3 w-3"})}),e("span",{className:"text-xs font-semibold text-purple-700 uppercase tracking-wider",children:"Preview câu trả lời"})]}),e("p",{className:"text-sm text-gray-800 leading-relaxed",children:k((()=>{const e=b.options.find(e=>e.id===f);return e?.text||""})())})]}),g&&j&&!v&&r("div",{className:"rounded-lg bg-gradient-to-r from-green-50 to-emerald-50 p-4 border border-green-200",children:[r("div",{className:"flex items-center gap-2 mb-2",children:[e("div",{className:"flex h-5 w-5 items-center justify-center rounded-full bg-green-500 text-white",children:e(i,{className:"h-3 w-3"})}),e("span",{className:"text-xs font-semibold text-green-700 uppercase tracking-wider",children:"Đáp án đúng"})]}),e("p",{className:"text-sm text-gray-800 leading-relaxed",children:k((()=>{const e=b.options.find(e=>e.id===h);return e?.text||""})())})]}),!g&&!j&&r("div",{className:"rounded-lg border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-3 flex items-center gap-2",children:[e("div",{className:"flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-full bg-amber-400 text-white",children:e("span",{className:"text-xs font-bold",children:"!"})}),e("span",{className:"text-sm text-amber-800",children:"Vui lòng chọn một đáp án"})]}),g&&p&&e("div",{className:"rounded-lg bg-gradient-to-r from-blue-50 to-indigo-50 p-4 border border-blue-200",children:r("div",{className:"flex items-start gap-3",children:[e("div",{className:"flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-blue-500 text-white",children:e(l,{className:"h-3.5 w-3.5"})}),r("div",{className:"flex-1",children:[e("span",{className:"text-xs font-semibold text-blue-700 uppercase tracking-wider",children:"Giải thích"}),e("p",{className:"mt-1 text-sm text-gray-700 leading-relaxed",children:p})]})]})})]})]})}
|