@tinyweb_dev/oe-exam-sdk 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/exam-create.js +1 -179
- package/dist/api/exam-create.types.js +1 -1
- package/dist/api/exam-questions.d.ts +1 -1
- package/dist/api/exam-questions.js +1 -98
- package/dist/api/exam-questions.types.d.ts +45 -1
- package/dist/api/exam-questions.types.js +1 -1
- package/dist/api/exam-taking.js +1 -73
- package/dist/api/exam-taking.types.js +1 -1
- package/dist/api/index.js +1 -6
- package/dist/api/result-review.js +1 -50
- package/dist/api/result-review.types.js +1 -1
- package/dist/components/CueCardEditor.js +1 -58
- package/dist/components/common/ResolvedImage.js +1 -15
- package/dist/components/exams/CompletionDialog.d.ts +8 -0
- package/dist/components/exams/CompletionDialog.js +1 -0
- package/dist/components/exams/CreateExamInfoForm.d.ts +4 -1
- package/dist/components/exams/CreateExamInfoForm.js +1 -35
- package/dist/components/exams/CreateExamPageContainer.d.ts +3 -0
- package/dist/components/exams/CreateExamPageContainer.js +1 -103
- package/dist/components/exams/ExamCreator.d.ts +9 -3
- package/dist/components/exams/ExamCreator.js +1 -89
- package/dist/components/exams/ExamPartTabs.d.ts +1 -1
- package/dist/components/exams/ExamPartTabs.js +1 -40
- package/dist/components/exams/ExamPreviewDialog.d.ts +20 -0
- package/dist/components/exams/ExamPreviewDialog.js +1 -0
- package/dist/components/exams/ExamQuestionGrid.d.ts +1 -1
- package/dist/components/exams/ExamQuestionGrid.js +1 -18
- package/dist/components/exams/ExamQuestionsPageContainer.d.ts +3 -0
- package/dist/components/exams/ExamQuestionsPageContainer.js +1 -90
- package/dist/components/exams/ExamViewDialog.d.ts +24 -0
- package/dist/components/exams/ExamViewDialog.js +1 -0
- package/dist/components/exams/PartAudioUploader.js +1 -9
- package/dist/components/exams/TemplateSelector.js +1 -66
- package/dist/components/exams/UnsavedChangesDialog.d.ts +8 -0
- package/dist/components/exams/UnsavedChangesDialog.js +1 -0
- package/dist/components/exams/api.d.ts +1 -1
- package/dist/components/exams/api.js +1 -3
- package/dist/components/exams/create-exam.texts.d.ts +58 -0
- package/dist/components/exams/create-exam.texts.js +1 -0
- package/dist/components/exams/create-exam.validation.js +1 -18
- package/dist/components/exams/exam-question-status.d.ts +9 -0
- package/dist/components/exams/exam-question-status.js +1 -0
- package/dist/components/exams/exam-template.utils.js +1 -18
- package/dist/components/exams/index.d.ts +11 -1
- package/dist/components/exams/index.js +1 -10
- package/dist/components/exams/take/ExamTakingPageContainer.js +1 -153
- package/dist/components/exams/take/GenericQuestionRenderer.js +1 -14
- package/dist/components/exams/take/exam-taking.utils.js +1 -88
- package/dist/components/exams/take/hooks/useSpeakingRecorder.js +1 -24
- package/dist/components/exams/take/index.js +1 -2
- package/dist/components/exams/take/types.js +1 -1
- package/dist/components/index.js +1 -42
- package/dist/components/questions/_shared/config/question-types.config.js +1 -333
- package/dist/components/questions/_shared/constants/color-palette.js +1 -21
- package/dist/components/questions/_shared/hooks/useAnswerGroupQuestionGroup.js +1 -105
- package/dist/components/questions/_shared/hooks/useBasicQuestionGroup.js +1 -222
- package/dist/components/questions/_shared/hooks/useCompareImagesGroup.js +1 -140
- package/dist/components/questions/_shared/hooks/useImageQuestionGroup.js +1 -123
- package/dist/components/questions/_shared/hooks/useInfoExchangeGroup.js +1 -50
- package/dist/components/questions/_shared/hooks/useQuestionFormState.js +1 -54
- package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +1 -123
- package/dist/components/questions/_shared/hooks/useQuestionListGroup.js +1 -121
- package/dist/components/questions/_shared/hooks/useQuestionOnChange.js +1 -70
- package/dist/components/questions/_shared/hooks/useSceneQuestionGroup.js +1 -194
- package/dist/components/questions/_shared/index.js +1 -7
- package/dist/components/questions/_shared/types/answer-the-question.type.js +1 -1
- package/dist/components/questions/_shared/types/arrange-letters-into-words.type.js +1 -1
- package/dist/components/questions/_shared/types/choose-correct-adjective.type.js +1 -3
- package/dist/components/questions/_shared/types/color-region.config.js +1 -1
- package/dist/components/questions/_shared/types/fill-in-blank.type.js +1 -1
- package/dist/components/questions/_shared/types/fill-missing-words-in-grid.type.js +1 -1
- package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +1 -1
- package/dist/components/questions/_shared/types/index.js +1 -12
- package/dist/components/questions/_shared/types/label-the-picture.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-choose-from-answer-group.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-choose-objects-in-scene.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-drag-objects-into-scene.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-answer.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-compare-images.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-image-group.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-info-exchange.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-odd-one-out.type.js +1 -3
- package/dist/components/questions/_shared/types/listen-and-speak-question-list.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-with-story-images.type.js +1 -3
- package/dist/components/questions/_shared/types/listen-and-tick-answer.type.js +1 -1
- package/dist/components/questions/_shared/types/listening-drawline.type.js +1 -1
- package/dist/components/questions/_shared/types/look-and-choose-answer.type.js +1 -1
- package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +1 -1
- package/dist/components/questions/_shared/types/match-by-writing-answer.type.js +1 -1
- package/dist/components/questions/_shared/types/multiple-choice.type.js +1 -1
- package/dist/components/questions/_shared/types/question-group.type.js +1 -11
- package/dist/components/questions/_shared/types/read-and-color-objects.type.js +1 -1
- package/dist/components/questions/_shared/types/read-displayed-content.type.js +1 -1
- package/dist/components/questions/_shared/types/read-passage-and-answer-questions.type.js +1 -1
- package/dist/components/questions/_shared/types/speaking-describe-image.type.js +1 -1
- package/dist/components/questions/_shared/types/spontaneous-qa.type.js +1 -1
- package/dist/components/questions/_shared/types/word-fill-paragraph-with-options.type.js +1 -1
- package/dist/components/questions/_shared/types/word-fill-paragraph.type.js +1 -1
- package/dist/components/questions/_shared/types/word-fill-structured-form.type.js +1 -1
- package/dist/components/questions/_shared/types/word-ordering.type.js +1 -1
- package/dist/components/questions/_shared/types/write-a-short-letter.type.js +1 -1
- package/dist/components/questions/_shared/types/write-correct-verb-form.type.js +1 -3
- package/dist/components/questions/_shared/types/write-sentences.type.js +1 -1
- package/dist/components/questions/_shared/types/write-short-paragraph.type.js +1 -1
- package/dist/components/questions/_shared/utils/answer-builder.js +1 -79
- package/dist/components/questions/_shared/utils/createColorRegionRegistration.js +1 -39
- package/dist/components/questions/_shared/utils/id-generator.js +1 -3
- package/dist/components/questions/_shared/utils/question-validation.js +1 -21
- package/dist/components/questions/components/ColorNode.js +1 -85
- package/dist/components/questions/components/ColorRegionClient.js +1 -545
- package/dist/components/questions/components/ColorRegionCreator.js +1 -420
- package/dist/components/questions/components/ColorRegionNode.js +1 -59
- package/dist/components/questions/components/CreatorGuide.js +1 -103
- package/dist/components/questions/components/NameNode.js +1 -26
- package/dist/components/questions/components/QuestionBankOption.js +1 -46
- package/dist/components/questions/components/QuestionBasicInfoForm.js +1 -9
- package/dist/components/questions/components/QuestionCommonFields.js +1 -33
- package/dist/components/questions/components/QuestionSearchDropdown.js +1 -258
- package/dist/components/questions/components/QuestionTypeSelector.js +1 -32
- package/dist/components/questions/components/QuestionTypeSpecificForms.js +1 -6
- package/dist/components/questions/components/RegionNode.js +1 -94
- package/dist/components/questions/components/index.js +1 -14
- package/dist/components/questions/creator/QuestionCreator.js +1 -90
- package/dist/components/questions/creator/QuestionGroupCreator.js +1 -119
- package/dist/components/questions/creator/dedicated-component-router.js +1 -79
- package/dist/components/questions/creator/index.js +1 -3
- package/dist/components/questions/creator/question-type-registry.js +1 -80
- package/dist/components/questions/groups/ArticlesGroupCard.js +1 -121
- package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -50
- package/dist/components/questions/groups/DocumentGroupCard.js +1 -114
- package/dist/components/questions/groups/MatchAnswerGroupCard.js +1 -25
- package/dist/components/questions/index.js +1 -10
- package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +1 -37
- package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +1 -351
- package/dist/components/questions/types/answer-the-question/register.js +1 -67
- package/dist/components/questions/types/answer-the-question/transform.js +1 -36
- package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +1 -141
- package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +1 -118
- package/dist/components/questions/types/arrange-letters-into-words/index.js +1 -2
- package/dist/components/questions/types/arrange-letters-into-words/register.js +1 -4
- package/dist/components/questions/types/arrange-letters-into-words/transform.js +1 -24
- package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -64
- package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +1 -169
- package/dist/components/questions/types/choose-correct-adjective/index.js +1 -2
- package/dist/components/questions/types/choose-correct-adjective/register.js +1 -55
- package/dist/components/questions/types/choose-correct-adjective/transform.js +1 -16
- package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +1 -131
- package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +1 -581
- package/dist/components/questions/types/choose-the-correct-answer/index.js +1 -2
- package/dist/components/questions/types/choose-the-correct-answer/register.js +1 -101
- package/dist/components/questions/types/choose-the-correct-answer/transform.js +1 -43
- package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +1 -275
- package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +1 -448
- package/dist/components/questions/types/fill-in-blank/index.js +1 -2
- package/dist/components/questions/types/fill-in-blank/register.js +1 -138
- package/dist/components/questions/types/fill-in-blank/transform.js +1 -59
- package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +1 -117
- package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +1 -671
- package/dist/components/questions/types/fill-missing-words-in-grid/index.js +1 -2
- package/dist/components/questions/types/fill-missing-words-in-grid/register.js +1 -37
- package/dist/components/questions/types/fill-missing-words-in-grid/transform.js +1 -15
- package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +1 -262
- package/dist/components/questions/types/gn-speaking-interview/register.js +1 -121
- package/dist/components/questions/types/gn-speaking-interview/transform.js +1 -48
- package/dist/components/questions/types/image-object-matching/ImageObjectMatchingClient.js +1 -16
- package/dist/components/questions/types/image-object-matching/ImageObjectMatchingCreator.js +1 -27
- package/dist/components/questions/types/image-object-matching/index.js +1 -2
- package/dist/components/questions/types/image-object-matching/register.js +1 -3
- package/dist/components/questions/types/image-object-matching/transform.js +1 -2
- package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +1 -102
- package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +1 -344
- package/dist/components/questions/types/label-the-picture/index.js +1 -2
- package/dist/components/questions/types/label-the-picture/register.js +1 -54
- package/dist/components/questions/types/label-the-picture/transform.js +1 -28
- package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +1 -78
- package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +1 -232
- package/dist/components/questions/types/listen-and-choose-from-answer-group/index.js +1 -2
- package/dist/components/questions/types/listen-and-choose-from-answer-group/register.js +1 -82
- package/dist/components/questions/types/listen-and-choose-from-answer-group/transform.js +1 -27
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +1 -70
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +1 -386
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/index.js +1 -2
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/register.js +1 -100
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/transform.js +1 -33
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +1 -106
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +1 -457
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/index.js +1 -2
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/register.js +1 -110
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/transform.js +1 -35
- package/dist/components/questions/types/listen-and-speak-answer/ArrowIndicator.js +1 -8
- package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -22
- package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +1 -291
- package/dist/components/questions/types/listen-and-speak-answer/index.js +1 -2
- package/dist/components/questions/types/listen-and-speak-answer/register.js +1 -84
- package/dist/components/questions/types/listen-and-speak-answer/transform.js +1 -27
- package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -32
- package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +1 -295
- package/dist/components/questions/types/listen-and-speak-compare-images/register.js +1 -79
- package/dist/components/questions/types/listen-and-speak-compare-images/transform.js +1 -27
- package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -34
- package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +1 -285
- package/dist/components/questions/types/listen-and-speak-image-group/register.js +1 -81
- package/dist/components/questions/types/listen-and-speak-image-group/transform.js +1 -26
- package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -38
- package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +1 -385
- package/dist/components/questions/types/listen-and-speak-info-exchange/register.js +1 -87
- package/dist/components/questions/types/listen-and-speak-info-exchange/transform.js +1 -32
- package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -26
- package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +1 -247
- package/dist/components/questions/types/listen-and-speak-odd-one-out/register.js +1 -59
- package/dist/components/questions/types/listen-and-speak-odd-one-out/transform.js +1 -18
- package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -26
- package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +1 -256
- package/dist/components/questions/types/listen-and-speak-question-list/register.js +1 -81
- package/dist/components/questions/types/listen-and-speak-question-list/transform.js +1 -26
- package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -26
- package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +1 -230
- package/dist/components/questions/types/listen-and-speak-with-story-images/register.js +1 -56
- package/dist/components/questions/types/listen-and-speak-with-story-images/transform.js +1 -16
- package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +1 -72
- package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +1 -230
- package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +1 -1
- package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +1 -66
- package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +1 -42
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +1 -96
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +1 -238
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +1 -2
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +1 -76
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +1 -19
- package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +1 -134
- package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +1 -617
- package/dist/components/questions/types/look-picture-fill-word-hint/index.js +1 -2
- package/dist/components/questions/types/look-picture-fill-word-hint/register.js +1 -82
- package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +1 -29
- package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +1 -38
- package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +1 -359
- package/dist/components/questions/types/match-by-writing-answer/index.js +1 -3
- package/dist/components/questions/types/match-by-writing-answer/register.js +1 -58
- package/dist/components/questions/types/match-by-writing-answer/transform.js +1 -61
- package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsClient.js +1 -16
- package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsCreator.js +1 -23
- package/dist/components/questions/types/read-and-color-objects/index.js +1 -2
- package/dist/components/questions/types/read-and-color-objects/register.js +1 -3
- package/dist/components/questions/types/read-and-color-objects/transform.js +1 -36
- package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +1 -125
- package/dist/components/questions/types/read-displayed-content/register.js +1 -64
- package/dist/components/questions/types/read-displayed-content/transform.js +1 -12
- package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -64
- package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +1 -511
- package/dist/components/questions/types/read-passage-and-answer-questions/index.js +1 -2
- package/dist/components/questions/types/read-passage-and-answer-questions/register.js +1 -58
- package/dist/components/questions/types/read-passage-and-answer-questions/transform.js +1 -28
- package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +1 -373
- package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +1 -41
- package/dist/components/questions/types/speaking-conversation/register.js +1 -110
- package/dist/components/questions/types/speaking-conversation/transform.js +1 -34
- package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +1 -74
- package/dist/components/questions/types/speaking-cue-card/register.js +1 -52
- package/dist/components/questions/types/speaking-cue-card/transform.js +1 -12
- package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -10
- package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +1 -161
- package/dist/components/questions/types/speaking-describe-image/index.js +1 -2
- package/dist/components/questions/types/speaking-describe-image/register.js +1 -69
- package/dist/components/questions/types/speaking-describe-image/transform.js +1 -16
- package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +1 -234
- package/dist/components/questions/types/spontaneous-qa/register.js +1 -93
- package/dist/components/questions/types/spontaneous-qa/transform.js +1 -34
- package/dist/components/questions/types/true-false/TrueFalseClient.js +1 -75
- package/dist/components/questions/types/true-false/TrueFalseCreator.js +1 -244
- package/dist/components/questions/types/true-false/index.js +1 -2
- package/dist/components/questions/types/true-false/register.js +1 -77
- package/dist/components/questions/types/true-false/transform.js +1 -32
- package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +1 -233
- package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +1 -631
- package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphWrapper.js +1 -28
- package/dist/components/questions/types/word-fill-paragraph/index.js +1 -2
- package/dist/components/questions/types/word-fill-paragraph/register.js +1 -136
- package/dist/components/questions/types/word-fill-paragraph/transform.js +1 -56
- package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -138
- package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +1 -261
- package/dist/components/questions/types/word-fill-structured-form/index.js +1 -2
- package/dist/components/questions/types/word-fill-structured-form/register.js +1 -67
- package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -40
- package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -84
- package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +1 -192
- package/dist/components/questions/types/word-ordering/index.js +1 -2
- package/dist/components/questions/types/word-ordering/register.js +1 -46
- package/dist/components/questions/types/word-ordering/transform.js +1 -24
- package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +1 -98
- package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +1 -320
- package/dist/components/questions/types/write-a-short-letter/index.js +1 -2
- package/dist/components/questions/types/write-a-short-letter/register.js +1 -68
- package/dist/components/questions/types/write-a-short-letter/transform.js +1 -25
- package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +1 -61
- package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +1 -196
- package/dist/components/questions/types/write-correct-verb-form/index.js +1 -2
- package/dist/components/questions/types/write-correct-verb-form/register.js +1 -62
- package/dist/components/questions/types/write-correct-verb-form/transform.js +1 -17
- package/dist/components/questions/types/write-sentences/WriteSentencesClient.js +1 -22
- package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +1 -174
- package/dist/components/questions/types/write-sentences/index.js +1 -2
- package/dist/components/questions/types/write-sentences/register.js +1 -58
- package/dist/components/questions/types/write-sentences/transform.js +1 -28
- package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -96
- package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +1 -161
- package/dist/components/questions/types/write-short-paragraph/index.js +1 -2
- package/dist/components/questions/types/write-short-paragraph/register.js +1 -4
- package/dist/components/questions/types/write-short-paragraph/transform.js +1 -21
- package/dist/components/questions/viewer/QuestionGroupViewer.js +1 -36
- package/dist/components/questions/viewer/QuestionViewer.js +1 -446
- package/dist/components/questions/viewer/index.js +1 -2
- package/dist/components/results/ResultReviewPageContainer.js +1 -98
- package/dist/components/results/ReviewQuestionRenderer.js +1 -72
- package/dist/components/results/index.js +1 -2
- package/dist/components/results/result-review.utils.js +1 -6
- package/dist/components/results/review-data.js +1 -150
- package/dist/components/results/review-renderer.utils.js +1 -97
- package/dist/components/results/review-types.js +1 -1
- package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +1 -138
- package/dist/components/themes/cambridge-yle/CambridgeYleBottomNav.js +1 -12
- package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.js +1 -18
- package/dist/components/themes/cambridge-yle/CambridgeYleExampleText.js +1 -11
- package/dist/components/themes/cambridge-yle/CambridgeYleHeader.js +1 -11
- package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.js +1 -9
- package/dist/components/themes/cambridge-yle/CambridgeYleNavButton.js +1 -13
- package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.js +1 -8
- package/dist/components/themes/cambridge-yle/CambridgeYleProgressBar.js +1 -6
- package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCard.js +1 -5
- package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCardFullWidth.js +1 -9
- package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.js +1 -41
- package/dist/components/themes/cambridge-yle/CambridgeYleTimer.js +1 -25
- package/dist/components/themes/cambridge-yle/index.js +1 -13
- package/dist/components/themes/english-certification/EcAnswerTheQuestion.js +1 -24
- package/dist/components/themes/english-certification/EcMatchByWritingAnswer.js +1 -88
- package/dist/components/themes/english-certification/EcWordFillParagraph.js +1 -55
- package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -156
- package/dist/components/themes/english-certification/EcWritingTask.js +1 -34
- package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +1 -114
- package/dist/components/themes/english-certification/EnglishCertificationBrand.js +1 -11
- package/dist/components/themes/english-certification/EnglishCertificationExamLayout.js +1 -21
- package/dist/components/themes/english-certification/EnglishCertificationFlagButton.js +1 -15
- package/dist/components/themes/english-certification/EnglishCertificationFooterBar.js +1 -19
- package/dist/components/themes/english-certification/EnglishCertificationGroupedQuestion.js +1 -148
- package/dist/components/themes/english-certification/EnglishCertificationHeader.js +1 -31
- package/dist/components/themes/english-certification/EnglishCertificationNavigationPanel.js +1 -71
- package/dist/components/themes/english-certification/EnglishCertificationOptionSelector.js +1 -33
- package/dist/components/themes/english-certification/EnglishCertificationPassagePanel.js +1 -16
- package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.js +1 -22
- package/dist/components/themes/english-certification/EnglishCertificationQuestionItem.js +1 -37
- package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +1 -72
- package/dist/components/themes/english-certification/EnglishCertificationReadingSection.js +1 -90
- package/dist/components/themes/english-certification/EnglishCertificationSingleQuestion.js +1 -18
- package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js +1 -29
- package/dist/components/themes/english-certification/english-certification-theme.js +1 -58
- package/dist/components/themes/english-certification/grouped-question-utils.js +1 -218
- package/dist/components/themes/english-certification/index.js +1 -21
- package/dist/components/themes/index.js +1 -6
- package/dist/components/themes/summer-sky/SummerSkyAudioPlayer.js +1 -170
- package/dist/components/themes/summer-sky/SummerSkyLayout.js +1 -221
- package/dist/components/themes/summer-sky/SummerSkySubmitModal.js +1 -18
- package/dist/components/themes/summer-sky/SummerSkyWaitingRoom.js +1 -194
- package/dist/components/themes/types.js +1 -1
- package/dist/components/ui/alert-dialog.js +1 -26
- package/dist/components/ui/alert.js +1 -22
- package/dist/components/ui/avatar.js +1 -12
- package/dist/components/ui/badge.js +1 -38
- package/dist/components/ui/button.js +1 -35
- package/dist/components/ui/calendar.js +1 -76
- package/dist/components/ui/card.js +1 -16
- package/dist/components/ui/carousel.js +1 -91
- package/dist/components/ui/checkbox.js +1 -9
- package/dist/components/ui/collapsible.js +1 -13
- package/dist/components/ui/command.js +1 -34
- package/dist/components/ui/confirm-dialog.js +1 -49
- package/dist/components/ui/dialog.js +1 -23
- package/dist/components/ui/drawer.js +1 -35
- package/dist/components/ui/dropdown-menu.js +1 -33
- package/dist/components/ui/file-upload.js +1 -155
- package/dist/components/ui/form.js +1 -62
- package/dist/components/ui/hover-card.js +1 -10
- package/dist/components/ui/image-preview.js +1 -29
- package/dist/components/ui/input.js +1 -8
- package/dist/components/ui/label.js +1 -9
- package/dist/components/ui/language-switcher.js +1 -15
- package/dist/components/ui/multi-select.js +1 -90
- package/dist/components/ui/points-input.js +1 -33
- package/dist/components/ui/popover.js +1 -26
- package/dist/components/ui/progress.js +1 -16
- package/dist/components/ui/radio-group.js +1 -12
- package/dist/components/ui/scroll-area.js +1 -11
- package/dist/components/ui/select.js +1 -38
- package/dist/components/ui/separator.js +1 -8
- package/dist/components/ui/sheet.js +1 -40
- package/dist/components/ui/single-select.js +1 -76
- package/dist/components/ui/skeleton.js +1 -6
- package/dist/components/ui/switch.js +1 -8
- package/dist/components/ui/table.js +1 -20
- package/dist/components/ui/tabs.js +1 -17
- package/dist/components/ui/textarea.js +1 -8
- package/dist/components/ui/tooltip.js +1 -11
- package/dist/index.d.ts +3 -3
- package/dist/index.js +1 -19
- package/dist/mcp/config.js +1 -32
- package/dist/mcp/http-client.js +1 -48
- package/dist/mcp/index.js +1 -29
- package/dist/mcp/tools/_helpers.js +1 -25
- package/dist/mcp/tools/exams.js +1 -439
- package/dist/mcp/tools/index.js +1 -6
- package/dist/mcp/tools/results.js +1 -19
- package/dist/shared/constants/ApiConstant.js +1 -276
- package/dist/shared/constants/QueryKeyConstant.js +1 -112
- package/dist/shared/constants/exam-level.enum.js +1 -41
- package/dist/shared/constants/landings/placement-test.constants.js +1 -431
- package/dist/shared/constants/question-skills.js +1 -267
- package/dist/shared/constants/routes.js +1 -81
- package/dist/shared/index.d.ts +3 -0
- package/dist/shared/index.js +1 -7
- package/dist/shared/lib/hooks/index.js +1 -10
- package/dist/shared/lib/hooks/useAlertDialog.js +1 -57
- package/dist/shared/lib/hooks/useAudioPlayCounter.js +1 -30
- package/dist/shared/lib/hooks/useAudioPreview.js +1 -81
- package/dist/shared/lib/hooks/useAudioUpload.js +1 -24
- package/dist/shared/lib/hooks/useConfirmDialog.js +1 -89
- package/dist/shared/lib/hooks/useDebouncedCallback.js +1 -32
- package/dist/shared/lib/hooks/useDelayedAutoPlay.js +1 -12
- package/dist/shared/lib/hooks/useFileUpload.js +1 -24
- package/dist/shared/lib/hooks/useImageUrl.js +1 -5
- package/dist/shared/lib/hooks/usePresignedFileUrl.js +1 -10
- package/dist/shared/lib/hooks/useReactHookForm.js +1 -31
- package/dist/shared/lib/hooks/useToast.js +1 -62
- package/dist/shared/lib/i18n/index.js +1 -54
- package/dist/shared/lib/i18n/messages/en/admin.js +1 -2104
- package/dist/shared/lib/i18n/messages/en/auth.js +1 -86
- package/dist/shared/lib/i18n/messages/en/common.js +1 -140
- package/dist/shared/lib/i18n/messages/en/file-upload.js +1 -18
- package/dist/shared/lib/i18n/messages/en/student.js +1 -133
- package/dist/shared/lib/i18n/messages/en/terms.js +1 -37
- package/dist/shared/lib/i18n/messages/en.js +1 -15
- package/dist/shared/lib/i18n/messages/vi/admin.js +1 -2104
- package/dist/shared/lib/i18n/messages/vi/auth.js +1 -86
- package/dist/shared/lib/i18n/messages/vi/common.js +1 -140
- package/dist/shared/lib/i18n/messages/vi/file-upload.js +1 -18
- package/dist/shared/lib/i18n/messages/vi/student.js +1 -133
- package/dist/shared/lib/i18n/messages/vi/terms.js +1 -37
- package/dist/shared/lib/i18n/messages/vi.js +1 -15
- package/dist/shared/lib/i18n/types.js +1 -1
- package/dist/shared/lib/stores/examQuestionStore.js +1 -212
- package/dist/shared/lib/stores/localeStore.js +1 -10
- package/dist/shared/lib/utils/public-page.js +1 -44
- package/dist/shared/lib/utils/question-reverse-transform.d.ts +6 -0
- package/dist/shared/lib/utils/question-reverse-transform.js +1 -0
- package/dist/shared/lib/utils/question-transform-types.js +1 -1
- package/dist/shared/lib/utils/question-transform-validation.d.ts +6 -0
- package/dist/shared/lib/utils/question-transform-validation.js +1 -0
- package/dist/shared/lib/utils/question-transform.d.ts +6 -2
- package/dist/shared/lib/utils/question-transform.js +1 -174
- package/dist/shared/lib/utils.js +1 -168
- package/dist/shared/lib/validation/choose-correct-answer.validation.js +1 -111
- package/dist/shared/lib/validation/label-the-picture.validation.js +1 -19
- package/dist/shared/lib/validation/listen-and-choose-from-answer-group.validation.js +1 -46
- package/dist/shared/lib/validation/listen-and-choose-objects-in-scene.validation.js +1 -21
- package/dist/shared/lib/validation/listen-and-drag-objects-into-scene.validation.js +1 -34
- package/dist/shared/lib/validation/listen-and-speak-answer.validation.js +1 -40
- package/dist/shared/lib/validation/listen-and-speak-compare-images.validation.js +1 -45
- package/dist/shared/lib/validation/listen-and-speak-image-group.validation.js +1 -19
- package/dist/shared/lib/validation/listen-and-speak-info-exchange.validation.js +1 -39
- package/dist/shared/lib/validation/listen-and-speak-odd-one-out.validation.js +1 -32
- package/dist/shared/lib/validation/listen-and-speak-question-list.validation.js +1 -19
- package/dist/shared/lib/validation/listen-and-speak-with-story-images.validation.js +1 -28
- package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +1 -12
- package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +1 -47
- package/dist/shared/lib/validation/read-and-color-objects.validation.js +1 -29
- package/dist/shared/lib/validation/read-displayed-content.validation.js +1 -28
- package/dist/shared/lib/validation/speaking-conversation.validation.js +1 -75
- package/dist/shared/lib/validation/speaking-describe-image.validation.js +1 -31
- package/dist/shared/lib/validation/spontaneous-qa.validation.js +1 -48
- package/dist/shared/lib/validation/word-fill-paragraph-default.validation.js +1 -38
- package/dist/shared/lib/validation/word-fill-paragraph-with-options.validation.js +1 -39
- package/dist/shared/lib/validation/word-fill-paragraph.validation.js +1 -43
- package/dist/shared/lib/validation/word-ordering.validation.js +1 -33
- package/dist/shared/types/branch.types.js +1 -6
- package/dist/shared/types/common.types.js +1 -50
- package/dist/shared/types/entities/exam-schedule.types.js +1 -5
- package/dist/shared/types/entities/exam.types.js +1 -1
- package/dist/shared/types/entities/question.types.js +1 -4
- package/dist/shared/types/entities/result.types.js +1 -4
- package/dist/shared/types/entities/user.types.js +1 -4
- package/dist/shared/types/exam-taking.types.js +1 -2
- package/dist/shared/types/history.types.js +1 -34
- package/dist/shared/types/index.js +1 -1
- package/dist/shared/types/questions/choose-correct-adjective.js +1 -4
- package/dist/shared/types/questions/choose-the-correct-answer.js +1 -9
- package/dist/shared/types/questions/fill-missing-words-in-grid.js +1 -11
- package/dist/shared/types/questions/index.js +1 -40
- package/dist/shared/types/questions/listen-and-speak-answer.js +1 -1
- package/dist/shared/types/questions/listen-and-speak-compare-images.js +1 -1
- package/dist/shared/types/questions/listen-and-speak-image-group.js +1 -1
- package/dist/shared/types/questions/listen-and-speak-info-exchange.js +1 -3
- package/dist/shared/types/questions/listen-and-speak-odd-one-out.js +1 -3
- package/dist/shared/types/questions/listen-and-speak-question-list.js +1 -1
- package/dist/shared/types/questions/listen-and-speak-with-story-images.js +1 -3
- package/dist/shared/types/questions/look-picture-choose-correct-answer.js +1 -5
- package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +1 -9
- package/dist/shared/types/questions/look-picture-fill-word-hint.js +1 -7
- package/dist/shared/types/questions/read-displayed-content.js +1 -1
- package/dist/shared/types/questions/speaking-conversation.js +1 -7
- package/dist/shared/types/questions/speaking-describe-image.js +1 -1
- package/dist/shared/types/questions/spontaneous-qa.js +1 -1
- package/dist/shared/types/questions/write-correct-verb-form.js +1 -12
- package/dist/shared/types/role.types.js +1 -1
- package/dist/shared/types/structure-management.js +1 -28
- package/dist/shared/types/student.types.js +1 -54
- package/dist/shared/types/teacher-dashboard-rooms.js +1 -2
- package/package.json +12 -3
|
@@ -1,103 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import { Lightbulb, ChevronDown } from 'lucide-react';
|
|
5
|
-
import { Collapsible, CollapsibleTrigger, CollapsibleContent } from '../../../components/ui/collapsible';
|
|
6
|
-
import { cn } from '../../../shared/lib/utils';
|
|
7
|
-
// ─── Color Scheme Map ─────────────────────────────────────────────────────────
|
|
8
|
-
const colorSchemes = {
|
|
9
|
-
blue: {
|
|
10
|
-
border: 'border-blue-200',
|
|
11
|
-
bg: 'bg-gradient-to-r from-blue-50 to-indigo-50',
|
|
12
|
-
icon: 'bg-blue-100 text-blue-600',
|
|
13
|
-
title: 'text-blue-800',
|
|
14
|
-
dot: 'bg-blue-400',
|
|
15
|
-
text: 'text-blue-700',
|
|
16
|
-
tipBg: 'bg-blue-100/60',
|
|
17
|
-
tipText: 'text-blue-700',
|
|
18
|
-
hoverBg: 'hover:from-blue-100/80 hover:to-indigo-100/60',
|
|
19
|
-
},
|
|
20
|
-
purple: {
|
|
21
|
-
border: 'border-purple-200',
|
|
22
|
-
bg: 'bg-gradient-to-r from-purple-50 to-violet-50',
|
|
23
|
-
icon: 'bg-purple-100 text-purple-600',
|
|
24
|
-
title: 'text-purple-800',
|
|
25
|
-
dot: 'bg-purple-400',
|
|
26
|
-
text: 'text-purple-700',
|
|
27
|
-
tipBg: 'bg-purple-100/60',
|
|
28
|
-
tipText: 'text-purple-700',
|
|
29
|
-
hoverBg: 'hover:from-purple-100/80 hover:to-violet-100/60',
|
|
30
|
-
},
|
|
31
|
-
emerald: {
|
|
32
|
-
border: 'border-emerald-200',
|
|
33
|
-
bg: 'bg-gradient-to-r from-emerald-50 to-teal-50',
|
|
34
|
-
icon: 'bg-emerald-100 text-emerald-600',
|
|
35
|
-
title: 'text-emerald-800',
|
|
36
|
-
dot: 'bg-emerald-400',
|
|
37
|
-
text: 'text-emerald-700',
|
|
38
|
-
tipBg: 'bg-emerald-100/60',
|
|
39
|
-
tipText: 'text-emerald-700',
|
|
40
|
-
hoverBg: 'hover:from-emerald-100/80 hover:to-teal-100/60',
|
|
41
|
-
},
|
|
42
|
-
amber: {
|
|
43
|
-
border: 'border-amber-200',
|
|
44
|
-
bg: 'bg-gradient-to-r from-amber-50 to-orange-50',
|
|
45
|
-
icon: 'bg-amber-100 text-amber-600',
|
|
46
|
-
title: 'text-amber-800',
|
|
47
|
-
dot: 'bg-amber-400',
|
|
48
|
-
text: 'text-amber-700',
|
|
49
|
-
tipBg: 'bg-amber-100/60',
|
|
50
|
-
tipText: 'text-amber-700',
|
|
51
|
-
hoverBg: 'hover:from-amber-100/80 hover:to-orange-100/60',
|
|
52
|
-
},
|
|
53
|
-
cyan: {
|
|
54
|
-
border: 'border-cyan-200',
|
|
55
|
-
bg: 'bg-gradient-to-r from-cyan-50 to-sky-50',
|
|
56
|
-
icon: 'bg-cyan-100 text-cyan-600',
|
|
57
|
-
title: 'text-cyan-800',
|
|
58
|
-
dot: 'bg-cyan-400',
|
|
59
|
-
text: 'text-cyan-700',
|
|
60
|
-
tipBg: 'bg-cyan-100/60',
|
|
61
|
-
tipText: 'text-cyan-700',
|
|
62
|
-
hoverBg: 'hover:from-cyan-100/80 hover:to-sky-100/60',
|
|
63
|
-
},
|
|
64
|
-
rose: {
|
|
65
|
-
border: 'border-rose-200',
|
|
66
|
-
bg: 'bg-gradient-to-r from-rose-50 to-pink-50',
|
|
67
|
-
icon: 'bg-rose-100 text-rose-600',
|
|
68
|
-
title: 'text-rose-800',
|
|
69
|
-
dot: 'bg-rose-400',
|
|
70
|
-
text: 'text-rose-700',
|
|
71
|
-
tipBg: 'bg-rose-100/60',
|
|
72
|
-
tipText: 'text-rose-700',
|
|
73
|
-
hoverBg: 'hover:from-rose-100/80 hover:to-pink-100/60',
|
|
74
|
-
},
|
|
75
|
-
teal: {
|
|
76
|
-
border: 'border-teal-200',
|
|
77
|
-
bg: 'bg-gradient-to-r from-teal-50 to-emerald-50',
|
|
78
|
-
icon: 'bg-teal-100 text-teal-600',
|
|
79
|
-
title: 'text-teal-800',
|
|
80
|
-
dot: 'bg-teal-400',
|
|
81
|
-
text: 'text-teal-700',
|
|
82
|
-
tipBg: 'bg-teal-100/60',
|
|
83
|
-
tipText: 'text-teal-700',
|
|
84
|
-
hoverBg: 'hover:from-teal-100/80 hover:to-emerald-100/60',
|
|
85
|
-
},
|
|
86
|
-
orange: {
|
|
87
|
-
border: 'border-orange-200',
|
|
88
|
-
bg: 'bg-gradient-to-r from-orange-50 to-amber-50',
|
|
89
|
-
icon: 'bg-orange-100 text-orange-600',
|
|
90
|
-
title: 'text-orange-800',
|
|
91
|
-
dot: 'bg-orange-400',
|
|
92
|
-
text: 'text-orange-700',
|
|
93
|
-
tipBg: 'bg-orange-100/60',
|
|
94
|
-
tipText: 'text-orange-700',
|
|
95
|
-
hoverBg: 'hover:from-orange-100/80 hover:to-amber-100/60',
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
// ─── Component ────────────────────────────────────────────────────────────────
|
|
99
|
-
export function CreatorGuide({ steps, tips = [], colorScheme = 'blue' }) {
|
|
100
|
-
const [isOpen, setIsOpen] = useState(false);
|
|
101
|
-
const colors = colorSchemes[colorScheme];
|
|
102
|
-
return (_jsx(Collapsible, { open: isOpen, onOpenChange: setIsOpen, children: _jsxs("div", { className: cn('rounded-xl border shadow-sm overflow-hidden', colors.border, colors.bg), children: [_jsx(CollapsibleTrigger, { asChild: true, children: _jsxs("button", { type: "button", className: cn('flex w-full items-center gap-3 px-5 py-3.5 transition-colors cursor-pointer', colors.bg, colors.hoverBg), children: [_jsx("div", { className: cn('flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg', colors.icon), children: _jsx(Lightbulb, { className: "h-4 w-4" }) }), _jsx("span", { className: cn('text-sm font-semibold flex-1 text-left', colors.title), children: "H\u01B0\u1EDBng d\u1EABn t\u1EA1o c\u00E2u h\u1ECFi" }), _jsx(ChevronDown, { className: cn('h-4 w-4 transition-transform duration-200', colors.text, isOpen && 'rotate-180') })] }) }), _jsx(CollapsibleContent, { children: _jsx("div", { className: "px-5 pb-4 pt-0", children: _jsxs("div", { className: "ml-11 space-y-1.5", children: [steps.map((step, index) => (_jsxs("div", { className: "flex items-start gap-2.5", children: [_jsx("span", { className: cn('mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full', colors.dot) }), _jsx("span", { className: cn('text-sm leading-relaxed', colors.text, step.highlight && 'font-semibold'), children: step.text })] }, index))), tips.length > 0 && (_jsx("div", { className: cn('mt-3 rounded-lg px-3 py-2', colors.tipBg), children: tips.map((tip, index) => (_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("span", { className: "text-sm", children: "\uD83D\uDCA1" }), _jsxs("span", { className: cn('text-sm font-medium', colors.tipText), children: [_jsx("strong", { children: "M\u1EB9o:" }), " ", tip] })] }, index))) }))] }) }) })] }) }));
|
|
103
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useState as r}from"react";import{Lightbulb as o,ChevronDown as a}from"lucide-react";import{Collapsible as l,CollapsibleTrigger as i,CollapsibleContent as n}from"../../../components/ui/collapsible";import{cn as d}from"../../../shared/lib/utils";const g={blue:{border:"border-blue-200",bg:"bg-gradient-to-r from-blue-50 to-indigo-50",icon:"bg-blue-100 text-blue-600",title:"text-blue-800",dot:"bg-blue-400",text:"text-blue-700",tipBg:"bg-blue-100/60",tipText:"text-blue-700",hoverBg:"hover:from-blue-100/80 hover:to-indigo-100/60"},purple:{border:"border-purple-200",bg:"bg-gradient-to-r from-purple-50 to-violet-50",icon:"bg-purple-100 text-purple-600",title:"text-purple-800",dot:"bg-purple-400",text:"text-purple-700",tipBg:"bg-purple-100/60",tipText:"text-purple-700",hoverBg:"hover:from-purple-100/80 hover:to-violet-100/60"},emerald:{border:"border-emerald-200",bg:"bg-gradient-to-r from-emerald-50 to-teal-50",icon:"bg-emerald-100 text-emerald-600",title:"text-emerald-800",dot:"bg-emerald-400",text:"text-emerald-700",tipBg:"bg-emerald-100/60",tipText:"text-emerald-700",hoverBg:"hover:from-emerald-100/80 hover:to-teal-100/60"},amber:{border:"border-amber-200",bg:"bg-gradient-to-r from-amber-50 to-orange-50",icon:"bg-amber-100 text-amber-600",title:"text-amber-800",dot:"bg-amber-400",text:"text-amber-700",tipBg:"bg-amber-100/60",tipText:"text-amber-700",hoverBg:"hover:from-amber-100/80 hover:to-orange-100/60"},cyan:{border:"border-cyan-200",bg:"bg-gradient-to-r from-cyan-50 to-sky-50",icon:"bg-cyan-100 text-cyan-600",title:"text-cyan-800",dot:"bg-cyan-400",text:"text-cyan-700",tipBg:"bg-cyan-100/60",tipText:"text-cyan-700",hoverBg:"hover:from-cyan-100/80 hover:to-sky-100/60"},rose:{border:"border-rose-200",bg:"bg-gradient-to-r from-rose-50 to-pink-50",icon:"bg-rose-100 text-rose-600",title:"text-rose-800",dot:"bg-rose-400",text:"text-rose-700",tipBg:"bg-rose-100/60",tipText:"text-rose-700",hoverBg:"hover:from-rose-100/80 hover:to-pink-100/60"},teal:{border:"border-teal-200",bg:"bg-gradient-to-r from-teal-50 to-emerald-50",icon:"bg-teal-100 text-teal-600",title:"text-teal-800",dot:"bg-teal-400",text:"text-teal-700",tipBg:"bg-teal-100/60",tipText:"text-teal-700",hoverBg:"hover:from-teal-100/80 hover:to-emerald-100/60"},orange:{border:"border-orange-200",bg:"bg-gradient-to-r from-orange-50 to-amber-50",icon:"bg-orange-100 text-orange-600",title:"text-orange-800",dot:"bg-orange-400",text:"text-orange-700",tipBg:"bg-orange-100/60",tipText:"text-orange-700",hoverBg:"hover:from-orange-100/80 hover:to-amber-100/60"}};export function CreatorGuide({steps:b,tips:m=[],colorScheme:s="blue"}){const[p,x]=r(!1),c=g[s];return e(l,{open:p,onOpenChange:x,children:t("div",{className:d("rounded-xl border shadow-sm overflow-hidden",c.border,c.bg),children:[e(i,{asChild:!0,children:t("button",{type:"button",className:d("flex w-full items-center gap-3 px-5 py-3.5 transition-colors cursor-pointer",c.bg,c.hoverBg),children:[e("div",{className:d("flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg",c.icon),children:e(o,{className:"h-4 w-4"})}),e("span",{className:d("text-sm font-semibold flex-1 text-left",c.title),children:"Hướng dẫn tạo câu hỏi"}),e(a,{className:d("h-4 w-4 transition-transform duration-200",c.text,p&&"rotate-180")})]})}),e(n,{children:e("div",{className:"px-5 pb-4 pt-0",children:t("div",{className:"ml-11 space-y-1.5",children:[b.map((r,o)=>t("div",{className:"flex items-start gap-2.5",children:[e("span",{className:d("mt-1.5 h-1.5 w-1.5 flex-shrink-0 rounded-full",c.dot)}),e("span",{className:d("text-sm leading-relaxed",c.text,r.highlight&&"font-semibold"),children:r.text})]},o)),m.length>0&&e("div",{className:d("mt-3 rounded-lg px-3 py-2",c.tipBg),children:m.map((r,o)=>t("div",{className:"flex items-start gap-2",children:[e("span",{className:"text-sm",children:"💡"}),t("span",{className:d("text-sm font-medium",c.tipText),children:[e("strong",{children:"Mẹo:"})," ",r]})]},o))})]})})})]})})}
|
|
@@ -1,26 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Button } from '../../../components/ui/button';
|
|
3
|
-
import { Badge } from '../../../components/ui/badge';
|
|
4
|
-
import { X } from 'lucide-react';
|
|
5
|
-
export function NameNode({ data, variant = 'creator' }) {
|
|
6
|
-
const isClientMode = variant === 'client';
|
|
7
|
-
const isCompact = data.displayMode === 'compact';
|
|
8
|
-
const textClass = isCompact ? 'text-[11px]' : 'text-[13px]';
|
|
9
|
-
if (isClientMode) {
|
|
10
|
-
const baseClass = data.isConnected
|
|
11
|
-
? 'relative flex h-full w-full items-center justify-center overflow-hidden rounded-2xl bg-white transition-all duration-200 ease-out'
|
|
12
|
-
: 'relative inline-flex min-w-[110px] items-center justify-center overflow-hidden rounded-2xl border border-gray-300 bg-white px-3 py-2 transition-all duration-200 ease-out hover:border-blue-400 cursor-move';
|
|
13
|
-
return (_jsxs("div", { className: `${baseClass} ${data.highlightClass ?? ''}`, style: data.highlightStyle, children: [data.isHighlighted && (_jsx("div", { className: "pointer-events-none absolute inset-0 z-[1] rounded-2xl bg-black/40" })), _jsx("span", { className: `relative ${data.isHighlighted ? 'z-[2]' : 'z-[1]'} font-medium text-black ${textClass}`, children: data.name })] }));
|
|
14
|
-
}
|
|
15
|
-
const isConnectedCreator = data.isConnected;
|
|
16
|
-
const baseClassesCreator = isCompact
|
|
17
|
-
? 'relative inline-flex min-w-[110px] items-center justify-between rounded-2xl border px-3 py-2 text-xs transition-all'
|
|
18
|
-
: 'relative flex min-w-[180px] items-center justify-between rounded-2xl border-2 px-3 py-2 text-sm transition-all';
|
|
19
|
-
const creatorClass = isConnectedCreator
|
|
20
|
-
? `${baseClassesCreator} border-green-500 bg-green-50 ring-2 ring-green-200`
|
|
21
|
-
: `${baseClassesCreator} border-gray-200 bg-white hover:border-blue-300`;
|
|
22
|
-
return (_jsxs("div", { className: creatorClass, children: [_jsx("span", { className: `font-medium text-gray-900 ${textClass}`, children: data.name }), _jsxs("div", { className: "flex items-center gap-2", children: [data.isConnected && (_jsx(Badge, { variant: "outline", className: "bg-green-50 text-xs text-green-700", children: "\u2713" })), data.isEditMode && (_jsx(Button, { size: "icon", variant: "ghost", className: "h-6 w-6", onClick: (e) => {
|
|
23
|
-
e.stopPropagation();
|
|
24
|
-
data.onRemove();
|
|
25
|
-
}, children: _jsx(X, { className: "h-3 w-3" }) }))] })] }));
|
|
26
|
-
}
|
|
1
|
+
import{jsx as e,jsxs as t}from"react/jsx-runtime";import{Button as i}from"../../../components/ui/button";import{Badge as n}from"../../../components/ui/badge";import{X as r}from"lucide-react";export function NameNode({data:o,variant:a="creator"}){const s="client"===a,l="compact"===o.displayMode,d=l?"text-[11px]":"text-[13px]";if(s){const i=o.isConnected?"relative flex h-full w-full items-center justify-center overflow-hidden rounded-2xl bg-white transition-all duration-200 ease-out":"relative inline-flex min-w-[110px] items-center justify-center overflow-hidden rounded-2xl border border-gray-300 bg-white px-3 py-2 transition-all duration-200 ease-out hover:border-blue-400 cursor-move";return t("div",{className:`${i} ${o.highlightClass??""}`,style:o.highlightStyle,children:[o.isHighlighted&&e("div",{className:"pointer-events-none absolute inset-0 z-[1] rounded-2xl bg-black/40"}),e("span",{className:`relative ${o.isHighlighted?"z-[2]":"z-[1]"} font-medium text-black ${d}`,children:o.name})]})}const c=o.isConnected,m=l?"relative inline-flex min-w-[110px] items-center justify-between rounded-2xl border px-3 py-2 text-xs transition-all":"relative flex min-w-[180px] items-center justify-between rounded-2xl border-2 px-3 py-2 text-sm transition-all";return t("div",{className:c?`${m} border-green-500 bg-green-50 ring-2 ring-green-200`:`${m} border-gray-200 bg-white hover:border-blue-300`,children:[e("span",{className:`font-medium text-gray-900 ${d}`,children:o.name}),t("div",{className:"flex items-center gap-2",children:[o.isConnected&&e(n,{variant:"outline",className:"bg-green-50 text-xs text-green-700",children:"✓"}),o.isEditMode&&e(i,{size:"icon",variant:"ghost",className:"h-6 w-6",onClick:e=>{e.stopPropagation(),o.onRemove()},children:e(r,{className:"h-3 w-3"})})]})]})}
|
|
@@ -1,46 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { Label } from '../../../components/ui/label';
|
|
4
|
-
import { Input } from '../../../components/ui/input';
|
|
5
|
-
import { Card, CardContent } from '../../../components/ui/card';
|
|
6
|
-
import { Alert, AlertDescription } from '../../../components/ui/alert';
|
|
7
|
-
import { Database, CheckCircle2, ExternalLink } from 'lucide-react';
|
|
8
|
-
import Link from 'next/link';
|
|
9
|
-
// Validation constants
|
|
10
|
-
const CODE_MAX_LENGTH = 100;
|
|
11
|
-
const TITLE_MAX_LENGTH = 255;
|
|
12
|
-
// Pattern: only alphanumeric characters, underscore, and hyphen
|
|
13
|
-
const CODE_PATTERN = /^[a-zA-Z0-9_-]*$/;
|
|
14
|
-
/**
|
|
15
|
-
* Shared component for "Add to Question Bank" option.
|
|
16
|
-
*
|
|
17
|
-
* Has two modes:
|
|
18
|
-
* 1. NEW: When existingBank is null - shows editable code/title inputs
|
|
19
|
-
* 2. EXISTING: When existingBank has data - shows read-only info with link to edit
|
|
20
|
-
*
|
|
21
|
-
* Validation rules:
|
|
22
|
-
* - Code: Required, max 100 chars, only letters, numbers, underscore (_), hyphen (-)
|
|
23
|
-
* - Title: Required, max 255 chars
|
|
24
|
-
*
|
|
25
|
-
* Used by all question type creators to allow adding questions to the question bank.
|
|
26
|
-
*/
|
|
27
|
-
export function QuestionBankOption({ checked, onCheckedChange, code, onCodeChange, title, onTitleChange, error, codeError, existingBank, disabled = false, }) {
|
|
28
|
-
// Handle code change with pattern validation
|
|
29
|
-
const handleCodeChange = (value) => {
|
|
30
|
-
// Only allow valid characters (alphanumeric, underscore, hyphen)
|
|
31
|
-
if (CODE_PATTERN.test(value)) {
|
|
32
|
-
onCodeChange(value.slice(0, CODE_MAX_LENGTH));
|
|
33
|
-
}
|
|
34
|
-
};
|
|
35
|
-
// Handle title change with max length
|
|
36
|
-
const handleTitleChange = (value) => {
|
|
37
|
-
onTitleChange(value.slice(0, TITLE_MAX_LENGTH));
|
|
38
|
-
};
|
|
39
|
-
// If existing bank data exists, show read-only mode
|
|
40
|
-
const isExistingBank = existingBank && existingBank.id;
|
|
41
|
-
return (_jsx(Card, { className: `border-dashed ${isExistingBank ? 'border-green-300 bg-green-50/50' : checked ? 'border-blue-300 bg-blue-50/50' : 'border-gray-300 bg-gray-50/50'}`, children: _jsx(CardContent, { className: "pt-4", children: isExistingBank ? (
|
|
42
|
-
// EXISTING BANK MODE - Read-only display with edit link
|
|
43
|
-
_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-full bg-green-100", children: _jsx(CheckCircle2, { className: "h-5 w-5 text-green-600" }) }), _jsx("div", { children: _jsx("span", { className: "text-sm font-medium text-green-700", children: "\u0110\u00E3 th\u00EAm v\u00E0o ng\u00E2n h\u00E0ng c\u00E2u h\u1ECFi" }) })] }), _jsxs("div", { className: "mt-4 pl-11 space-y-4", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: "questionCode", className: "text-sm font-medium text-gray-600", children: "M\u00E3 c\u00E2u h\u1ECFi" }), _jsx(Input, { id: "questionCode", value: existingBank.code, readOnly: true, disabled: true, className: "mt-1.5 bg-gray-100 text-gray-900 cursor-not-allowed" })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "questionTitle", className: "text-sm font-medium text-gray-600", children: "Ti\u00EAu \u0111\u1EC1 c\u00E2u h\u1ECFi" }), _jsx(Input, { id: "questionTitle", value: existingBank.title, readOnly: true, disabled: true, className: "mt-1.5 bg-gray-100 text-gray-900 cursor-not-allowed" })] }), _jsxs(Alert, { className: "border-blue-200 bg-blue-50", children: [_jsx(Database, { className: "h-4 w-4 text-blue-600" }), _jsx(AlertDescription, { className: "text-sm text-blue-800", children: _jsxs(Link, { href: `/manage/question-banks?code=${encodeURIComponent(existingBank.code)}`, target: "_blank", rel: "noopener noreferrer", className: "inline-flex items-center gap-1.5 text-blue-600 hover:text-blue-800 font-medium hover:underline", children: ["Ng\u00E2n h\u00E0ng c\u00E2u h\u1ECFi", _jsx(ExternalLink, { className: "h-3.5 w-3.5" })] }) })] })] })] })) : (
|
|
44
|
-
// NEW BANK MODE - Editable inputs
|
|
45
|
-
_jsxs(_Fragment, { children: [_jsxs("label", { className: `flex items-center gap-3 ${disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'}`, children: [_jsx("input", { type: "checkbox", checked: checked, onChange: (e) => !disabled && onCheckedChange(e.target.checked), disabled: disabled, className: "h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500 disabled:cursor-not-allowed" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Database, { className: `h-4 w-4 ${checked ? 'text-blue-600' : 'text-gray-500'}` }), _jsx("span", { className: `text-sm font-medium ${checked ? 'text-blue-700' : 'text-gray-700'}`, children: "Th\u00EAm v\u00E0o ng\u00E2n h\u00E0ng c\u00E2u h\u1ECFi" })] })] }), checked && (_jsxs("div", { className: "mt-4 pl-7 space-y-4", children: [_jsxs("div", { children: [_jsxs(Label, { htmlFor: "questionCode", className: "text-sm font-medium", children: ["M\u00E3 c\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "questionCode", value: code, onChange: (e) => handleCodeChange(e.target.value), placeholder: 'VD: "Q001", "GRID_001", "MC-PAST-01"...', className: `mt-1.5 ${codeError ? 'border-red-500 focus:ring-red-500' : ''}`, disabled: disabled, maxLength: CODE_MAX_LENGTH }), _jsxs("div", { className: "mt-1 flex justify-between", children: [codeError ? (_jsx("p", { className: "text-sm text-red-500", children: codeError })) : (_jsx("p", { className: "text-xs text-gray-500", children: "Ch\u1EC9 ch\u1EEF c\u00E1i, s\u1ED1, d\u1EA5u g\u1EA1ch d\u01B0\u1EDBi (_) v\u00E0 g\u1EA1ch ngang (-)" })), _jsxs("span", { className: "text-xs text-gray-400", children: [code.length, "/", CODE_MAX_LENGTH] })] })] }), _jsxs("div", { children: [_jsxs(Label, { htmlFor: "questionTitle", className: "text-sm font-medium", children: ["Ti\u00EAu \u0111\u1EC1 c\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "questionTitle", value: title, onChange: (e) => handleTitleChange(e.target.value), placeholder: 'VD: "Grid \u0111i\u1EC1n t\u1EEB v\u1EC1 \u0111\u1ED9ng v\u1EADt", "Tr\u1EAFc nghi\u1EC7m th\u00EC qu\u00E1 kh\u1EE9"...', className: `mt-1.5 ${error ? 'border-red-500 focus:ring-red-500' : ''}`, disabled: disabled, maxLength: TITLE_MAX_LENGTH }), _jsxs("div", { className: "mt-1 flex justify-between", children: [error ? (_jsx("p", { className: "text-sm text-red-500", children: error })) : (_jsx("p", { className: "text-xs text-gray-500", children: "M\u00E3 v\u00E0 ti\u00EAu \u0111\u1EC1 gi\u00FAp t\u00ECm ki\u1EBFm c\u00E2u h\u1ECFi trong \u0111\u1EC1 thi kh\u00E1c" })), _jsxs("span", { className: "text-xs text-gray-400", children: [title.length, "/", TITLE_MAX_LENGTH] })] })] })] }))] })) }) }));
|
|
46
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as t,Fragment as r}from"react/jsx-runtime";import{Label as a}from"../../../components/ui/label";import{Input as n}from"../../../components/ui/input";import{Card as l,CardContent as s}from"../../../components/ui/card";import{Alert as i,AlertDescription as c}from"../../../components/ui/alert";import{Database as d,CheckCircle2 as o,ExternalLink as m}from"lucide-react";import h from"next/link";const u=/^[a-zA-Z0-9_-]*$/;export function QuestionBankOption({checked:g,onCheckedChange:x,code:b,onCodeChange:p,title:N,onTitleChange:f,error:v,codeError:y,existingBank:C,disabled:k=!1}){const w=C&&C.id;return e(l,{className:"border-dashed "+(w?"border-green-300 bg-green-50/50":g?"border-blue-300 bg-blue-50/50":"border-gray-300 bg-gray-50/50"),children:e(s,{className:"pt-4",children:t(r,w?{children:[t("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-full bg-green-100",children:e(o,{className:"h-5 w-5 text-green-600"})}),e("div",{children:e("span",{className:"text-sm font-medium text-green-700",children:"Đã thêm vào ngân hàng câu hỏi"})})]}),t("div",{className:"mt-4 pl-11 space-y-4",children:[t("div",{children:[e(a,{htmlFor:"questionCode",className:"text-sm font-medium text-gray-600",children:"Mã câu hỏi"}),e(n,{id:"questionCode",value:C.code,readOnly:!0,disabled:!0,className:"mt-1.5 bg-gray-100 text-gray-900 cursor-not-allowed"})]}),t("div",{children:[e(a,{htmlFor:"questionTitle",className:"text-sm font-medium text-gray-600",children:"Tiêu đề câu hỏi"}),e(n,{id:"questionTitle",value:C.title,readOnly:!0,disabled:!0,className:"mt-1.5 bg-gray-100 text-gray-900 cursor-not-allowed"})]}),t(i,{className:"border-blue-200 bg-blue-50",children:[e(d,{className:"h-4 w-4 text-blue-600"}),e(c,{className:"text-sm text-blue-800",children:t(h,{href:`/manage/question-banks?code=${encodeURIComponent(C.code)}`,target:"_blank",rel:"noopener noreferrer",className:"inline-flex items-center gap-1.5 text-blue-600 hover:text-blue-800 font-medium hover:underline",children:["Ngân hàng câu hỏi",e(m,{className:"h-3.5 w-3.5"})]})})]})]})]}:{children:[t("label",{className:"flex items-center gap-3 "+(k?"cursor-not-allowed opacity-50":"cursor-pointer"),children:[e("input",{type:"checkbox",checked:g,onChange:e=>!k&&x(e.target.checked),disabled:k,className:"h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-blue-500 disabled:cursor-not-allowed"}),t("div",{className:"flex items-center gap-2",children:[e(d,{className:"h-4 w-4 "+(g?"text-blue-600":"text-gray-500")}),e("span",{className:"text-sm font-medium "+(g?"text-blue-700":"text-gray-700"),children:"Thêm vào ngân hàng câu hỏi"})]})]}),g&&t("div",{className:"mt-4 pl-7 space-y-4",children:[t("div",{children:[t(a,{htmlFor:"questionCode",className:"text-sm font-medium",children:["Mã câu hỏi ",e("span",{className:"text-red-500",children:"*"})]}),e(n,{id:"questionCode",value:b,onChange:e=>{return t=e.target.value,void(u.test(t)&&p(t.slice(0,100)));var t},placeholder:'VD: "Q001", "GRID_001", "MC-PAST-01"...',className:"mt-1.5 "+(y?"border-red-500 focus:ring-red-500":""),disabled:k,maxLength:100}),t("div",{className:"mt-1 flex justify-between",children:[e("p",y?{className:"text-sm text-red-500",children:y}:{className:"text-xs text-gray-500",children:"Chỉ chữ cái, số, dấu gạch dưới (_) và gạch ngang (-)"}),t("span",{className:"text-xs text-gray-400",children:[b.length,"/",100]})]})]}),t("div",{children:[t(a,{htmlFor:"questionTitle",className:"text-sm font-medium",children:["Tiêu đề câu hỏi ",e("span",{className:"text-red-500",children:"*"})]}),e(n,{id:"questionTitle",value:N,onChange:e=>{return t=e.target.value,void f(t.slice(0,255));var t},placeholder:'VD: "Grid điền từ về động vật", "Trắc nghiệm thì quá khứ"...',className:"mt-1.5 "+(v?"border-red-500 focus:ring-red-500":""),disabled:k,maxLength:255}),t("div",{className:"mt-1 flex justify-between",children:[e("p",v?{className:"text-sm text-red-500",children:v}:{className:"text-xs text-gray-500",children:"Mã và tiêu đề giúp tìm kiếm câu hỏi trong đề thi khác"}),t("span",{className:"text-xs text-gray-400",children:[N.length,"/",255]})]})]})]})]})})})}
|
|
@@ -1,9 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { Label } from '../../../components/ui/label';
|
|
3
|
-
import { Input } from '../../../components/ui/input';
|
|
4
|
-
import { Textarea } from '../../../components/ui/textarea';
|
|
5
|
-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../../components/ui/select';
|
|
6
|
-
import { QuestionTypeSelector } from './QuestionTypeSelector';
|
|
7
|
-
export function QuestionBasicInfoForm({ questionType, content, level, difficulty, skill, points, isEmbedded, prefilledType, onQuestionTypeChange, onContentChange, onLevelChange, onDifficultyChange, onSkillChange, onPointsChange, }) {
|
|
8
|
-
return (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: "questionType", children: "Lo\u1EA1i c\u00E2u h\u1ECFi *" }), _jsx(QuestionTypeSelector, { value: questionType, onValueChange: onQuestionTypeChange, disabled: isEmbedded && !!prefilledType })] }), questionType !== 'CHOOSE_THE_CORRECT_ANSWER' && (_jsxs("div", { children: [_jsx(Label, { htmlFor: "content", children: "N\u1ED9i dung c\u00E2u h\u1ECFi *" }), _jsx(Textarea, { id: "content", value: content, onChange: (e) => onContentChange(e.target.value), placeholder: "Nh\u1EADp n\u1ED9i dung c\u00E2u h\u1ECFi...", className: "mt-1", rows: 3 })] })), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: "level", children: "C\u1EA5p \u0111\u1ED9 *" }), _jsxs(Select, { value: level, onValueChange: (value) => onLevelChange(value), children: [_jsx(SelectTrigger, { id: "level", className: "mt-1", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "A1", children: "A1" }), _jsx(SelectItem, { value: "A2", children: "A2" }), _jsx(SelectItem, { value: "B1", children: "B1" }), _jsx(SelectItem, { value: "B2", children: "B2" }), _jsx(SelectItem, { value: "C1", children: "C1" }), _jsx(SelectItem, { value: "C2", children: "C2" })] })] })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "difficulty", children: "\u0110\u1ED9 kh\u00F3 *" }), _jsxs(Select, { value: difficulty, onValueChange: (value) => onDifficultyChange(value), children: [_jsx(SelectTrigger, { id: "difficulty", className: "mt-1", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "EASY", children: "D\u1EC5" }), _jsx(SelectItem, { value: "MEDIUM", children: "Trung b\u00ECnh" }), _jsx(SelectItem, { value: "HARD", children: "Kh\u00F3" })] })] })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "skill", children: "K\u1EF9 n\u0103ng *" }), _jsxs(Select, { value: skill, onValueChange: (value) => onSkillChange(value), children: [_jsx(SelectTrigger, { id: "skill", className: "mt-1", children: _jsx(SelectValue, {}) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "LISTENING", children: "Listening" }), _jsx(SelectItem, { value: "READING", children: "Reading" }), _jsx(SelectItem, { value: "WRITING", children: "Writing" }), _jsx(SelectItem, { value: "SPEAKING", children: "Speaking" })] })] })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "points", children: "\u0110i\u1EC3m *" }), _jsx(Input, { id: "points", type: "number", value: points, onChange: (e) => onPointsChange(Number(e.target.value)), className: "mt-1", min: 1 })] })] })] }));
|
|
9
|
-
}
|
|
1
|
+
import{jsx as e,jsxs as n}from"react/jsx-runtime";import{Label as i}from"../../../components/ui/label";import{Input as l}from"../../../components/ui/input";import{Textarea as r}from"../../../components/ui/textarea";import{Select as a,SelectContent as c,SelectItem as d,SelectTrigger as h,SelectValue as o}from"../../../components/ui/select";import{QuestionTypeSelector as t}from"./QuestionTypeSelector";export function QuestionBasicInfoForm({questionType:u,content:s,level:m,difficulty:v,skill:p,points:g,isEmbedded:C,prefilledType:f,onQuestionTypeChange:N,onContentChange:y,onLevelChange:E,onDifficultyChange:T,onSkillChange:A,onPointsChange:I}){return n("div",{className:"space-y-4",children:[n("div",{children:[e(i,{htmlFor:"questionType",children:"Loại câu hỏi *"}),e(t,{value:u,onValueChange:N,disabled:C&&!!f})]}),"CHOOSE_THE_CORRECT_ANSWER"!==u&&n("div",{children:[e(i,{htmlFor:"content",children:"Nội dung câu hỏi *"}),e(r,{id:"content",value:s,onChange:e=>y(e.target.value),placeholder:"Nhập nội dung câu hỏi...",className:"mt-1",rows:3})]}),n("div",{className:"grid grid-cols-2 gap-4",children:[n("div",{children:[e(i,{htmlFor:"level",children:"Cấp độ *"}),n(a,{value:m,onValueChange:e=>E(e),children:[e(h,{id:"level",className:"mt-1",children:e(o,{})}),n(c,{children:[e(d,{value:"A1",children:"A1"}),e(d,{value:"A2",children:"A2"}),e(d,{value:"B1",children:"B1"}),e(d,{value:"B2",children:"B2"}),e(d,{value:"C1",children:"C1"}),e(d,{value:"C2",children:"C2"})]})]})]}),n("div",{children:[e(i,{htmlFor:"difficulty",children:"Độ khó *"}),n(a,{value:v,onValueChange:e=>T(e),children:[e(h,{id:"difficulty",className:"mt-1",children:e(o,{})}),n(c,{children:[e(d,{value:"EASY",children:"Dễ"}),e(d,{value:"MEDIUM",children:"Trung bình"}),e(d,{value:"HARD",children:"Khó"})]})]})]}),n("div",{children:[e(i,{htmlFor:"skill",children:"Kỹ năng *"}),n(a,{value:p,onValueChange:e=>A(e),children:[e(h,{id:"skill",className:"mt-1",children:e(o,{})}),n(c,{children:[e(d,{value:"LISTENING",children:"Listening"}),e(d,{value:"READING",children:"Reading"}),e(d,{value:"WRITING",children:"Writing"}),e(d,{value:"SPEAKING",children:"Speaking"})]})]})]}),n("div",{children:[e(i,{htmlFor:"points",children:"Điểm *"}),e(l,{id:"points",type:"number",value:g,onChange:e=>I(Number(e.target.value)),className:"mt-1",min:1})]})]})]})}
|
|
@@ -1,33 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Label } from '../../../components/ui/label';
|
|
4
|
-
import { Textarea } from '../../../components/ui/textarea';
|
|
5
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../../components/ui/card';
|
|
6
|
-
import { PointsInput } from '../../../components/ui/points-input';
|
|
7
|
-
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../../components/ui/select';
|
|
8
|
-
/**
|
|
9
|
-
* Shared component for common question fields:
|
|
10
|
-
* - Points (điểm số)
|
|
11
|
-
* - Difficulty (độ khó)
|
|
12
|
-
* - Explanation (giải thích)
|
|
13
|
-
*
|
|
14
|
-
* Used by all question type creators to ensure consistency.
|
|
15
|
-
*/
|
|
16
|
-
export function QuestionCommonFields({ points, onPointsChange, difficulty = 'MEDIUM', onDifficultyChange, showDifficulty = true, explanation = '', onExplanationChange, showExplanation = true, layout = 'horizontal', }) {
|
|
17
|
-
const getDifficultyLabel = (diff) => {
|
|
18
|
-
switch (diff) {
|
|
19
|
-
case 'EASY':
|
|
20
|
-
return 'Dễ';
|
|
21
|
-
case 'MEDIUM':
|
|
22
|
-
return 'Trung bình';
|
|
23
|
-
case 'HARD':
|
|
24
|
-
return 'Khó';
|
|
25
|
-
default:
|
|
26
|
-
return diff;
|
|
27
|
-
}
|
|
28
|
-
};
|
|
29
|
-
return (_jsxs(Card, { children: [_jsx(CardHeader, { className: "pb-3", children: _jsx(CardTitle, { className: "text-base", children: "Th\u00F4ng tin c\u01A1 b\u1EA3n" }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { className: layout === 'horizontal' ? 'grid grid-cols-2 gap-4' : 'space-y-4', children: [_jsx("div", { children: _jsx(PointsInput, { defaultValue: points, onChange: (e) => {
|
|
30
|
-
const value = Number(e.target.value);
|
|
31
|
-
onPointsChange(value || 1);
|
|
32
|
-
} }) }), showDifficulty && onDifficultyChange && (_jsxs("div", { children: [_jsx(Label, { htmlFor: "difficulty", className: "text-sm font-medium", children: "\u0110\u1ED9 kh\u00F3" }), _jsxs(Select, { value: difficulty, onValueChange: (value) => onDifficultyChange(value), children: [_jsx(SelectTrigger, { id: "difficulty", className: "mt-1.5", children: _jsx(SelectValue, { placeholder: "Ch\u1ECDn \u0111\u1ED9 kh\u00F3", children: getDifficultyLabel(difficulty) }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "EASY", children: "D\u1EC5" }), _jsx(SelectItem, { value: "MEDIUM", children: "Trung b\u00ECnh" }), _jsx(SelectItem, { value: "HARD", children: "Kh\u00F3" })] })] })] }))] }), showExplanation && onExplanationChange && (_jsxs("div", { children: [_jsx(Label, { htmlFor: "explanation", className: "text-sm font-medium", children: "Gi\u1EA3i th\u00EDch" }), _jsx(Textarea, { id: "explanation", value: explanation, onChange: (e) => onExplanationChange(e.target.value), placeholder: "Nh\u1EADp gi\u1EA3i th\u00EDch cho \u0111\u00E1p \u00E1n \u0111\u00FAng...", className: "mt-1.5", rows: 3 }), _jsx("p", { className: "mt-1 text-xs text-gray-500", children: "Gi\u1EA3i th\u00EDch s\u1EBD \u0111\u01B0\u1EE3c hi\u1EC3n th\u1ECB cho h\u1ECDc sinh sau khi n\u1ED9p b\u00E0i" })] }))] })] }));
|
|
33
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as n}from"react/jsx-runtime";import{Label as i}from"../../../components/ui/label";import{Textarea as t}from"../../../components/ui/textarea";import{Card as a,CardContent as l,CardHeader as c,CardTitle as r}from"../../../components/ui/card";import{PointsInput as o}from"../../../components/ui/points-input";import{Select as h,SelectContent as s,SelectItem as m,SelectTrigger as u,SelectValue as d}from"../../../components/ui/select";export function QuestionCommonFields({points:p,onPointsChange:f,difficulty:g="MEDIUM",onDifficultyChange:x,showDifficulty:v=!0,explanation:N="",onExplanationChange:y,showExplanation:D=!0,layout:b="horizontal"}){return n(a,{children:[e(c,{className:"pb-3",children:e(r,{className:"text-base",children:"Thông tin cơ bản"})}),n(l,{className:"space-y-4",children:[n("div",{className:"horizontal"===b?"grid grid-cols-2 gap-4":"space-y-4",children:[e("div",{children:e(o,{defaultValue:p,onChange:e=>{const n=Number(e.target.value);f(n||1)}})}),v&&x&&n("div",{children:[e(i,{htmlFor:"difficulty",className:"text-sm font-medium",children:"Độ khó"}),n(h,{value:g,onValueChange:e=>x(e),children:[e(u,{id:"difficulty",className:"mt-1.5",children:e(d,{placeholder:"Chọn độ khó",children:(e=>{switch(e){case"EASY":return"Dễ";case"MEDIUM":return"Trung bình";case"HARD":return"Khó";default:return e}})(g)})}),n(s,{children:[e(m,{value:"EASY",children:"Dễ"}),e(m,{value:"MEDIUM",children:"Trung bình"}),e(m,{value:"HARD",children:"Khó"})]})]})]})]}),D&&y&&n("div",{children:[e(i,{htmlFor:"explanation",className:"text-sm font-medium",children:"Giải thích"}),e(t,{id:"explanation",value:N,onChange:e=>y(e.target.value),placeholder:"Nhập giải thích cho đáp án đúng...",className:"mt-1.5",rows:3}),e("p",{className:"mt-1 text-xs text-gray-500",children:"Giải thích sẽ được hiển thị cho học sinh sau khi nộp bài"})]})]})]})}
|
|
@@ -1,258 +1 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useMemo, useState } from 'react';
|
|
4
|
-
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '../../../components/ui/dropdown-menu';
|
|
5
|
-
import { Input } from '../../../components/ui/input';
|
|
6
|
-
import { Badge } from '../../../components/ui/badge';
|
|
7
|
-
import { Loader2, Search, Image as ImageIcon, FileText, Grid3X3, BookOpen, Library, Sparkles, Hash, BarChart3, Lightbulb, CheckCircle2, } from 'lucide-react';
|
|
8
|
-
import { useQuery } from '@tanstack/react-query';
|
|
9
|
-
import { ScrollArea } from '../../../components/ui/scroll-area';
|
|
10
|
-
import { cn } from '../../../shared/lib/utils';
|
|
11
|
-
const truncate = (text, maxLength) => {
|
|
12
|
-
if (!text)
|
|
13
|
-
return '';
|
|
14
|
-
// Convert to string if it's not already
|
|
15
|
-
const textStr = typeof text === 'string' ? text : String(text);
|
|
16
|
-
if (textStr.length <= maxLength)
|
|
17
|
-
return textStr;
|
|
18
|
-
return `${textStr.substring(0, maxLength)}...`;
|
|
19
|
-
};
|
|
20
|
-
// Image-based question types
|
|
21
|
-
const IMAGE_BASED_TYPES = [
|
|
22
|
-
'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER',
|
|
23
|
-
'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER',
|
|
24
|
-
'LOOK_PICTURE_FILL_WORD_HINT',
|
|
25
|
-
'FILL_IN_BLANK',
|
|
26
|
-
'LABEL_THE_PICTURE',
|
|
27
|
-
'READ_AND_COLOR_OBJECTS',
|
|
28
|
-
'IMAGE_OBJECT_MATCHING',
|
|
29
|
-
'FILL_IN_BLANK',
|
|
30
|
-
];
|
|
31
|
-
// Passage-based question types
|
|
32
|
-
const PASSAGE_BASED_TYPES = [
|
|
33
|
-
'READ_PASSAGE_AND_ANSWER_QUESTIONS',
|
|
34
|
-
'READ_PASSAGE_AND_COMPLETE_STORY',
|
|
35
|
-
];
|
|
36
|
-
// Grid-based question types
|
|
37
|
-
const GRID_BASED_TYPES = ['FILL_MISSING_WORDS_IN_GRID'];
|
|
38
|
-
// Get display icon for question type
|
|
39
|
-
const getQuestionTypeIcon = (type) => {
|
|
40
|
-
if (IMAGE_BASED_TYPES.includes(type)) {
|
|
41
|
-
return _jsx(ImageIcon, { className: "h-4 w-4 text-blue-500" });
|
|
42
|
-
}
|
|
43
|
-
if (PASSAGE_BASED_TYPES.includes(type)) {
|
|
44
|
-
return _jsx(BookOpen, { className: "h-4 w-4 text-green-500" });
|
|
45
|
-
}
|
|
46
|
-
if (GRID_BASED_TYPES.includes(type)) {
|
|
47
|
-
return _jsx(Grid3X3, { className: "h-4 w-4 text-purple-500" });
|
|
48
|
-
}
|
|
49
|
-
return _jsx(FileText, { className: "h-4 w-4 text-gray-500" });
|
|
50
|
-
};
|
|
51
|
-
// Get question type display name in Vietnamese
|
|
52
|
-
const getQuestionTypeName = (type) => {
|
|
53
|
-
const typeMap = {
|
|
54
|
-
'LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER': 'Nhìn tranh điền chỗ trống',
|
|
55
|
-
'LOOK_PICTURE_CHOOSE_CORRECT_ANSWER': 'Nhìn tranh chọn đáp án',
|
|
56
|
-
'LOOK_PICTURE_FILL_WORD_HINT': 'Nhìn tranh điền từ gợi ý',
|
|
57
|
-
'FILL_MISSING_WORDS_IN_GRID': 'Điền chữ trong bảng',
|
|
58
|
-
'CHOOSE_THE_CORRECT_ANSWER': 'Chọn đáp án đúng',
|
|
59
|
-
'LABEL_THE_PICTURE': 'Gắn nhãn hình ảnh',
|
|
60
|
-
'READ_AND_COLOR_OBJECTS': 'Đọc và tô màu',
|
|
61
|
-
'IMAGE_OBJECT_MATCHING': 'Ghép hình ảnh - Đối tượng',
|
|
62
|
-
'WRITE_CORRECT_VERB_FORM': 'Viết dạng động từ',
|
|
63
|
-
'FILL_IN_BLANK': 'Điền vào chỗ trống',
|
|
64
|
-
'READ_PASSAGE_AND_ANSWER_QUESTIONS': 'Đọc hiểu',
|
|
65
|
-
};
|
|
66
|
-
return type ? typeMap[type] || type.replace(/_/g, ' ').toLowerCase() : 'Chưa xác định';
|
|
67
|
-
};
|
|
68
|
-
// Enhanced function to extract display text from different question content structures
|
|
69
|
-
export const getQuestionDisplayText = (question) => {
|
|
70
|
-
return getContentDisplayText(question.content, question.type);
|
|
71
|
-
};
|
|
72
|
-
// Extract display text from content object (for cases where we have content but not full Question)
|
|
73
|
-
export const getContentDisplayText = (content, type) => {
|
|
74
|
-
if (!content)
|
|
75
|
-
return type || 'Câu hỏi';
|
|
76
|
-
// Handle string content directly
|
|
77
|
-
if (typeof content === 'string') {
|
|
78
|
-
return content;
|
|
79
|
-
}
|
|
80
|
-
// Handle object content based on question type
|
|
81
|
-
if (typeof content === 'object') {
|
|
82
|
-
// LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER, LOOK_PICTURE_CHOOSE_CORRECT_ANSWER
|
|
83
|
-
if (content.subQuestions && Array.isArray(content.subQuestions)) {
|
|
84
|
-
const firstSubQ = content.subQuestions[0];
|
|
85
|
-
if (firstSubQ?.question) {
|
|
86
|
-
return firstSubQ.question;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
// For content with direct question field (single question format)
|
|
90
|
-
if (content.question) {
|
|
91
|
-
return content.question;
|
|
92
|
-
}
|
|
93
|
-
// LOOK_PICTURE_FILL_WORD_HINT - show word pattern
|
|
94
|
-
if (content.word) {
|
|
95
|
-
return `Điền chữ: ${content.word}`;
|
|
96
|
-
}
|
|
97
|
-
// READ_PASSAGE_AND_ANSWER_QUESTIONS
|
|
98
|
-
if (content.passage) {
|
|
99
|
-
return content.passageTitle || truncate(content.passage, 60);
|
|
100
|
-
}
|
|
101
|
-
// FILL_MISSING_WORDS_IN_GRID
|
|
102
|
-
if (content.grid && content.rows && content.columns) {
|
|
103
|
-
return `Lưới ${content.rows}x${content.columns}`;
|
|
104
|
-
}
|
|
105
|
-
// LABEL_THE_PICTURE
|
|
106
|
-
if (content.wordBank) {
|
|
107
|
-
const words = content.wordBank.map((w) => w.text || w).slice(0, 3).join(', ');
|
|
108
|
-
return `Từ vựng: ${words}${content.wordBank.length > 3 ? '...' : ''}`;
|
|
109
|
-
}
|
|
110
|
-
// READ_AND_COLOR_OBJECTS / IMAGE_OBJECT_MATCHING
|
|
111
|
-
if (content.colorNodes || content.regionNodes) {
|
|
112
|
-
const regionCount = content.regionNodes?.length || 0;
|
|
113
|
-
return `${regionCount} vùng tô màu`;
|
|
114
|
-
}
|
|
115
|
-
// WRITE_A_SHORT_LETTER
|
|
116
|
-
if (content.contexts) {
|
|
117
|
-
const firstContext = content.contexts[0];
|
|
118
|
-
return firstContext?.text ? truncate(firstContext.text, 50) : 'Viết thư ngắn';
|
|
119
|
-
}
|
|
120
|
-
// CHOOSE_THE_CORRECT_ANSWER
|
|
121
|
-
if (content.options && Array.isArray(content.options)) {
|
|
122
|
-
return content.questionText || content.question || 'Câu hỏi trắc nghiệm';
|
|
123
|
-
}
|
|
124
|
-
// FILL_IN_BLANK
|
|
125
|
-
if (content.blanks) {
|
|
126
|
-
const firstBlank = content.blanks[0];
|
|
127
|
-
return firstBlank?.text || content.sentence || 'Điền vào chỗ trống';
|
|
128
|
-
}
|
|
129
|
-
// Fallback: try common field names
|
|
130
|
-
if (content.instruction)
|
|
131
|
-
return content.instruction;
|
|
132
|
-
if (content.mainTitle)
|
|
133
|
-
return content.mainTitle;
|
|
134
|
-
if (content.title)
|
|
135
|
-
return content.title;
|
|
136
|
-
if (content.text)
|
|
137
|
-
return content.text;
|
|
138
|
-
}
|
|
139
|
-
return type || 'Câu hỏi';
|
|
140
|
-
};
|
|
141
|
-
// Get difficulty badge color
|
|
142
|
-
const getDifficultyBadgeClass = (difficulty) => {
|
|
143
|
-
switch (difficulty?.toUpperCase()) {
|
|
144
|
-
case 'EASY':
|
|
145
|
-
return 'bg-green-100 text-green-700 border-green-200';
|
|
146
|
-
case 'HARD':
|
|
147
|
-
return 'bg-red-100 text-red-700 border-red-200';
|
|
148
|
-
default:
|
|
149
|
-
return 'bg-yellow-100 text-yellow-700 border-yellow-200';
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
// Get difficulty display name
|
|
153
|
-
const getDifficultyName = (difficulty) => {
|
|
154
|
-
switch (difficulty?.toUpperCase()) {
|
|
155
|
-
case 'EASY':
|
|
156
|
-
return 'Dễ';
|
|
157
|
-
case 'HARD':
|
|
158
|
-
return 'Khó';
|
|
159
|
-
case 'MEDIUM':
|
|
160
|
-
return 'TB';
|
|
161
|
-
default:
|
|
162
|
-
return 'TB';
|
|
163
|
-
}
|
|
164
|
-
};
|
|
165
|
-
// Get subquestion count if applicable
|
|
166
|
-
const getSubQuestionCount = (question) => {
|
|
167
|
-
const content = question.content;
|
|
168
|
-
if (!content)
|
|
169
|
-
return null;
|
|
170
|
-
if (content.subQuestions && Array.isArray(content.subQuestions)) {
|
|
171
|
-
return content.subQuestions.length;
|
|
172
|
-
}
|
|
173
|
-
return null;
|
|
174
|
-
};
|
|
175
|
-
export function QuestionSearchDropdown({ questionType, onSelect, selectedQuestionLabel, placeholder = 'Tìm câu hỏi từ ngân hàng', disabled = false, buttonClassName, compact = false, searchQuestions, }) {
|
|
176
|
-
const [open, setOpen] = useState(false);
|
|
177
|
-
const [searchTerm, setSearchTerm] = useState('');
|
|
178
|
-
const [debouncedTerm, setDebouncedTerm] = useState('');
|
|
179
|
-
useEffect(() => {
|
|
180
|
-
const handler = setTimeout(() => {
|
|
181
|
-
setDebouncedTerm(searchTerm.trim());
|
|
182
|
-
}, 300);
|
|
183
|
-
return () => clearTimeout(handler);
|
|
184
|
-
}, [searchTerm]);
|
|
185
|
-
// Transform QuestionBankItem to Question format with extended info
|
|
186
|
-
const transformToQuestion = (item) => ({
|
|
187
|
-
id: item.id,
|
|
188
|
-
type: item.questionType,
|
|
189
|
-
content: item.content,
|
|
190
|
-
answer: item.correctAnswer,
|
|
191
|
-
points: parseFloat(String(item.points ?? 1)) || 1,
|
|
192
|
-
difficulty: item.difficulty?.toUpperCase(),
|
|
193
|
-
skill: (item.skill || undefined),
|
|
194
|
-
explanation: item.explanation || undefined,
|
|
195
|
-
level: 'A1', // Default value
|
|
196
|
-
createdAt: item.createdAt,
|
|
197
|
-
updatedAt: item.updatedAt,
|
|
198
|
-
// Extended info from question bank
|
|
199
|
-
code: item.code,
|
|
200
|
-
title: item.title || undefined,
|
|
201
|
-
usageCount: item.usageCount,
|
|
202
|
-
});
|
|
203
|
-
// Host app may provide dynamic search directly via component props.
|
|
204
|
-
const { data: questionBankData, isFetching } = useQuery({
|
|
205
|
-
queryKey: ['questionBanks', 'search', questionType, debouncedTerm],
|
|
206
|
-
queryFn: async () => {
|
|
207
|
-
if (!searchQuestions)
|
|
208
|
-
return { data: [], meta: { totalItems: 0 } };
|
|
209
|
-
return searchQuestions({
|
|
210
|
-
questionType,
|
|
211
|
-
keyword: debouncedTerm || undefined,
|
|
212
|
-
page: 1,
|
|
213
|
-
size: 10,
|
|
214
|
-
});
|
|
215
|
-
},
|
|
216
|
-
enabled: open && Boolean(questionType) && Boolean(searchQuestions) && (debouncedTerm.length > 0 || !!questionType),
|
|
217
|
-
staleTime: 60 * 1000,
|
|
218
|
-
refetchOnWindowFocus: false,
|
|
219
|
-
});
|
|
220
|
-
// Transform API response to Question array
|
|
221
|
-
const questions = useMemo(() => {
|
|
222
|
-
if (!questionBankData?.data)
|
|
223
|
-
return [];
|
|
224
|
-
return questionBankData.data.map(transformToQuestion);
|
|
225
|
-
}, [questionBankData]);
|
|
226
|
-
const totalItems = questionBankData?.meta?.totalItems || 0;
|
|
227
|
-
const hasNoResults = !isFetching && questions.length === 0 && debouncedTerm.length > 0;
|
|
228
|
-
// Show empty bank message when: API returned (questionBankData exists), no questions, and no search term
|
|
229
|
-
const hasEmptyBank = !isFetching && questionBankData && questions.length === 0 && debouncedTerm.length === 0;
|
|
230
|
-
// Only show initial tips when: not fetching, no API response yet (first open before any query)
|
|
231
|
-
const showInitialMessage = !isFetching && !questionBankData && questions.length === 0 && debouncedTerm.length === 0;
|
|
232
|
-
const buttonLabel = useMemo(() => {
|
|
233
|
-
if (selectedQuestionLabel) {
|
|
234
|
-
return truncate(selectedQuestionLabel, 40);
|
|
235
|
-
}
|
|
236
|
-
return placeholder;
|
|
237
|
-
}, [placeholder, selectedQuestionLabel]);
|
|
238
|
-
const handleSelect = (question) => {
|
|
239
|
-
onSelect(question);
|
|
240
|
-
setOpen(false);
|
|
241
|
-
setSearchTerm('');
|
|
242
|
-
setDebouncedTerm('');
|
|
243
|
-
};
|
|
244
|
-
return (_jsxs(DropdownMenu, { open: open, onOpenChange: (value) => setOpen(!disabled && value), children: [_jsx(DropdownMenuTrigger, { asChild: true, disabled: disabled, children: compact ? (_jsxs("button", { type: "button", className: cn('group inline-flex h-8 items-center gap-1.5 rounded-md border px-3 text-sm font-medium transition-all', selectedQuestionLabel
|
|
245
|
-
? 'border-emerald-200 bg-emerald-50 text-emerald-700 hover:bg-emerald-100'
|
|
246
|
-
: 'border-indigo-200 text-indigo-600 hover:bg-indigo-50 hover:text-indigo-700', 'focus:outline-none focus:ring-2 focus:ring-indigo-200 focus:ring-offset-1', disabled && 'cursor-not-allowed opacity-50', buttonClassName), children: [selectedQuestionLabel ? (_jsx(CheckCircle2, { className: "h-4 w-4" })) : (_jsx(Library, { className: "h-4 w-4" })), selectedQuestionLabel ? 'Đã chọn' : 'Ngân hàng'] })) : (_jsxs("button", { type: "button", className: cn('group relative flex w-full items-center gap-3 rounded-xl border bg-white p-3 text-left shadow-sm transition-all', selectedQuestionLabel
|
|
247
|
-
? 'border-emerald-200 bg-gradient-to-r from-emerald-50/50 to-teal-50/50'
|
|
248
|
-
: 'border-gray-200 hover:border-indigo-200 hover:bg-gradient-to-r hover:from-indigo-50/30 hover:to-purple-50/30', 'focus:outline-none focus:ring-2 focus:ring-indigo-200 focus:ring-offset-1', disabled && 'cursor-not-allowed opacity-50', buttonClassName), children: [_jsx("div", { className: cn('flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg transition-all', selectedQuestionLabel
|
|
249
|
-
? 'bg-gradient-to-br from-emerald-500 to-teal-600 shadow-sm'
|
|
250
|
-
: 'bg-gradient-to-br from-indigo-100 to-purple-100 group-hover:from-indigo-500 group-hover:to-purple-600 group-hover:shadow-sm'), children: selectedQuestionLabel ? (_jsx(CheckCircle2, { className: "h-5 w-5 text-white" })) : (_jsx(Library, { className: cn('h-5 w-5 transition-colors', 'text-indigo-500 group-hover:text-white') })) }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("div", { className: "flex items-center gap-2", children: _jsx("span", { className: cn('text-xs font-medium uppercase tracking-wide', selectedQuestionLabel ? 'text-emerald-600' : 'text-indigo-500'), children: selectedQuestionLabel ? '✓ Đã chọn từ ngân hàng' : 'Ngân hàng câu hỏi' }) }), _jsx("p", { className: cn('mt-0.5 truncate text-sm', selectedQuestionLabel ? 'font-medium text-gray-800' : 'text-gray-500'), children: selectedQuestionLabel
|
|
251
|
-
? truncate(selectedQuestionLabel, 55)
|
|
252
|
-
: 'Nhấn để tìm và sử dụng câu hỏi có sẵn' })] }), _jsx("div", { className: cn('flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full transition-all', selectedQuestionLabel
|
|
253
|
-
? 'bg-emerald-100 text-emerald-600'
|
|
254
|
-
: 'bg-gray-100 text-gray-400 group-hover:bg-indigo-100 group-hover:text-indigo-600'), children: _jsx(Search, { className: "h-4 w-4" }) })] })) }), _jsxs(DropdownMenuContent, { className: "w-[520px] p-0", align: compact ? "end" : "start", children: [_jsx("div", { className: "border-b border-gray-100 bg-gradient-to-r from-indigo-50 to-purple-50 px-4 py-3", 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-indigo-500 to-purple-600 shadow-sm", children: _jsx(Library, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { className: "flex-1", children: [_jsx("h3", { className: "text-sm font-semibold text-gray-800", children: "Ng\u00E2n h\u00E0ng c\u00E2u h\u1ECFi" }), _jsx("p", { className: "text-xs text-gray-500", children: "Ch\u1ECDn c\u00E2u h\u1ECFi c\u00F3 s\u1EB5n \u0111\u1EC3 \u0111i\u1EC1n nhanh v\u00E0o form" })] })] }) }), _jsxs("div", { className: "border-b border-gray-100 bg-gray-50/50 p-3", children: [_jsxs("div", { className: "relative", children: [_jsx(Search, { className: "absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400" }), _jsx(Input, { placeholder: "T\u00ECm theo m\u00E3, ti\u00EAu \u0111\u1EC1 ho\u1EB7c n\u1ED9i dung...", value: searchTerm, onChange: (event) => setSearchTerm(event.target.value), autoFocus: true, className: "border-gray-200 bg-white pl-10 pr-10 transition-all focus:border-indigo-300 focus:ring-indigo-200" }), isFetching && (_jsx(Loader2, { className: "absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 animate-spin text-indigo-500" }))] }), _jsxs("div", { className: "mt-2 flex items-center justify-between", children: [_jsx("div", { className: "flex items-center gap-2", children: _jsx(Badge, { variant: "outline", className: "border-indigo-200 bg-indigo-50 text-indigo-700", children: getQuestionTypeName(questionType) }) }), totalItems > 0 && (_jsxs("span", { className: "text-xs text-gray-500", children: ["Hi\u1EC3n th\u1ECB ", questions.length, "/", totalItems, " k\u1EBFt qu\u1EA3"] }))] })] }), _jsx(ScrollArea, { className: "h-80", children: showInitialMessage ? (_jsxs("div", { className: "flex flex-col items-center justify-center p-8 text-center", children: [_jsx("div", { className: "mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-gradient-to-br from-indigo-100 to-purple-100", children: _jsx(Sparkles, { className: "h-8 w-8 text-indigo-500" }) }), _jsx("p", { className: "text-sm font-medium text-gray-700", children: "T\u00ECm c\u00E2u h\u1ECFi t\u1EEB ng\u00E2n h\u00E0ng" }), _jsx("p", { className: "mt-1 text-xs text-gray-500", children: "Nh\u1EADp m\u00E3 c\u00E2u h\u1ECFi, ti\u00EAu \u0111\u1EC1 ho\u1EB7c t\u1EEB kh\u00F3a \u0111\u1EC3 t\u00ECm ki\u1EBFm" }), _jsx("div", { className: "mt-4 rounded-lg border border-blue-200 bg-blue-50 p-3", children: _jsxs("div", { className: "flex items-start gap-2", children: [_jsx(Lightbulb, { className: "mt-0.5 h-4 w-4 flex-shrink-0 text-blue-500" }), _jsxs("div", { className: "text-left text-xs text-blue-700", children: [_jsx("p", { className: "font-medium", children: "M\u1EB9o t\u00ECm ki\u1EBFm nhanh:" }), _jsxs("ul", { className: "mt-1 list-inside list-disc space-y-0.5 text-blue-600", children: [_jsx("li", { children: "Nh\u1EADp m\u00E3 c\u00E2u h\u1ECFi (VD: M001)" }), _jsx("li", { children: "Nh\u1EADp ti\u00EAu \u0111\u1EC1 ho\u1EB7c m\u1ED9t ph\u1EA7n n\u1ED9i dung" }), _jsx("li", { children: "K\u1EBFt qu\u1EA3 s\u1EBD t\u1EF1 \u0111\u1ED9ng c\u1EADp nh\u1EADt" })] })] })] }) })] })) : hasEmptyBank ? (_jsxs("div", { className: "flex flex-col items-center justify-center p-8 text-center", children: [_jsx("div", { className: "mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-amber-100", children: _jsx(Library, { className: "h-8 w-8 text-amber-500" }) }), _jsx("p", { className: "text-sm font-medium text-gray-700", children: "Ch\u01B0a c\u00F3 c\u00E2u h\u1ECFi trong ng\u00E2n h\u00E0ng" }), _jsxs("p", { className: "mt-1 text-xs text-gray-500", children: ["Ng\u00E2n h\u00E0ng c\u00E2u h\u1ECFi ch\u01B0a c\u00F3 d\u1EEF li\u1EC7u cho lo\u1EA1i: ", _jsx("strong", { children: getQuestionTypeName(questionType) })] }), _jsx("p", { className: "mt-2 text-xs text-gray-400", children: "H\u00E3y th\u00EAm c\u00E2u h\u1ECFi v\u00E0o ng\u00E2n h\u00E0ng ho\u1EB7c t\u1EA1o c\u00E2u h\u1ECFi m\u1EDBi" })] })) : hasNoResults ? (_jsxs("div", { className: "flex flex-col items-center justify-center p-8 text-center", children: [_jsx("div", { className: "mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-gray-100", children: _jsx(Search, { className: "h-8 w-8 text-gray-400" }) }), _jsx("p", { className: "text-sm font-medium text-gray-700", children: "Kh\u00F4ng t\u00ECm th\u1EA5y k\u1EBFt qu\u1EA3" }), _jsxs("p", { className: "mt-1 text-xs text-gray-500", children: ["Th\u1EED t\u00ECm v\u1EDBi t\u1EEB kh\u00F3a kh\u00E1c: \"", debouncedTerm, "\""] })] })) : (_jsx("div", { className: "divide-y divide-gray-100 p-2", children: questions.map((question) => {
|
|
255
|
-
const subQuestionCount = getSubQuestionCount(question);
|
|
256
|
-
return (_jsxs(DropdownMenuItem, { className: "flex cursor-pointer flex-col items-start gap-2 rounded-xl p-3 transition-all hover:bg-gradient-to-r hover:from-indigo-50 hover:to-purple-50 focus:bg-gradient-to-r focus:from-indigo-50 focus:to-purple-50", onSelect: () => handleSelect(question), children: [_jsxs("div", { className: "flex w-full items-start justify-between gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [question.code && (_jsxs("div", { className: "flex items-center gap-1 rounded-md bg-gradient-to-r from-indigo-500 to-purple-500 px-2 py-0.5 text-xs font-bold text-white shadow-sm", children: [_jsx(Hash, { className: "h-3 w-3" }), question.code] })), _jsx("div", { className: "flex-shrink-0", children: getQuestionTypeIcon(question.type) })] }), question.usageCount !== undefined && question.usageCount > 0 && (_jsxs("div", { className: "flex items-center gap-1 text-xs text-gray-500", title: "S\u1ED1 l\u1EA7n s\u1EED d\u1EE5ng", children: [_jsx(BarChart3, { className: "h-3 w-3" }), question.usageCount] }))] }), question.title && (_jsx("p", { className: "text-sm font-semibold text-gray-800", children: truncate(question.title, 60) })), _jsx("p", { className: "text-xs text-gray-600", children: truncate(getQuestionDisplayText(question), 80) }), _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [_jsx(Badge, { variant: "outline", className: cn('px-2 py-0.5 text-[10px] font-semibold', getDifficultyBadgeClass(question.difficulty)), children: getDifficultyName(question.difficulty) }), _jsxs(Badge, { variant: "outline", className: "border-blue-200 bg-blue-50 px-2 py-0.5 text-[10px] font-semibold text-blue-700", children: [question.points || 1, " \u0111i\u1EC3m"] }), question.skill && (_jsx(Badge, { variant: "outline", className: "border-gray-200 bg-gray-50 px-2 py-0.5 text-[10px] font-semibold text-gray-600", children: question.skill.toLowerCase() })), subQuestionCount && subQuestionCount > 1 && (_jsxs(Badge, { variant: "outline", className: "border-purple-200 bg-purple-50 px-2 py-0.5 text-[10px] font-semibold text-purple-700", children: [subQuestionCount, " c\u00E2u con"] }))] })] }, question.id));
|
|
257
|
-
}) })) }), _jsx("div", { className: "border-t border-gray-100 bg-gray-50/50 px-4 py-2", children: _jsx("p", { className: "text-center text-xs text-gray-500", children: "Click \u0111\u1EC3 ch\u1ECDn c\u00E2u h\u1ECFi v\u00E0 t\u1EF1 \u0111\u1ED9ng \u0111i\u1EC1n v\u00E0o form" }) })] })] }));
|
|
258
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useEffect as n,useMemo as r,useState as i}from"react";import{DropdownMenu as s,DropdownMenuContent as a,DropdownMenuItem as o,DropdownMenuTrigger as l}from"../../../components/ui/dropdown-menu";import{Input as c}from"../../../components/ui/input";import{Badge as d}from"../../../components/ui/badge";import{Loader2 as m,Search as u,Image as h,FileText as g,Grid3X3 as p,BookOpen as x,Library as f,Sparkles as N,Hash as b,BarChart3 as y,Lightbulb as v,CheckCircle2 as _}from"lucide-react";import{useQuery as w}from"@tanstack/react-query";import{ScrollArea as C}from"../../../components/ui/scroll-area";import{cn as E}from"../../../shared/lib/utils";const A=(e,t)=>{if(!e)return"";const n="string"==typeof e?e:String(e);return n.length<=t?n:`${n.substring(0,t)}...`},T=["LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER","LOOK_PICTURE_CHOOSE_CORRECT_ANSWER","LOOK_PICTURE_FILL_WORD_HINT","FILL_IN_BLANK","LABEL_THE_PICTURE","READ_AND_COLOR_OBJECTS","IMAGE_OBJECT_MATCHING","FILL_IN_BLANK"],O=["READ_PASSAGE_AND_ANSWER_QUESTIONS","READ_PASSAGE_AND_COMPLETE_STORY"],R=["FILL_MISSING_WORDS_IN_GRID"],I=e=>e?{LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER:"Nhìn tranh điền chỗ trống",LOOK_PICTURE_CHOOSE_CORRECT_ANSWER:"Nhìn tranh chọn đáp án",LOOK_PICTURE_FILL_WORD_HINT:"Nhìn tranh điền từ gợi ý",FILL_MISSING_WORDS_IN_GRID:"Điền chữ trong bảng",CHOOSE_THE_CORRECT_ANSWER:"Chọn đáp án đúng",LABEL_THE_PICTURE:"Gắn nhãn hình ảnh",READ_AND_COLOR_OBJECTS:"Đọc và tô màu",IMAGE_OBJECT_MATCHING:"Ghép hình ảnh - Đối tượng",WRITE_CORRECT_VERB_FORM:"Viết dạng động từ",FILL_IN_BLANK:"Điền vào chỗ trống",READ_PASSAGE_AND_ANSWER_QUESTIONS:"Đọc hiểu"}[e]||e.replace(/_/g," ").toLowerCase():"Chưa xác định";export const getQuestionDisplayText=e=>getContentDisplayText(e.content,e.type);export const getContentDisplayText=(e,t)=>{if(!e)return t||"Câu hỏi";if("string"==typeof e)return e;if("object"==typeof e){if(e.subQuestions&&Array.isArray(e.subQuestions)){const t=e.subQuestions[0];if(t?.question)return t.question}if(e.question)return e.question;if(e.word)return`Điền chữ: ${e.word}`;if(e.passage)return e.passageTitle||A(e.passage,60);if(e.grid&&e.rows&&e.columns)return`Lưới ${e.rows}x${e.columns}`;if(e.wordBank){return`Từ vựng: ${e.wordBank.map(e=>e.text||e).slice(0,3).join(", ")}${e.wordBank.length>3?"...":""}`}if(e.colorNodes||e.regionNodes){return`${e.regionNodes?.length||0} vùng tô màu`}if(e.contexts){const t=e.contexts[0];return t?.text?A(t.text,50):"Viết thư ngắn"}if(e.options&&Array.isArray(e.options))return e.questionText||e.question||"Câu hỏi trắc nghiệm";if(e.blanks){const t=e.blanks[0];return t?.text||e.sentence||"Điền vào chỗ trống"}if(e.instruction)return e.instruction;if(e.mainTitle)return e.mainTitle;if(e.title)return e.title;if(e.text)return e.text}return t||"Câu hỏi"};const L=e=>{switch(e?.toUpperCase()){case"EASY":return"bg-green-100 text-green-700 border-green-200";case"HARD":return"bg-red-100 text-red-700 border-red-200";default:return"bg-yellow-100 text-yellow-700 border-yellow-200"}},S=e=>{switch(e?.toUpperCase()){case"EASY":return"Dễ";case"HARD":return"Khó";default:return"TB"}};export function QuestionSearchDropdown({questionType:k,onSelect:D,selectedQuestionLabel:B,placeholder:j="Tìm câu hỏi từ ngân hàng",disabled:q=!1,buttonClassName:H,compact:F=!1,searchQuestions:K}){const[G,Q]=i(!1),[U,W]=i(""),[P,M]=i("");n(()=>{const e=setTimeout(()=>{M(U.trim())},300);return()=>clearTimeout(e)},[U]);const $=e=>({id:e.id,type:e.questionType,content:e.content,answer:e.correctAnswer,points:parseFloat(String(e.points??1))||1,difficulty:e.difficulty?.toUpperCase(),skill:e.skill||void 0,explanation:e.explanation||void 0,level:"A1",createdAt:e.createdAt,updatedAt:e.updatedAt,code:e.code,title:e.title||void 0,usageCount:e.usageCount}),{data:J,isFetching:V}=w({queryKey:["questionBanks","search",k,P],queryFn:async()=>K?K({questionType:k,keyword:P||void 0,page:1,size:10}):{data:[],meta:{totalItems:0}},enabled:G&&Boolean(k)&&Boolean(K)&&(P.length>0||!!k),staleTime:6e4,refetchOnWindowFocus:!1}),Y=r(()=>J?.data?J.data.map($):[],[J]),z=J?.meta?.totalItems||0,X=!V&&0===Y.length&&P.length>0,Z=!V&&J&&0===Y.length&&0===P.length,ee=!V&&!J&&0===Y.length&&0===P.length;r(()=>B?A(B,40):j,[j,B]);return t(s,{open:G,onOpenChange:e=>Q(!q&&e),children:[e(l,{asChild:!0,disabled:q,children:t("button",F?{type:"button",className:E("group inline-flex h-8 items-center gap-1.5 rounded-md border px-3 text-sm font-medium transition-all",B?"border-emerald-200 bg-emerald-50 text-emerald-700 hover:bg-emerald-100":"border-indigo-200 text-indigo-600 hover:bg-indigo-50 hover:text-indigo-700","focus:outline-none focus:ring-2 focus:ring-indigo-200 focus:ring-offset-1",q&&"cursor-not-allowed opacity-50",H),children:[e(B?_:f,{className:"h-4 w-4"}),B?"Đã chọn":"Ngân hàng"]}:{type:"button",className:E("group relative flex w-full items-center gap-3 rounded-xl border bg-white p-3 text-left shadow-sm transition-all",B?"border-emerald-200 bg-gradient-to-r from-emerald-50/50 to-teal-50/50":"border-gray-200 hover:border-indigo-200 hover:bg-gradient-to-r hover:from-indigo-50/30 hover:to-purple-50/30","focus:outline-none focus:ring-2 focus:ring-indigo-200 focus:ring-offset-1",q&&"cursor-not-allowed opacity-50",H),children:[e("div",{className:E("flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg transition-all",B?"bg-gradient-to-br from-emerald-500 to-teal-600 shadow-sm":"bg-gradient-to-br from-indigo-100 to-purple-100 group-hover:from-indigo-500 group-hover:to-purple-600 group-hover:shadow-sm"),children:B?e(_,{className:"h-5 w-5 text-white"}):e(f,{className:E("h-5 w-5 transition-colors","text-indigo-500 group-hover:text-white")})}),t("div",{className:"min-w-0 flex-1",children:[e("div",{className:"flex items-center gap-2",children:e("span",{className:E("text-xs font-medium uppercase tracking-wide",B?"text-emerald-600":"text-indigo-500"),children:B?"✓ Đã chọn từ ngân hàng":"Ngân hàng câu hỏi"})}),e("p",{className:E("mt-0.5 truncate text-sm",B?"font-medium text-gray-800":"text-gray-500"),children:B?A(B,55):"Nhấn để tìm và sử dụng câu hỏi có sẵn"})]}),e("div",{className:E("flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full transition-all",B?"bg-emerald-100 text-emerald-600":"bg-gray-100 text-gray-400 group-hover:bg-indigo-100 group-hover:text-indigo-600"),children:e(u,{className:"h-4 w-4"})})]})}),t(a,{className:"w-[520px] p-0",align:F?"end":"start",children:[e("div",{className:"border-b border-gray-100 bg-gradient-to-r from-indigo-50 to-purple-50 px-4 py-3",children:t("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-indigo-500 to-purple-600 shadow-sm",children:e(f,{className:"h-4 w-4 text-white"})}),t("div",{className:"flex-1",children:[e("h3",{className:"text-sm font-semibold text-gray-800",children:"Ngân hàng câu hỏi"}),e("p",{className:"text-xs text-gray-500",children:"Chọn câu hỏi có sẵn để điền nhanh vào form"})]})]})}),t("div",{className:"border-b border-gray-100 bg-gray-50/50 p-3",children:[t("div",{className:"relative",children:[e(u,{className:"absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-gray-400"}),e(c,{placeholder:"Tìm theo mã, tiêu đề hoặc nội dung...",value:U,onChange:e=>W(e.target.value),autoFocus:!0,className:"border-gray-200 bg-white pl-10 pr-10 transition-all focus:border-indigo-300 focus:ring-indigo-200"}),V&&e(m,{className:"absolute right-3 top-1/2 h-4 w-4 -translate-y-1/2 animate-spin text-indigo-500"})]}),t("div",{className:"mt-2 flex items-center justify-between",children:[e("div",{className:"flex items-center gap-2",children:e(d,{variant:"outline",className:"border-indigo-200 bg-indigo-50 text-indigo-700",children:I(k)})}),z>0&&t("span",{className:"text-xs text-gray-500",children:["Hiển thị ",Y.length,"/",z," kết quả"]})]})]}),e(C,{className:"h-80",children:ee?t("div",{className:"flex flex-col items-center justify-center p-8 text-center",children:[e("div",{className:"mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-gradient-to-br from-indigo-100 to-purple-100",children:e(N,{className:"h-8 w-8 text-indigo-500"})}),e("p",{className:"text-sm font-medium text-gray-700",children:"Tìm câu hỏi từ ngân hàng"}),e("p",{className:"mt-1 text-xs text-gray-500",children:"Nhập mã câu hỏi, tiêu đề hoặc từ khóa để tìm kiếm"}),e("div",{className:"mt-4 rounded-lg border border-blue-200 bg-blue-50 p-3",children:t("div",{className:"flex items-start gap-2",children:[e(v,{className:"mt-0.5 h-4 w-4 flex-shrink-0 text-blue-500"}),t("div",{className:"text-left text-xs text-blue-700",children:[e("p",{className:"font-medium",children:"Mẹo tìm kiếm nhanh:"}),t("ul",{className:"mt-1 list-inside list-disc space-y-0.5 text-blue-600",children:[e("li",{children:"Nhập mã câu hỏi (VD: M001)"}),e("li",{children:"Nhập tiêu đề hoặc một phần nội dung"}),e("li",{children:"Kết quả sẽ tự động cập nhật"})]})]})]})})]}):Z?t("div",{className:"flex flex-col items-center justify-center p-8 text-center",children:[e("div",{className:"mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-amber-100",children:e(f,{className:"h-8 w-8 text-amber-500"})}),e("p",{className:"text-sm font-medium text-gray-700",children:"Chưa có câu hỏi trong ngân hàng"}),t("p",{className:"mt-1 text-xs text-gray-500",children:["Ngân hàng câu hỏi chưa có dữ liệu cho loại: ",e("strong",{children:I(k)})]}),e("p",{className:"mt-2 text-xs text-gray-400",children:"Hãy thêm câu hỏi vào ngân hàng hoặc tạo câu hỏi mới"})]}):X?t("div",{className:"flex flex-col items-center justify-center p-8 text-center",children:[e("div",{className:"mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-gray-100",children:e(u,{className:"h-8 w-8 text-gray-400"})}),e("p",{className:"text-sm font-medium text-gray-700",children:"Không tìm thấy kết quả"}),t("p",{className:"mt-1 text-xs text-gray-500",children:['Thử tìm với từ khóa khác: "',P,'"']})]}):e("div",{className:"divide-y divide-gray-100 p-2",children:Y.map(n=>{const r=(e=>{const t=e.content;return t&&t.subQuestions&&Array.isArray(t.subQuestions)?t.subQuestions.length:null})(n);return t(o,{className:"flex cursor-pointer flex-col items-start gap-2 rounded-xl p-3 transition-all hover:bg-gradient-to-r hover:from-indigo-50 hover:to-purple-50 focus:bg-gradient-to-r focus:from-indigo-50 focus:to-purple-50",onSelect:()=>(e=>{D(e),Q(!1),W(""),M("")})(n),children:[t("div",{className:"flex w-full items-start justify-between gap-2",children:[t("div",{className:"flex items-center gap-2",children:[n.code&&t("div",{className:"flex items-center gap-1 rounded-md bg-gradient-to-r from-indigo-500 to-purple-500 px-2 py-0.5 text-xs font-bold text-white shadow-sm",children:[e(b,{className:"h-3 w-3"}),n.code]}),e("div",{className:"flex-shrink-0",children:(i=n.type,T.includes(i)?e(h,{className:"h-4 w-4 text-blue-500"}):O.includes(i)?e(x,{className:"h-4 w-4 text-green-500"}):R.includes(i)?e(p,{className:"h-4 w-4 text-purple-500"}):e(g,{className:"h-4 w-4 text-gray-500"}))})]}),void 0!==n.usageCount&&n.usageCount>0&&t("div",{className:"flex items-center gap-1 text-xs text-gray-500",title:"Số lần sử dụng",children:[e(y,{className:"h-3 w-3"}),n.usageCount]})]}),n.title&&e("p",{className:"text-sm font-semibold text-gray-800",children:A(n.title,60)}),e("p",{className:"text-xs text-gray-600",children:A(getQuestionDisplayText(n),80)}),t("div",{className:"flex flex-wrap items-center gap-1.5",children:[e(d,{variant:"outline",className:E("px-2 py-0.5 text-[10px] font-semibold",L(n.difficulty)),children:S(n.difficulty)}),t(d,{variant:"outline",className:"border-blue-200 bg-blue-50 px-2 py-0.5 text-[10px] font-semibold text-blue-700",children:[n.points||1," điểm"]}),n.skill&&e(d,{variant:"outline",className:"border-gray-200 bg-gray-50 px-2 py-0.5 text-[10px] font-semibold text-gray-600",children:n.skill.toLowerCase()}),r&&r>1&&t(d,{variant:"outline",className:"border-purple-200 bg-purple-50 px-2 py-0.5 text-[10px] font-semibold text-purple-700",children:[r," câu con"]})]})]},n.id);var i})})}),e("div",{className:"border-t border-gray-100 bg-gray-50/50 px-4 py-2",children:e("p",{className:"text-center text-xs text-gray-500",children:"Click để chọn câu hỏi và tự động điền vào form"})})]})]})}
|