@tinyweb_dev/oe-exam-sdk 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/exam-create.js +1 -179
- package/dist/api/exam-create.types.js +1 -1
- package/dist/api/exam-questions.js +1 -98
- package/dist/api/exam-questions.types.js +1 -1
- package/dist/api/exam-taking.js +1 -73
- package/dist/api/exam-taking.types.js +1 -1
- package/dist/api/index.js +1 -6
- package/dist/api/result-review.js +1 -50
- package/dist/api/result-review.types.js +1 -1
- package/dist/components/CueCardEditor.js +1 -58
- package/dist/components/common/ResolvedImage.js +1 -15
- package/dist/components/exams/CreateExamInfoForm.js +1 -35
- package/dist/components/exams/CreateExamPageContainer.js +1 -103
- package/dist/components/exams/ExamCreator.js +1 -89
- package/dist/components/exams/ExamPartTabs.js +1 -40
- package/dist/components/exams/ExamQuestionGrid.js +1 -18
- package/dist/components/exams/ExamQuestionsPageContainer.js +1 -90
- package/dist/components/exams/PartAudioUploader.js +1 -9
- package/dist/components/exams/TemplateSelector.js +1 -66
- package/dist/components/exams/api.js +1 -3
- package/dist/components/exams/create-exam.validation.js +1 -18
- package/dist/components/exams/exam-template.utils.js +1 -18
- package/dist/components/exams/index.js +1 -10
- package/dist/components/exams/take/ExamTakingPageContainer.js +1 -153
- package/dist/components/exams/take/GenericQuestionRenderer.js +1 -14
- package/dist/components/exams/take/exam-taking.utils.js +1 -88
- package/dist/components/exams/take/hooks/useSpeakingRecorder.js +1 -24
- package/dist/components/exams/take/index.js +1 -2
- package/dist/components/exams/take/types.js +1 -1
- package/dist/components/index.js +1 -42
- package/dist/components/questions/_shared/config/question-types.config.js +1 -333
- package/dist/components/questions/_shared/constants/color-palette.js +1 -21
- package/dist/components/questions/_shared/hooks/useAnswerGroupQuestionGroup.js +1 -105
- package/dist/components/questions/_shared/hooks/useBasicQuestionGroup.js +1 -222
- package/dist/components/questions/_shared/hooks/useCompareImagesGroup.js +1 -140
- package/dist/components/questions/_shared/hooks/useImageQuestionGroup.js +1 -123
- package/dist/components/questions/_shared/hooks/useInfoExchangeGroup.js +1 -50
- package/dist/components/questions/_shared/hooks/useQuestionFormState.js +1 -54
- package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +1 -123
- package/dist/components/questions/_shared/hooks/useQuestionListGroup.js +1 -121
- package/dist/components/questions/_shared/hooks/useQuestionOnChange.js +1 -70
- package/dist/components/questions/_shared/hooks/useSceneQuestionGroup.js +1 -194
- package/dist/components/questions/_shared/index.js +1 -7
- package/dist/components/questions/_shared/types/answer-the-question.type.js +1 -1
- package/dist/components/questions/_shared/types/arrange-letters-into-words.type.js +1 -1
- package/dist/components/questions/_shared/types/choose-correct-adjective.type.js +1 -3
- package/dist/components/questions/_shared/types/color-region.config.js +1 -1
- package/dist/components/questions/_shared/types/fill-in-blank.type.js +1 -1
- package/dist/components/questions/_shared/types/fill-missing-words-in-grid.type.js +1 -1
- package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +1 -1
- package/dist/components/questions/_shared/types/index.js +1 -12
- package/dist/components/questions/_shared/types/label-the-picture.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-choose-from-answer-group.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-choose-objects-in-scene.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-drag-objects-into-scene.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-answer.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-compare-images.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-image-group.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-info-exchange.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-odd-one-out.type.js +1 -3
- package/dist/components/questions/_shared/types/listen-and-speak-question-list.type.js +1 -1
- package/dist/components/questions/_shared/types/listen-and-speak-with-story-images.type.js +1 -3
- package/dist/components/questions/_shared/types/listen-and-tick-answer.type.js +1 -1
- package/dist/components/questions/_shared/types/listening-drawline.type.js +1 -1
- package/dist/components/questions/_shared/types/look-and-choose-answer.type.js +1 -1
- package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +1 -1
- package/dist/components/questions/_shared/types/match-by-writing-answer.type.js +1 -1
- package/dist/components/questions/_shared/types/multiple-choice.type.js +1 -1
- package/dist/components/questions/_shared/types/question-group.type.js +1 -11
- package/dist/components/questions/_shared/types/read-and-color-objects.type.js +1 -1
- package/dist/components/questions/_shared/types/read-displayed-content.type.js +1 -1
- package/dist/components/questions/_shared/types/read-passage-and-answer-questions.type.js +1 -1
- package/dist/components/questions/_shared/types/speaking-describe-image.type.js +1 -1
- package/dist/components/questions/_shared/types/spontaneous-qa.type.js +1 -1
- package/dist/components/questions/_shared/types/word-fill-paragraph-with-options.type.js +1 -1
- package/dist/components/questions/_shared/types/word-fill-paragraph.type.js +1 -1
- package/dist/components/questions/_shared/types/word-fill-structured-form.type.js +1 -1
- package/dist/components/questions/_shared/types/word-ordering.type.js +1 -1
- package/dist/components/questions/_shared/types/write-a-short-letter.type.js +1 -1
- package/dist/components/questions/_shared/types/write-correct-verb-form.type.js +1 -3
- package/dist/components/questions/_shared/types/write-sentences.type.js +1 -1
- package/dist/components/questions/_shared/types/write-short-paragraph.type.js +1 -1
- package/dist/components/questions/_shared/utils/answer-builder.js +1 -79
- package/dist/components/questions/_shared/utils/createColorRegionRegistration.js +1 -39
- package/dist/components/questions/_shared/utils/id-generator.js +1 -3
- package/dist/components/questions/_shared/utils/question-validation.js +1 -21
- package/dist/components/questions/components/ColorNode.js +1 -85
- package/dist/components/questions/components/ColorRegionClient.js +1 -545
- package/dist/components/questions/components/ColorRegionCreator.js +1 -420
- package/dist/components/questions/components/ColorRegionNode.js +1 -59
- package/dist/components/questions/components/CreatorGuide.js +1 -103
- package/dist/components/questions/components/NameNode.js +1 -26
- package/dist/components/questions/components/QuestionBankOption.js +1 -46
- package/dist/components/questions/components/QuestionBasicInfoForm.js +1 -9
- package/dist/components/questions/components/QuestionCommonFields.js +1 -33
- package/dist/components/questions/components/QuestionSearchDropdown.js +1 -258
- package/dist/components/questions/components/QuestionTypeSelector.js +1 -32
- package/dist/components/questions/components/QuestionTypeSpecificForms.js +1 -6
- package/dist/components/questions/components/RegionNode.js +1 -94
- package/dist/components/questions/components/index.js +1 -14
- package/dist/components/questions/creator/QuestionCreator.js +1 -90
- package/dist/components/questions/creator/QuestionGroupCreator.js +1 -119
- package/dist/components/questions/creator/dedicated-component-router.js +1 -79
- package/dist/components/questions/creator/index.js +1 -3
- package/dist/components/questions/creator/question-type-registry.js +1 -80
- package/dist/components/questions/groups/ArticlesGroupCard.js +1 -121
- package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -50
- package/dist/components/questions/groups/DocumentGroupCard.js +1 -114
- package/dist/components/questions/groups/MatchAnswerGroupCard.js +1 -25
- package/dist/components/questions/index.js +1 -10
- package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +1 -37
- package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +1 -351
- package/dist/components/questions/types/answer-the-question/register.js +1 -67
- package/dist/components/questions/types/answer-the-question/transform.js +1 -36
- package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +1 -141
- package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +1 -118
- package/dist/components/questions/types/arrange-letters-into-words/index.js +1 -2
- package/dist/components/questions/types/arrange-letters-into-words/register.js +1 -4
- package/dist/components/questions/types/arrange-letters-into-words/transform.js +1 -24
- package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -64
- package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +1 -169
- package/dist/components/questions/types/choose-correct-adjective/index.js +1 -2
- package/dist/components/questions/types/choose-correct-adjective/register.js +1 -55
- package/dist/components/questions/types/choose-correct-adjective/transform.js +1 -16
- package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +1 -131
- package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +1 -581
- package/dist/components/questions/types/choose-the-correct-answer/index.js +1 -2
- package/dist/components/questions/types/choose-the-correct-answer/register.js +1 -101
- package/dist/components/questions/types/choose-the-correct-answer/transform.js +1 -43
- package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +1 -275
- package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +1 -448
- package/dist/components/questions/types/fill-in-blank/index.js +1 -2
- package/dist/components/questions/types/fill-in-blank/register.js +1 -138
- package/dist/components/questions/types/fill-in-blank/transform.js +1 -59
- package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +1 -117
- package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +1 -671
- package/dist/components/questions/types/fill-missing-words-in-grid/index.js +1 -2
- package/dist/components/questions/types/fill-missing-words-in-grid/register.js +1 -37
- package/dist/components/questions/types/fill-missing-words-in-grid/transform.js +1 -15
- package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +1 -262
- package/dist/components/questions/types/gn-speaking-interview/register.js +1 -121
- package/dist/components/questions/types/gn-speaking-interview/transform.js +1 -48
- package/dist/components/questions/types/image-object-matching/ImageObjectMatchingClient.js +1 -16
- package/dist/components/questions/types/image-object-matching/ImageObjectMatchingCreator.js +1 -27
- package/dist/components/questions/types/image-object-matching/index.js +1 -2
- package/dist/components/questions/types/image-object-matching/register.js +1 -3
- package/dist/components/questions/types/image-object-matching/transform.js +1 -2
- package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +1 -102
- package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +1 -344
- package/dist/components/questions/types/label-the-picture/index.js +1 -2
- package/dist/components/questions/types/label-the-picture/register.js +1 -54
- package/dist/components/questions/types/label-the-picture/transform.js +1 -28
- package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +1 -78
- package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +1 -232
- package/dist/components/questions/types/listen-and-choose-from-answer-group/index.js +1 -2
- package/dist/components/questions/types/listen-and-choose-from-answer-group/register.js +1 -82
- package/dist/components/questions/types/listen-and-choose-from-answer-group/transform.js +1 -27
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +1 -70
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +1 -386
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/index.js +1 -2
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/register.js +1 -100
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/transform.js +1 -33
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +1 -106
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +1 -457
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/index.js +1 -2
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/register.js +1 -110
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/transform.js +1 -35
- package/dist/components/questions/types/listen-and-speak-answer/ArrowIndicator.js +1 -8
- package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -22
- package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +1 -291
- package/dist/components/questions/types/listen-and-speak-answer/index.js +1 -2
- package/dist/components/questions/types/listen-and-speak-answer/register.js +1 -84
- package/dist/components/questions/types/listen-and-speak-answer/transform.js +1 -27
- package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -32
- package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +1 -295
- package/dist/components/questions/types/listen-and-speak-compare-images/register.js +1 -79
- package/dist/components/questions/types/listen-and-speak-compare-images/transform.js +1 -27
- package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -34
- package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +1 -285
- package/dist/components/questions/types/listen-and-speak-image-group/register.js +1 -81
- package/dist/components/questions/types/listen-and-speak-image-group/transform.js +1 -26
- package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -38
- package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +1 -385
- package/dist/components/questions/types/listen-and-speak-info-exchange/register.js +1 -87
- package/dist/components/questions/types/listen-and-speak-info-exchange/transform.js +1 -32
- package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -26
- package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +1 -247
- package/dist/components/questions/types/listen-and-speak-odd-one-out/register.js +1 -59
- package/dist/components/questions/types/listen-and-speak-odd-one-out/transform.js +1 -18
- package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -26
- package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +1 -256
- package/dist/components/questions/types/listen-and-speak-question-list/register.js +1 -81
- package/dist/components/questions/types/listen-and-speak-question-list/transform.js +1 -26
- package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -26
- package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +1 -230
- package/dist/components/questions/types/listen-and-speak-with-story-images/register.js +1 -56
- package/dist/components/questions/types/listen-and-speak-with-story-images/transform.js +1 -16
- package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +1 -72
- package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +1 -230
- package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +1 -1
- package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +1 -66
- package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +1 -42
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +1 -96
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +1 -238
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +1 -2
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +1 -76
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +1 -19
- package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +1 -134
- package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +1 -617
- package/dist/components/questions/types/look-picture-fill-word-hint/index.js +1 -2
- package/dist/components/questions/types/look-picture-fill-word-hint/register.js +1 -82
- package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +1 -29
- package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +1 -38
- package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +1 -359
- package/dist/components/questions/types/match-by-writing-answer/index.js +1 -3
- package/dist/components/questions/types/match-by-writing-answer/register.js +1 -58
- package/dist/components/questions/types/match-by-writing-answer/transform.js +1 -61
- package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsClient.js +1 -16
- package/dist/components/questions/types/read-and-color-objects/ReadAndColorObjectsCreator.js +1 -23
- package/dist/components/questions/types/read-and-color-objects/index.js +1 -2
- package/dist/components/questions/types/read-and-color-objects/register.js +1 -3
- package/dist/components/questions/types/read-and-color-objects/transform.js +1 -36
- package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +1 -125
- package/dist/components/questions/types/read-displayed-content/register.js +1 -64
- package/dist/components/questions/types/read-displayed-content/transform.js +1 -12
- package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -64
- package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +1 -511
- package/dist/components/questions/types/read-passage-and-answer-questions/index.js +1 -2
- package/dist/components/questions/types/read-passage-and-answer-questions/register.js +1 -58
- package/dist/components/questions/types/read-passage-and-answer-questions/transform.js +1 -28
- package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +1 -373
- package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +1 -41
- package/dist/components/questions/types/speaking-conversation/register.js +1 -110
- package/dist/components/questions/types/speaking-conversation/transform.js +1 -34
- package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +1 -74
- package/dist/components/questions/types/speaking-cue-card/register.js +1 -52
- package/dist/components/questions/types/speaking-cue-card/transform.js +1 -12
- package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -10
- package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +1 -161
- package/dist/components/questions/types/speaking-describe-image/index.js +1 -2
- package/dist/components/questions/types/speaking-describe-image/register.js +1 -69
- package/dist/components/questions/types/speaking-describe-image/transform.js +1 -16
- package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +1 -234
- package/dist/components/questions/types/spontaneous-qa/register.js +1 -93
- package/dist/components/questions/types/spontaneous-qa/transform.js +1 -34
- package/dist/components/questions/types/true-false/TrueFalseClient.js +1 -75
- package/dist/components/questions/types/true-false/TrueFalseCreator.js +1 -244
- package/dist/components/questions/types/true-false/index.js +1 -2
- package/dist/components/questions/types/true-false/register.js +1 -77
- package/dist/components/questions/types/true-false/transform.js +1 -32
- package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +1 -233
- package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +1 -631
- package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphWrapper.js +1 -28
- package/dist/components/questions/types/word-fill-paragraph/index.js +1 -2
- package/dist/components/questions/types/word-fill-paragraph/register.js +1 -136
- package/dist/components/questions/types/word-fill-paragraph/transform.js +1 -56
- package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -138
- package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +1 -261
- package/dist/components/questions/types/word-fill-structured-form/index.js +1 -2
- package/dist/components/questions/types/word-fill-structured-form/register.js +1 -67
- package/dist/components/questions/types/word-fill-structured-form/transform.js +1 -40
- package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -84
- package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +1 -192
- package/dist/components/questions/types/word-ordering/index.js +1 -2
- package/dist/components/questions/types/word-ordering/register.js +1 -46
- package/dist/components/questions/types/word-ordering/transform.js +1 -24
- package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +1 -98
- package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +1 -320
- package/dist/components/questions/types/write-a-short-letter/index.js +1 -2
- package/dist/components/questions/types/write-a-short-letter/register.js +1 -68
- package/dist/components/questions/types/write-a-short-letter/transform.js +1 -25
- package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +1 -61
- package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +1 -196
- package/dist/components/questions/types/write-correct-verb-form/index.js +1 -2
- package/dist/components/questions/types/write-correct-verb-form/register.js +1 -62
- package/dist/components/questions/types/write-correct-verb-form/transform.js +1 -17
- package/dist/components/questions/types/write-sentences/WriteSentencesClient.js +1 -22
- package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +1 -174
- package/dist/components/questions/types/write-sentences/index.js +1 -2
- package/dist/components/questions/types/write-sentences/register.js +1 -58
- package/dist/components/questions/types/write-sentences/transform.js +1 -28
- package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -96
- package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +1 -161
- package/dist/components/questions/types/write-short-paragraph/index.js +1 -2
- package/dist/components/questions/types/write-short-paragraph/register.js +1 -4
- package/dist/components/questions/types/write-short-paragraph/transform.js +1 -21
- package/dist/components/questions/viewer/QuestionGroupViewer.js +1 -36
- package/dist/components/questions/viewer/QuestionViewer.js +1 -446
- package/dist/components/questions/viewer/index.js +1 -2
- package/dist/components/results/ResultReviewPageContainer.js +1 -98
- package/dist/components/results/ReviewQuestionRenderer.js +1 -72
- package/dist/components/results/index.js +1 -2
- package/dist/components/results/result-review.utils.js +1 -6
- package/dist/components/results/review-data.js +1 -150
- package/dist/components/results/review-renderer.utils.js +1 -97
- package/dist/components/results/review-types.js +1 -1
- package/dist/components/themes/cambridge-yle/CambridgeYleAudioPlayer.js +1 -138
- package/dist/components/themes/cambridge-yle/CambridgeYleBottomNav.js +1 -12
- package/dist/components/themes/cambridge-yle/CambridgeYleExamLayout.js +1 -18
- package/dist/components/themes/cambridge-yle/CambridgeYleExampleText.js +1 -11
- package/dist/components/themes/cambridge-yle/CambridgeYleHeader.js +1 -11
- package/dist/components/themes/cambridge-yle/CambridgeYleInstructionBanner.js +1 -9
- package/dist/components/themes/cambridge-yle/CambridgeYleNavButton.js +1 -13
- package/dist/components/themes/cambridge-yle/CambridgeYlePartBanner.js +1 -8
- package/dist/components/themes/cambridge-yle/CambridgeYleProgressBar.js +1 -6
- package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCard.js +1 -5
- package/dist/components/themes/cambridge-yle/CambridgeYleQuestionCardFullWidth.js +1 -9
- package/dist/components/themes/cambridge-yle/CambridgeYleSidebar.js +1 -41
- package/dist/components/themes/cambridge-yle/CambridgeYleTimer.js +1 -25
- package/dist/components/themes/cambridge-yle/index.js +1 -13
- package/dist/components/themes/english-certification/EcAnswerTheQuestion.js +1 -24
- package/dist/components/themes/english-certification/EcMatchByWritingAnswer.js +1 -88
- package/dist/components/themes/english-certification/EcWordFillParagraph.js +1 -55
- package/dist/components/themes/english-certification/EcWordFillStructuredForm.js +1 -156
- package/dist/components/themes/english-certification/EcWritingTask.js +1 -34
- package/dist/components/themes/english-certification/EnglishCertificationAudioPlayer.js +1 -114
- package/dist/components/themes/english-certification/EnglishCertificationBrand.js +1 -11
- package/dist/components/themes/english-certification/EnglishCertificationExamLayout.js +1 -21
- package/dist/components/themes/english-certification/EnglishCertificationFlagButton.js +1 -15
- package/dist/components/themes/english-certification/EnglishCertificationFooterBar.js +1 -19
- package/dist/components/themes/english-certification/EnglishCertificationGroupedQuestion.js +1 -148
- package/dist/components/themes/english-certification/EnglishCertificationHeader.js +1 -31
- package/dist/components/themes/english-certification/EnglishCertificationNavigationPanel.js +1 -71
- package/dist/components/themes/english-certification/EnglishCertificationOptionSelector.js +1 -33
- package/dist/components/themes/english-certification/EnglishCertificationPassagePanel.js +1 -16
- package/dist/components/themes/english-certification/EnglishCertificationQuestionCard.js +1 -22
- package/dist/components/themes/english-certification/EnglishCertificationQuestionItem.js +1 -37
- package/dist/components/themes/english-certification/EnglishCertificationQuestionRenderer.js +1 -72
- package/dist/components/themes/english-certification/EnglishCertificationReadingSection.js +1 -90
- package/dist/components/themes/english-certification/EnglishCertificationSingleQuestion.js +1 -18
- package/dist/components/themes/english-certification/EnglishCertificationSingleQuestionList.js +1 -29
- package/dist/components/themes/english-certification/english-certification-theme.js +1 -58
- package/dist/components/themes/english-certification/grouped-question-utils.js +1 -218
- package/dist/components/themes/english-certification/index.js +1 -21
- package/dist/components/themes/index.js +1 -6
- package/dist/components/themes/summer-sky/SummerSkyAudioPlayer.js +1 -170
- package/dist/components/themes/summer-sky/SummerSkyLayout.js +1 -221
- package/dist/components/themes/summer-sky/SummerSkySubmitModal.js +1 -18
- package/dist/components/themes/summer-sky/SummerSkyWaitingRoom.js +1 -194
- package/dist/components/themes/types.js +1 -1
- package/dist/components/ui/alert-dialog.js +1 -26
- package/dist/components/ui/alert.js +1 -22
- package/dist/components/ui/avatar.js +1 -12
- package/dist/components/ui/badge.js +1 -38
- package/dist/components/ui/button.js +1 -35
- package/dist/components/ui/calendar.js +1 -76
- package/dist/components/ui/card.js +1 -16
- package/dist/components/ui/carousel.js +1 -91
- package/dist/components/ui/checkbox.js +1 -9
- package/dist/components/ui/collapsible.js +1 -13
- package/dist/components/ui/command.js +1 -34
- package/dist/components/ui/confirm-dialog.js +1 -49
- package/dist/components/ui/dialog.js +1 -23
- package/dist/components/ui/drawer.js +1 -35
- package/dist/components/ui/dropdown-menu.js +1 -33
- package/dist/components/ui/file-upload.js +1 -155
- package/dist/components/ui/form.js +1 -62
- package/dist/components/ui/hover-card.js +1 -10
- package/dist/components/ui/image-preview.js +1 -29
- package/dist/components/ui/input.js +1 -8
- package/dist/components/ui/label.js +1 -9
- package/dist/components/ui/language-switcher.js +1 -15
- package/dist/components/ui/multi-select.js +1 -90
- package/dist/components/ui/points-input.js +1 -33
- package/dist/components/ui/popover.js +1 -26
- package/dist/components/ui/progress.js +1 -16
- package/dist/components/ui/radio-group.js +1 -12
- package/dist/components/ui/scroll-area.js +1 -11
- package/dist/components/ui/select.js +1 -38
- package/dist/components/ui/separator.js +1 -8
- package/dist/components/ui/sheet.js +1 -40
- package/dist/components/ui/single-select.js +1 -76
- package/dist/components/ui/skeleton.js +1 -6
- package/dist/components/ui/switch.js +1 -8
- package/dist/components/ui/table.js +1 -20
- package/dist/components/ui/tabs.js +1 -17
- package/dist/components/ui/textarea.js +1 -8
- package/dist/components/ui/tooltip.js +1 -11
- package/dist/index.js +1 -19
- package/dist/mcp/config.js +1 -32
- package/dist/mcp/http-client.js +1 -48
- package/dist/mcp/index.js +1 -29
- package/dist/mcp/tools/_helpers.js +1 -25
- package/dist/mcp/tools/exams.js +1 -439
- package/dist/mcp/tools/index.js +1 -6
- package/dist/mcp/tools/results.js +1 -19
- package/dist/shared/constants/ApiConstant.js +1 -276
- package/dist/shared/constants/QueryKeyConstant.js +1 -112
- package/dist/shared/constants/exam-level.enum.js +1 -41
- package/dist/shared/constants/landings/placement-test.constants.js +1 -431
- package/dist/shared/constants/question-skills.js +1 -267
- package/dist/shared/constants/routes.js +1 -81
- package/dist/shared/index.js +1 -7
- package/dist/shared/lib/hooks/index.js +1 -10
- package/dist/shared/lib/hooks/useAlertDialog.js +1 -57
- package/dist/shared/lib/hooks/useAudioPlayCounter.js +1 -30
- package/dist/shared/lib/hooks/useAudioPreview.js +1 -81
- package/dist/shared/lib/hooks/useAudioUpload.js +1 -24
- package/dist/shared/lib/hooks/useConfirmDialog.js +1 -89
- package/dist/shared/lib/hooks/useDebouncedCallback.js +1 -32
- package/dist/shared/lib/hooks/useDelayedAutoPlay.js +1 -12
- package/dist/shared/lib/hooks/useFileUpload.js +1 -24
- package/dist/shared/lib/hooks/useImageUrl.js +1 -5
- package/dist/shared/lib/hooks/usePresignedFileUrl.js +1 -10
- package/dist/shared/lib/hooks/useReactHookForm.js +1 -31
- package/dist/shared/lib/hooks/useToast.js +1 -62
- package/dist/shared/lib/i18n/index.js +1 -54
- package/dist/shared/lib/i18n/messages/en/admin.js +1 -2104
- package/dist/shared/lib/i18n/messages/en/auth.js +1 -86
- package/dist/shared/lib/i18n/messages/en/common.js +1 -140
- package/dist/shared/lib/i18n/messages/en/file-upload.js +1 -18
- package/dist/shared/lib/i18n/messages/en/student.js +1 -133
- package/dist/shared/lib/i18n/messages/en/terms.js +1 -37
- package/dist/shared/lib/i18n/messages/en.js +1 -15
- package/dist/shared/lib/i18n/messages/vi/admin.js +1 -2104
- package/dist/shared/lib/i18n/messages/vi/auth.js +1 -86
- package/dist/shared/lib/i18n/messages/vi/common.js +1 -140
- package/dist/shared/lib/i18n/messages/vi/file-upload.js +1 -18
- package/dist/shared/lib/i18n/messages/vi/student.js +1 -133
- package/dist/shared/lib/i18n/messages/vi/terms.js +1 -37
- package/dist/shared/lib/i18n/messages/vi.js +1 -15
- package/dist/shared/lib/i18n/types.js +1 -1
- package/dist/shared/lib/stores/examQuestionStore.js +1 -212
- package/dist/shared/lib/stores/localeStore.js +1 -10
- package/dist/shared/lib/utils/public-page.js +1 -44
- package/dist/shared/lib/utils/question-transform-types.js +1 -1
- package/dist/shared/lib/utils/question-transform.js +1 -174
- package/dist/shared/lib/utils.js +1 -168
- package/dist/shared/lib/validation/choose-correct-answer.validation.js +1 -111
- package/dist/shared/lib/validation/label-the-picture.validation.js +1 -19
- package/dist/shared/lib/validation/listen-and-choose-from-answer-group.validation.js +1 -46
- package/dist/shared/lib/validation/listen-and-choose-objects-in-scene.validation.js +1 -21
- package/dist/shared/lib/validation/listen-and-drag-objects-into-scene.validation.js +1 -34
- package/dist/shared/lib/validation/listen-and-speak-answer.validation.js +1 -40
- package/dist/shared/lib/validation/listen-and-speak-compare-images.validation.js +1 -45
- package/dist/shared/lib/validation/listen-and-speak-image-group.validation.js +1 -19
- package/dist/shared/lib/validation/listen-and-speak-info-exchange.validation.js +1 -39
- package/dist/shared/lib/validation/listen-and-speak-odd-one-out.validation.js +1 -32
- package/dist/shared/lib/validation/listen-and-speak-question-list.validation.js +1 -19
- package/dist/shared/lib/validation/listen-and-speak-with-story-images.validation.js +1 -28
- package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +1 -12
- package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +1 -47
- package/dist/shared/lib/validation/read-and-color-objects.validation.js +1 -29
- package/dist/shared/lib/validation/read-displayed-content.validation.js +1 -28
- package/dist/shared/lib/validation/speaking-conversation.validation.js +1 -75
- package/dist/shared/lib/validation/speaking-describe-image.validation.js +1 -31
- package/dist/shared/lib/validation/spontaneous-qa.validation.js +1 -48
- package/dist/shared/lib/validation/word-fill-paragraph-default.validation.js +1 -38
- package/dist/shared/lib/validation/word-fill-paragraph-with-options.validation.js +1 -39
- package/dist/shared/lib/validation/word-fill-paragraph.validation.js +1 -43
- package/dist/shared/lib/validation/word-ordering.validation.js +1 -33
- package/dist/shared/types/branch.types.js +1 -6
- package/dist/shared/types/common.types.js +1 -50
- package/dist/shared/types/entities/exam-schedule.types.js +1 -5
- package/dist/shared/types/entities/exam.types.js +1 -1
- package/dist/shared/types/entities/question.types.js +1 -4
- package/dist/shared/types/entities/result.types.js +1 -4
- package/dist/shared/types/entities/user.types.js +1 -4
- package/dist/shared/types/exam-taking.types.js +1 -2
- package/dist/shared/types/history.types.js +1 -34
- package/dist/shared/types/index.js +1 -1
- package/dist/shared/types/questions/choose-correct-adjective.js +1 -4
- package/dist/shared/types/questions/choose-the-correct-answer.js +1 -9
- package/dist/shared/types/questions/fill-missing-words-in-grid.js +1 -11
- package/dist/shared/types/questions/index.js +1 -40
- package/dist/shared/types/questions/listen-and-speak-answer.js +1 -1
- package/dist/shared/types/questions/listen-and-speak-compare-images.js +1 -1
- package/dist/shared/types/questions/listen-and-speak-image-group.js +1 -1
- package/dist/shared/types/questions/listen-and-speak-info-exchange.js +1 -3
- package/dist/shared/types/questions/listen-and-speak-odd-one-out.js +1 -3
- package/dist/shared/types/questions/listen-and-speak-question-list.js +1 -1
- package/dist/shared/types/questions/listen-and-speak-with-story-images.js +1 -3
- package/dist/shared/types/questions/look-picture-choose-correct-answer.js +1 -5
- package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +1 -9
- package/dist/shared/types/questions/look-picture-fill-word-hint.js +1 -7
- package/dist/shared/types/questions/read-displayed-content.js +1 -1
- package/dist/shared/types/questions/speaking-conversation.js +1 -7
- package/dist/shared/types/questions/speaking-describe-image.js +1 -1
- package/dist/shared/types/questions/spontaneous-qa.js +1 -1
- package/dist/shared/types/questions/write-correct-verb-form.js +1 -12
- package/dist/shared/types/role.types.js +1 -1
- package/dist/shared/types/structure-management.js +1 -28
- package/dist/shared/types/student.types.js +1 -54
- package/dist/shared/types/teacher-dashboard-rooms.js +1 -2
- package/package.json +4 -2
|
@@ -1,80 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { fillInBlankRegistration } from '../types/fill-in-blank/register';
|
|
3
|
-
import { arrangeLettersIntoWordsRegistration } from '../types/arrange-letters-into-words/register';
|
|
4
|
-
import { writeShortParagraphRegistration } from '../types/write-short-paragraph/register';
|
|
5
|
-
import { fillMissingWordsInGridRegistration } from '../types/fill-missing-words-in-grid/register';
|
|
6
|
-
import { lookPictureChooseCorrectAnswerRegistration } from '../types/look-picture-choose-correct-answer/register';
|
|
7
|
-
import { lookPictureFillBlankChooseAnswerRegistration } from '../types/look-picture-fill-blank-choose-answer/register';
|
|
8
|
-
import { lookPictureFillWordHintRegistration } from '../types/look-picture-fill-word-hint/register';
|
|
9
|
-
import { labelThePictureRegistration } from '../types/label-the-picture/register';
|
|
10
|
-
import { readAndColorObjectsRegistration } from '../types/read-and-color-objects/register';
|
|
11
|
-
import { imageObjectMatchingRegistration } from '../types/image-object-matching/register';
|
|
12
|
-
import { readPassageAndAnswerQuestionsRegistration } from '../types/read-passage-and-answer-questions/register';
|
|
13
|
-
import { chooseTheCorrectAnswerRegistration } from '../types/choose-the-correct-answer/register';
|
|
14
|
-
import { writeAShortLetterRegistration } from '../types/write-a-short-letter/register';
|
|
15
|
-
import { writeCorrectVerbFormRegistration } from '../types/write-correct-verb-form/register';
|
|
16
|
-
import { chooseCorrectAdjectiveRegistration } from '../types/choose-correct-adjective/register';
|
|
17
|
-
import { answerTheQuestionRegistration } from '../types/answer-the-question/register';
|
|
18
|
-
import { trueFalseRegistration } from '../types/true-false/register';
|
|
19
|
-
import { wordOrderingRegistration } from '../types/word-ordering/register';
|
|
20
|
-
import { wordFillParagraphRegistration } from '../types/word-fill-paragraph/register';
|
|
21
|
-
import { matchByWritingAnswerRegistration } from '../types/match-by-writing-answer/register';
|
|
22
|
-
import { writeSentencesRegistration } from '../types/write-sentences/register';
|
|
23
|
-
import { listenAndChooseFromAnswerGroupRegistration } from '../types/listen-and-choose-from-answer-group/register';
|
|
24
|
-
import { listenAndChooseObjectsInSceneRegistration } from '../types/listen-and-choose-objects-in-scene/register';
|
|
25
|
-
import { listenAndDragObjectsIntoSceneRegistration } from '../types/listen-and-drag-objects-into-scene/register';
|
|
26
|
-
import { listenAndSpeakAnswerRegistration } from '../types/listen-and-speak-answer/register';
|
|
27
|
-
import { listenAndSpeakImageGroupRegistration } from '../types/listen-and-speak-image-group/register';
|
|
28
|
-
import { listenAndSpeakQuestionListRegistration } from '../types/listen-and-speak-question-list/register';
|
|
29
|
-
import { ListenAndSpeakCompareImagesRegistration } from '../types/listen-and-speak-compare-images/register';
|
|
30
|
-
import { listenAndSpeakWithStoryImagesRegistration } from '../types/listen-and-speak-with-story-images/register';
|
|
31
|
-
import { listenAndSpeakOddOneOutRegistration } from '../types/listen-and-speak-odd-one-out/register';
|
|
32
|
-
import { listenAndSpeakInfoExchangeRegistration } from '../types/listen-and-speak-info-exchange/register';
|
|
33
|
-
import { speakingDescribeImageRegistration } from '../types/speaking-describe-image/register';
|
|
34
|
-
import { spontaneousQARegistration } from '../types/spontaneous-qa/register';
|
|
35
|
-
import { readDisplayedContentRegistration } from '../types/read-displayed-content/register';
|
|
36
|
-
import { speakingConversationRegistration } from '../types/speaking-conversation/register';
|
|
37
|
-
import { gnSpeakingInterviewRegistration } from '../types/gn-speaking-interview/register';
|
|
38
|
-
import { speakingCueCardRegistration } from '../types/speaking-cue-card/register';
|
|
39
|
-
import { wordFillStructuredFormRegistration } from '../types/word-fill-structured-form/register';
|
|
40
|
-
// ─── Registry map ───────────────────────────────────────────────────────────
|
|
41
|
-
export const questionTypeRegistry = {
|
|
42
|
-
CHOOSE_THE_CORRECT_ANSWER: chooseTheCorrectAnswerRegistration,
|
|
43
|
-
FILL_IN_BLANK: fillInBlankRegistration,
|
|
44
|
-
ARRANGE_LETTERS_INTO_WORDS: arrangeLettersIntoWordsRegistration,
|
|
45
|
-
WRITE_SHORT_PARAGRAPH: writeShortParagraphRegistration,
|
|
46
|
-
FILL_MISSING_WORDS_IN_GRID: fillMissingWordsInGridRegistration,
|
|
47
|
-
LOOK_PICTURE_CHOOSE_CORRECT_ANSWER: lookPictureChooseCorrectAnswerRegistration,
|
|
48
|
-
LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER: lookPictureFillBlankChooseAnswerRegistration,
|
|
49
|
-
LOOK_PICTURE_FILL_WORD_HINT: lookPictureFillWordHintRegistration,
|
|
50
|
-
LABEL_THE_PICTURE: labelThePictureRegistration,
|
|
51
|
-
READ_AND_COLOR_OBJECTS: readAndColorObjectsRegistration,
|
|
52
|
-
IMAGE_OBJECT_MATCHING: imageObjectMatchingRegistration,
|
|
53
|
-
READ_PASSAGE_AND_ANSWER_QUESTIONS: readPassageAndAnswerQuestionsRegistration,
|
|
54
|
-
WRITE_A_SHORT_LETTER: writeAShortLetterRegistration,
|
|
55
|
-
WRITE_CORRECT_VERB_FORM: writeCorrectVerbFormRegistration,
|
|
56
|
-
CHOOSE_CORRECT_ADJECTIVE: chooseCorrectAdjectiveRegistration,
|
|
57
|
-
ANSWER_THE_QUESTION: answerTheQuestionRegistration,
|
|
58
|
-
TRUE_FALSE: trueFalseRegistration,
|
|
59
|
-
WORD_ORDERING: wordOrderingRegistration,
|
|
60
|
-
WORD_FILL_PARAGRAPH: wordFillParagraphRegistration, // Will need to handle viewMode mapping either here or in wrapper
|
|
61
|
-
MATCH_BY_WRITING_ANSWER: matchByWritingAnswerRegistration,
|
|
62
|
-
WRITE_SENTENCES: writeSentencesRegistration,
|
|
63
|
-
LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP: listenAndChooseFromAnswerGroupRegistration,
|
|
64
|
-
LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE: listenAndChooseObjectsInSceneRegistration,
|
|
65
|
-
LISTEN_AND_DRAG_OBJECTS_INTO_SCENE: listenAndDragObjectsIntoSceneRegistration,
|
|
66
|
-
LISTEN_AND_SPEAK_ANSWER: listenAndSpeakAnswerRegistration,
|
|
67
|
-
LISTEN_AND_SPEAK_IMAGE_GROUP: listenAndSpeakImageGroupRegistration,
|
|
68
|
-
LISTEN_AND_SPEAK_QUESTION_LIST: listenAndSpeakQuestionListRegistration,
|
|
69
|
-
LISTEN_AND_SPEAK_COMPARE_IMAGES: ListenAndSpeakCompareImagesRegistration,
|
|
70
|
-
LISTEN_AND_SPEAK_WITH_STORY_IMAGES: listenAndSpeakWithStoryImagesRegistration,
|
|
71
|
-
LISTEN_AND_SPEAK_ODD_ONE_OUT: listenAndSpeakOddOneOutRegistration,
|
|
72
|
-
LISTEN_AND_SPEAK_INFO_EXCHANGE: listenAndSpeakInfoExchangeRegistration,
|
|
73
|
-
SPEAKING_DESCRIBE_IMAGE: speakingDescribeImageRegistration,
|
|
74
|
-
SPONTANEOUS_QA: spontaneousQARegistration,
|
|
75
|
-
READ_DISPLAYED_CONTENT: readDisplayedContentRegistration,
|
|
76
|
-
SPEAKING_CONVERSATION: speakingConversationRegistration,
|
|
77
|
-
GN_SPEAKING_INTERVIEW: gnSpeakingInterviewRegistration,
|
|
78
|
-
SPEAKING_CUE_CARD: speakingCueCardRegistration,
|
|
79
|
-
WORD_FILL_STRUCTURED_FORM: wordFillStructuredFormRegistration,
|
|
80
|
-
};
|
|
1
|
+
import{fillInBlankRegistration as r}from"../types/fill-in-blank/register";import{arrangeLettersIntoWordsRegistration as e}from"../types/arrange-letters-into-words/register";import{writeShortParagraphRegistration as t}from"../types/write-short-paragraph/register";import{fillMissingWordsInGridRegistration as s}from"../types/fill-missing-words-in-grid/register";import{lookPictureChooseCorrectAnswerRegistration as o}from"../types/look-picture-choose-correct-answer/register";import{lookPictureFillBlankChooseAnswerRegistration as i}from"../types/look-picture-fill-blank-choose-answer/register";import{lookPictureFillWordHintRegistration as _}from"../types/look-picture-fill-word-hint/register";import{labelThePictureRegistration as p}from"../types/label-the-picture/register";import{readAndColorObjectsRegistration as E}from"../types/read-and-color-objects/register";import{imageObjectMatchingRegistration as m}from"../types/image-object-matching/register";import{readPassageAndAnswerQuestionsRegistration as n}from"../types/read-passage-and-answer-questions/register";import{chooseTheCorrectAnswerRegistration as S}from"../types/choose-the-correct-answer/register";import{writeAShortLetterRegistration as a}from"../types/write-a-short-letter/register";import{writeCorrectVerbFormRegistration as A}from"../types/write-correct-verb-form/register";import{chooseCorrectAdjectiveRegistration as N}from"../types/choose-correct-adjective/register";import{answerTheQuestionRegistration as R}from"../types/answer-the-question/register";import{trueFalseRegistration as g}from"../types/true-false/register";import{wordOrderingRegistration as T}from"../types/word-ordering/register";import{wordFillParagraphRegistration as I}from"../types/word-fill-paragraph/register";import{matchByWritingAnswerRegistration as O}from"../types/match-by-writing-answer/register";import{writeSentencesRegistration as f}from"../types/write-sentences/register";import{listenAndChooseFromAnswerGroupRegistration as y}from"../types/listen-and-choose-from-answer-group/register";import{listenAndChooseObjectsInSceneRegistration as c}from"../types/listen-and-choose-objects-in-scene/register";import{listenAndDragObjectsIntoSceneRegistration as C}from"../types/listen-and-drag-objects-into-scene/register";import{listenAndSpeakAnswerRegistration as l}from"../types/listen-and-speak-answer/register";import{listenAndSpeakImageGroupRegistration as L}from"../types/listen-and-speak-image-group/register";import{listenAndSpeakQuestionListRegistration as d}from"../types/listen-and-speak-question-list/register";import{ListenAndSpeakCompareImagesRegistration as D}from"../types/listen-and-speak-compare-images/register";import{listenAndSpeakWithStoryImagesRegistration as P}from"../types/listen-and-speak-with-story-images/register";import{listenAndSpeakOddOneOutRegistration as G}from"../types/listen-and-speak-odd-one-out/register";import{listenAndSpeakInfoExchangeRegistration as w}from"../types/listen-and-speak-info-exchange/register";import{speakingDescribeImageRegistration as W}from"../types/speaking-describe-image/register";import{spontaneousQARegistration as h}from"../types/spontaneous-qa/register";import{readDisplayedContentRegistration as H}from"../types/read-displayed-content/register";import{speakingConversationRegistration as k}from"../types/speaking-conversation/register";import{gnSpeakingInterviewRegistration as u}from"../types/gn-speaking-interview/register";import{speakingCueCardRegistration as K}from"../types/speaking-cue-card/register";import{wordFillStructuredFormRegistration as U}from"../types/word-fill-structured-form/register";export const questionTypeRegistry={CHOOSE_THE_CORRECT_ANSWER:S,FILL_IN_BLANK:r,ARRANGE_LETTERS_INTO_WORDS:e,WRITE_SHORT_PARAGRAPH:t,FILL_MISSING_WORDS_IN_GRID:s,LOOK_PICTURE_CHOOSE_CORRECT_ANSWER:o,LOOK_PICTURE_FILL_BLANK_CHOOSE_ANSWER:i,LOOK_PICTURE_FILL_WORD_HINT:_,LABEL_THE_PICTURE:p,READ_AND_COLOR_OBJECTS:E,IMAGE_OBJECT_MATCHING:m,READ_PASSAGE_AND_ANSWER_QUESTIONS:n,WRITE_A_SHORT_LETTER:a,WRITE_CORRECT_VERB_FORM:A,CHOOSE_CORRECT_ADJECTIVE:N,ANSWER_THE_QUESTION:R,TRUE_FALSE:g,WORD_ORDERING:T,WORD_FILL_PARAGRAPH:I,MATCH_BY_WRITING_ANSWER:O,WRITE_SENTENCES:f,LISTEN_AND_CHOOSE_FROM_ANSWER_GROUP:y,LISTEN_AND_CHOOSE_OBJECTS_IN_SCENE:c,LISTEN_AND_DRAG_OBJECTS_INTO_SCENE:C,LISTEN_AND_SPEAK_ANSWER:l,LISTEN_AND_SPEAK_IMAGE_GROUP:L,LISTEN_AND_SPEAK_QUESTION_LIST:d,LISTEN_AND_SPEAK_COMPARE_IMAGES:D,LISTEN_AND_SPEAK_WITH_STORY_IMAGES:P,LISTEN_AND_SPEAK_ODD_ONE_OUT:G,LISTEN_AND_SPEAK_INFO_EXCHANGE:w,SPEAKING_DESCRIBE_IMAGE:W,SPONTANEOUS_QA:h,READ_DISPLAYED_CONTENT:H,SPEAKING_CONVERSATION:k,GN_SPEAKING_INTERVIEW:u,SPEAKING_CUE_CARD:K,WORD_FILL_STRUCTURED_FORM:U};
|
|
@@ -1,121 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useState } from 'react';
|
|
4
|
-
import { useEditor, EditorContent } from '@tiptap/react';
|
|
5
|
-
import StarterKit from '@tiptap/starter-kit';
|
|
6
|
-
import TextAlign from '@tiptap/extension-text-align';
|
|
7
|
-
import { ImagePreview } from '../../../components/ui/image-preview';
|
|
8
|
-
import { Input } from '../../../components/ui/input';
|
|
9
|
-
import { Label } from '../../../components/ui/label';
|
|
10
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../../components/ui/card';
|
|
11
|
-
import { FileUpload } from '../../../components/ui/file-upload';
|
|
12
|
-
import { ImageIcon, Layers, Type, FileText, PlusCircle, Trash2, Bold, Italic, Strikethrough, Heading1, Heading2, List, ListOrdered, AlignLeft, AlignCenter, AlignRight, Undo2, Redo2, } from 'lucide-react';
|
|
13
|
-
import { Button } from '../../../components/ui/button';
|
|
14
|
-
import { usePresignedFileUrl } from '../../../shared/lib/hooks';
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
// Toolbar helpers
|
|
17
|
-
// ---------------------------------------------------------------------------
|
|
18
|
-
function ToolbarBtn({ onClick, active, disabled, title, children, }) {
|
|
19
|
-
return (_jsx("button", { type: "button", title: title, onClick: onClick, disabled: disabled, className: `flex h-7 w-7 items-center justify-center rounded-md text-sm transition-colors
|
|
20
|
-
${active ? 'bg-teal-500 text-white shadow-sm' : 'text-gray-600 hover:bg-gray-200 hover:text-gray-900'}
|
|
21
|
-
${disabled ? 'cursor-not-allowed opacity-40' : 'cursor-pointer'}
|
|
22
|
-
`, children: children }));
|
|
23
|
-
}
|
|
24
|
-
function ToolbarSep() {
|
|
25
|
-
return _jsx("div", { className: "mx-0.5 h-5 w-px shrink-0 bg-gray-300" });
|
|
26
|
-
}
|
|
27
|
-
function ArticleEditor({ value, onChange, isReadOnly = false }) {
|
|
28
|
-
const [mounted, setMounted] = useState(false);
|
|
29
|
-
const editor = useEditor({
|
|
30
|
-
immediatelyRender: false,
|
|
31
|
-
editable: !isReadOnly,
|
|
32
|
-
extensions: [
|
|
33
|
-
StarterKit,
|
|
34
|
-
TextAlign.configure({ types: ['heading', 'paragraph'] }),
|
|
35
|
-
],
|
|
36
|
-
content: value || '',
|
|
37
|
-
onUpdate: ({ editor }) => {
|
|
38
|
-
onChange(editor.getHTML());
|
|
39
|
-
},
|
|
40
|
-
editorProps: {
|
|
41
|
-
attributes: {
|
|
42
|
-
class: [
|
|
43
|
-
'focus:outline-none min-h-[120px] max-h-[400px] overflow-y-auto p-3 text-sm text-gray-800 leading-relaxed',
|
|
44
|
-
'prose prose-sm max-w-none',
|
|
45
|
-
'[&_h1]:text-xl [&_h1]:font-bold [&_h1]:mb-2',
|
|
46
|
-
'[&_h2]:text-lg [&_h2]:font-bold [&_h2]:mb-1.5',
|
|
47
|
-
'[&_ul]:list-disc [&_ul]:ml-5 [&_ol]:list-decimal [&_ol]:ml-5',
|
|
48
|
-
'[&_li]:mb-0.5',
|
|
49
|
-
'[&_strong]:font-bold [&_em]:italic [&_s]:line-through',
|
|
50
|
-
'[&_hr]:border-gray-300 [&_hr]:my-3',
|
|
51
|
-
isReadOnly ? 'bg-gray-50 cursor-not-allowed' : 'bg-white',
|
|
52
|
-
].join(' '),
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
useEffect(() => { setMounted(true); }, []);
|
|
57
|
-
useEffect(() => {
|
|
58
|
-
if (editor && value !== undefined && value !== editor.getHTML()) {
|
|
59
|
-
setTimeout(() => editor.commands.setContent(value, { emitUpdate: false }), 0);
|
|
60
|
-
}
|
|
61
|
-
}, [value, editor]);
|
|
62
|
-
useEffect(() => {
|
|
63
|
-
editor?.setEditable(!isReadOnly);
|
|
64
|
-
}, [isReadOnly, editor]);
|
|
65
|
-
if (!mounted)
|
|
66
|
-
return null;
|
|
67
|
-
return (_jsxs("div", { className: `overflow-hidden rounded-sm border-2 transition-all ${isReadOnly
|
|
68
|
-
? 'border-gray-200 bg-gray-50'
|
|
69
|
-
: 'border-gray-200 bg-white focus-within:border-teal-400 focus-within:shadow-sm focus-within:shadow-teal-100'}`, children: [!isReadOnly && (_jsxs("div", { className: "flex flex-wrap items-center gap-1 border-b border-gray-200 bg-gray-50/80 px-2 py-1.5", children: [_jsx(ToolbarBtn, { title: "Ho\u00E0n t\u00E1c", onClick: () => editor?.chain().focus().undo().run(), disabled: !editor?.can().undo(), children: _jsx(Undo2, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarBtn, { title: "L\u00E0m l\u1EA1i", onClick: () => editor?.chain().focus().redo().run(), disabled: !editor?.can().redo(), children: _jsx(Redo2, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarSep, {}), _jsx(ToolbarBtn, { title: "Heading 1", active: editor?.isActive('heading', { level: 1 }), onClick: () => editor?.chain().focus().toggleHeading({ level: 1 }).run(), children: _jsx(Heading1, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarBtn, { title: "Heading 2", active: editor?.isActive('heading', { level: 2 }), onClick: () => editor?.chain().focus().toggleHeading({ level: 2 }).run(), children: _jsx(Heading2, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarSep, {}), _jsx(ToolbarBtn, { title: "In \u0111\u1EADm", active: editor?.isActive('bold'), onClick: () => editor?.chain().focus().toggleBold().run(), children: _jsx(Bold, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarBtn, { title: "In nghi\u00EAng", active: editor?.isActive('italic'), onClick: () => editor?.chain().focus().toggleItalic().run(), children: _jsx(Italic, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarBtn, { title: "G\u1EA1ch ngang", active: editor?.isActive('strike'), onClick: () => editor?.chain().focus().toggleStrike().run(), children: _jsx(Strikethrough, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarSep, {}), _jsx(ToolbarBtn, { title: "Danh s\u00E1ch ch\u1EA5m", active: editor?.isActive('bulletList'), onClick: () => editor?.chain().focus().toggleBulletList().run(), children: _jsx(List, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarBtn, { title: "Danh s\u00E1ch s\u1ED1", active: editor?.isActive('orderedList'), onClick: () => editor?.chain().focus().toggleOrderedList().run(), children: _jsx(ListOrdered, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarSep, {}), _jsx(ToolbarBtn, { title: "C\u0103n tr\u00E1i", active: editor?.isActive({ textAlign: 'left' }), onClick: () => editor?.chain().focus().setTextAlign('left').run(), children: _jsx(AlignLeft, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarBtn, { title: "C\u0103n gi\u1EEFa", active: editor?.isActive({ textAlign: 'center' }), onClick: () => editor?.chain().focus().setTextAlign('center').run(), children: _jsx(AlignCenter, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarBtn, { title: "C\u0103n ph\u1EA3i", active: editor?.isActive({ textAlign: 'right' }), onClick: () => editor?.chain().focus().setTextAlign('right').run(), children: _jsx(AlignRight, { className: "h-3.5 w-3.5" }) })] })), _jsx(EditorContent, { editor: editor })] }));
|
|
70
|
-
}
|
|
71
|
-
// ---------------------------------------------------------------------------
|
|
72
|
-
// Sub-component: single article image preview with presigned URL
|
|
73
|
-
// ---------------------------------------------------------------------------
|
|
74
|
-
function ArticleImagePreview({ src, alt }) {
|
|
75
|
-
const { previewUrl } = usePresignedFileUrl(src);
|
|
76
|
-
const displayUrl = previewUrl || '';
|
|
77
|
-
if (!displayUrl)
|
|
78
|
-
return null;
|
|
79
|
-
return (_jsx(ImagePreview, { src: displayUrl, alt: alt, className: "mt-2 h-40 w-full max-w-xs rounded-lg border border-gray-200" }));
|
|
80
|
-
}
|
|
81
|
-
// ---------------------------------------------------------------------------
|
|
82
|
-
// Component
|
|
83
|
-
// ---------------------------------------------------------------------------
|
|
84
|
-
/**
|
|
85
|
-
* `ArticlesGroupCard`
|
|
86
|
-
*
|
|
87
|
-
* Renders N article cards within a single group. Each article has:
|
|
88
|
-
* - name (e.g. "Ariel", "Clive", "Vicky")
|
|
89
|
-
* - content (textarea for the reading passage)
|
|
90
|
-
* - imageUrl (file upload for the person's photo)
|
|
91
|
-
*
|
|
92
|
-
* Used for question types that require multiple reading passages per group,
|
|
93
|
-
* such as KEY_FOR_SCHOOLS Reading Part 2.
|
|
94
|
-
*/
|
|
95
|
-
export function ArticlesGroupCard({ groupNumber, articleCount, articles, onArticlesChange, title = '', onTitleChange, isReadOnly = false, uploadIdPrefix = 'articles-group', }) {
|
|
96
|
-
// Use the articles array directly. If empty, fallback to articleCount
|
|
97
|
-
const displayArticles = articles.length > 0
|
|
98
|
-
? articles
|
|
99
|
-
: Array.from({ length: articleCount }, () => ({ name: '', content: '', imageUrl: '' }));
|
|
100
|
-
const handleFieldChange = (index, field, value) => {
|
|
101
|
-
if (isReadOnly)
|
|
102
|
-
return;
|
|
103
|
-
const updated = displayArticles.map((article, i) => i === index ? { ...article, [field]: value } : article);
|
|
104
|
-
onArticlesChange(updated);
|
|
105
|
-
};
|
|
106
|
-
const handleAddArticle = () => {
|
|
107
|
-
if (isReadOnly)
|
|
108
|
-
return;
|
|
109
|
-
onArticlesChange([...displayArticles, { name: '', content: '', imageUrl: '' }]);
|
|
110
|
-
};
|
|
111
|
-
const handleDeleteArticle = (index) => {
|
|
112
|
-
if (isReadOnly)
|
|
113
|
-
return;
|
|
114
|
-
const updated = displayArticles.filter((_, i) => i !== index);
|
|
115
|
-
onArticlesChange(updated);
|
|
116
|
-
};
|
|
117
|
-
return (_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50", children: [_jsx("div", { className: "h-1.5 bg-gradient-to-r from-teal-500 via-emerald-500 to-green-500" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-teal-500 to-emerald-600 shadow-md", children: _jsx(Layers, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-lg font-bold text-gray-900", children: "B\u00E0i vi\u1EBFt nh\u00F3m" }), _jsxs("span", { className: "inline-flex items-center rounded-full border border-teal-300 bg-teal-100 px-2.5 py-0.5 text-xs font-semibold text-teal-700", children: ["Nh\u00F3m ", groupNumber] }), _jsxs("span", { className: "inline-flex items-center rounded-full border border-emerald-200 bg-emerald-50 px-2.5 py-0.5 text-xs font-medium text-emerald-600", children: [articles.length || articleCount, " b\u00E0i vi\u1EBFt"] })] }), _jsxs("p", { className: "text-sm text-gray-500", children: ["Nh\u1EADp n\u1ED9i dung t\u1EEBng b\u00E0i vi\u1EBFt \u2014 d\u00F9ng chung cho t\u1EA5t c\u1EA3 c\u00E2u h\u1ECFi trong Nh\u00F3m ", groupNumber, ". B\u1EA1n c\u00F3 th\u1EC3 th\u00EAm ho\u1EB7c x\u00F3a b\u00E0i vi\u1EBFt."] })] })] }) }), _jsxs(CardContent, { className: "space-y-5", children: [_jsxs("div", { className: "space-y-2 pb-2", children: [_jsxs(Label, { htmlFor: `${uploadIdPrefix}-title`, className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(Type, { className: "h-4 w-4 text-teal-500" }), "Ti\u00EAu \u0111\u1EC1 nh\u00F3m b\u00E0i vi\u1EBFt", ' ', _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(T\u00F9y ch\u1ECDn)" })] }), _jsx(Input, { id: `${uploadIdPrefix}-title`, value: title, onChange: (e) => onTitleChange?.(e.target.value), placeholder: "Nh\u1EADp ti\u00EAu \u0111\u1EC1 chung cho nh\u00F3m b\u00E0i vi\u1EBFt\u2026", readOnly: isReadOnly, className: `border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200 ${isReadOnly ? 'bg-gray-50 text-gray-500 cursor-not-allowed border-transparent focus:border-transparent focus:ring-0' : ''}` })] }), displayArticles.map((article, index) => {
|
|
118
|
-
const letter = String.fromCharCode(65 + index); // A, B, C...
|
|
119
|
-
return (_jsxs("div", { className: "rounded-xl border-2 border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4 transition-all hover:border-teal-200 hover:shadow-sm", children: [_jsxs("div", { className: "mb-3 flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-teal-500 to-emerald-600 text-sm font-bold text-white shadow-sm", children: letter }), _jsxs("span", { className: "text-sm font-semibold text-gray-700", children: ["B\u00E0i vi\u1EBFt ", index + 1] })] }), displayArticles.length > 1 && !isReadOnly && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => handleDeleteArticle(index), className: "h-8 w-8 p-0 text-red-500 hover:bg-red-50 hover:text-red-600", title: "X\u00F3a b\u00E0i vi\u1EBFt", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { htmlFor: `${uploadIdPrefix}-name-${index}`, className: "flex items-center gap-1.5 text-sm font-medium text-gray-700", children: [_jsx(Type, { className: "h-3.5 w-3.5 text-teal-500" }), "T\u00EAn ng\u01B0\u1EDDi / ti\u00EAu \u0111\u1EC1"] }), _jsx(Input, { id: `${uploadIdPrefix}-name-${index}`, value: article.name, onChange: (e) => handleFieldChange(index, 'name', e.target.value), placeholder: `VD: Ariel, Clive, Vicky...`, readOnly: isReadOnly, className: `border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200 ${isReadOnly ? 'bg-gray-50 text-gray-500 cursor-not-allowed border-transparent focus:border-transparent focus:ring-0' : ''}` })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { className: "flex items-center gap-1.5 text-sm font-medium text-gray-700", children: [_jsx(FileText, { className: "h-3.5 w-3.5 text-teal-500" }), "N\u1ED9i dung b\u00E0i vi\u1EBFt"] }), _jsx(ArticleEditor, { value: article.content, onChange: (html) => handleFieldChange(index, 'content', html), isReadOnly: isReadOnly })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { className: "flex items-center gap-1.5 text-sm font-medium text-gray-700", children: [_jsx(ImageIcon, { className: "h-3.5 w-3.5 text-teal-500" }), "H\u00ECnh \u1EA3nh", ' ', _jsx("span", { className: "text-xs font-normal text-gray-400", children: "(T\u00F9y ch\u1ECDn)" })] }), !isReadOnly && (_jsx(FileUpload, { id: `${uploadIdPrefix}-image-${index}`, label: "", accept: "image/*", value: article.imageUrl, onChange: (url) => handleFieldChange(index, 'imageUrl', url), maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" })), article.imageUrl && (_jsx(ArticleImagePreview, { src: article.imageUrl, alt: `Article ${letter} image` }))] })] })] }, index));
|
|
120
|
-
}), !isReadOnly && (_jsxs(Button, { type: "button", variant: "outline", className: "w-full border-dashed border-teal-300 bg-teal-50/50 text-teal-700 hover:bg-teal-50 hover:text-teal-800 hover:border-teal-400", onClick: handleAddArticle, children: [_jsx(PlusCircle, { className: "mr-2 h-4 w-4" }), "Th\u00EAm b\u00E0i vi\u1EBFt"] }))] })] }));
|
|
121
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useEffect as a,useState as i}from"react";import{useEditor as r,EditorContent as l}from"@tiptap/react";import n from"@tiptap/starter-kit";import s from"@tiptap/extension-text-align";import{ImagePreview as o}from"../../../components/ui/image-preview";import{Input as c}from"../../../components/ui/input";import{Label as d}from"../../../components/ui/label";import{Card as m,CardContent as h,CardHeader as g,CardTitle as u}from"../../../components/ui/card";import{FileUpload as p}from"../../../components/ui/file-upload";import{ImageIcon as f,Layers as x,Type as b,FileText as v,PlusCircle as N,Trash2 as y,Bold as w,Italic as C,Strikethrough as k,Heading1 as A,Heading2 as _,List as T,ListOrdered as U,AlignLeft as $,AlignCenter as j,AlignRight as H,Undo2 as L,Redo2 as O}from"lucide-react";import{Button as B}from"../../../components/ui/button";import{usePresignedFileUrl as R}from"../../../shared/lib/hooks";function I({onClick:t,active:a,disabled:i,title:r,children:l}){return e("button",{type:"button",title:r,onClick:t,disabled:i,className:`flex h-7 w-7 items-center justify-center rounded-md text-sm transition-colors\n ${a?"bg-teal-500 text-white shadow-sm":"text-gray-600 hover:bg-gray-200 hover:text-gray-900"}\n ${i?"cursor-not-allowed opacity-40":"cursor-pointer"}\n `,children:l})}function D(){return e("div",{className:"mx-0.5 h-5 w-px shrink-0 bg-gray-300"})}function S({value:o,onChange:c,isReadOnly:d=!1}){const[m,h]=i(!1),g=r({immediatelyRender:!1,editable:!d,extensions:[n,s.configure({types:["heading","paragraph"]})],content:o||"",onUpdate:({editor:e})=>{c(e.getHTML())},editorProps:{attributes:{class:["focus:outline-none min-h-[120px] max-h-[400px] overflow-y-auto p-3 text-sm text-gray-800 leading-relaxed","prose prose-sm max-w-none","[&_h1]:text-xl [&_h1]:font-bold [&_h1]:mb-2","[&_h2]:text-lg [&_h2]:font-bold [&_h2]:mb-1.5","[&_ul]:list-disc [&_ul]:ml-5 [&_ol]:list-decimal [&_ol]:ml-5","[&_li]:mb-0.5","[&_strong]:font-bold [&_em]:italic [&_s]:line-through","[&_hr]:border-gray-300 [&_hr]:my-3",d?"bg-gray-50 cursor-not-allowed":"bg-white"].join(" ")}}});return a(()=>{h(!0)},[]),a(()=>{g&&void 0!==o&&o!==g.getHTML()&&setTimeout(()=>g.commands.setContent(o,{emitUpdate:!1}),0)},[o,g]),a(()=>{g?.setEditable(!d)},[d,g]),m?t("div",{className:"overflow-hidden rounded-sm border-2 transition-all "+(d?"border-gray-200 bg-gray-50":"border-gray-200 bg-white focus-within:border-teal-400 focus-within:shadow-sm focus-within:shadow-teal-100"),children:[!d&&t("div",{className:"flex flex-wrap items-center gap-1 border-b border-gray-200 bg-gray-50/80 px-2 py-1.5",children:[e(I,{title:"Hoàn tác",onClick:()=>g?.chain().focus().undo().run(),disabled:!g?.can().undo(),children:e(L,{className:"h-3.5 w-3.5"})}),e(I,{title:"Làm lại",onClick:()=>g?.chain().focus().redo().run(),disabled:!g?.can().redo(),children:e(O,{className:"h-3.5 w-3.5"})}),e(D,{}),e(I,{title:"Heading 1",active:g?.isActive("heading",{level:1}),onClick:()=>g?.chain().focus().toggleHeading({level:1}).run(),children:e(A,{className:"h-3.5 w-3.5"})}),e(I,{title:"Heading 2",active:g?.isActive("heading",{level:2}),onClick:()=>g?.chain().focus().toggleHeading({level:2}).run(),children:e(_,{className:"h-3.5 w-3.5"})}),e(D,{}),e(I,{title:"In đậm",active:g?.isActive("bold"),onClick:()=>g?.chain().focus().toggleBold().run(),children:e(w,{className:"h-3.5 w-3.5"})}),e(I,{title:"In nghiêng",active:g?.isActive("italic"),onClick:()=>g?.chain().focus().toggleItalic().run(),children:e(C,{className:"h-3.5 w-3.5"})}),e(I,{title:"Gạch ngang",active:g?.isActive("strike"),onClick:()=>g?.chain().focus().toggleStrike().run(),children:e(k,{className:"h-3.5 w-3.5"})}),e(D,{}),e(I,{title:"Danh sách chấm",active:g?.isActive("bulletList"),onClick:()=>g?.chain().focus().toggleBulletList().run(),children:e(T,{className:"h-3.5 w-3.5"})}),e(I,{title:"Danh sách số",active:g?.isActive("orderedList"),onClick:()=>g?.chain().focus().toggleOrderedList().run(),children:e(U,{className:"h-3.5 w-3.5"})}),e(D,{}),e(I,{title:"Căn trái",active:g?.isActive({textAlign:"left"}),onClick:()=>g?.chain().focus().setTextAlign("left").run(),children:e($,{className:"h-3.5 w-3.5"})}),e(I,{title:"Căn giữa",active:g?.isActive({textAlign:"center"}),onClick:()=>g?.chain().focus().setTextAlign("center").run(),children:e(j,{className:"h-3.5 w-3.5"})}),e(I,{title:"Căn phải",active:g?.isActive({textAlign:"right"}),onClick:()=>g?.chain().focus().setTextAlign("right").run(),children:e(H,{className:"h-3.5 w-3.5"})})]}),e(l,{editor:g})]}):null}function z({src:t,alt:a}){const{previewUrl:i}=R(t),r=i||"";return r?e(o,{src:r,alt:a,className:"mt-2 h-40 w-full max-w-xs rounded-lg border border-gray-200"}):null}export function ArticlesGroupCard({groupNumber:a,articleCount:i,articles:r,onArticlesChange:l,title:n="",onTitleChange:s,isReadOnly:o=!1,uploadIdPrefix:w="articles-group"}){const C=r.length>0?r:Array.from({length:i},()=>({name:"",content:"",imageUrl:""})),k=(e,t,a)=>{if(o)return;const i=C.map((i,r)=>r===e?{...i,[t]:a}:i);l(i)};return t(m,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-teal-100/50",children:[e("div",{className:"h-1.5 bg-gradient-to-r from-teal-500 via-emerald-500 to-green-500"}),e(g,{className:"pb-4",children:t("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-teal-500 to-emerald-600 shadow-md",children:e(x,{className:"h-5 w-5 text-white"})}),t("div",{className:"flex-1",children:[t("div",{className:"flex items-center gap-2",children:[e(u,{className:"text-lg font-bold text-gray-900",children:"Bài viết nhóm"}),t("span",{className:"inline-flex items-center rounded-full border border-teal-300 bg-teal-100 px-2.5 py-0.5 text-xs font-semibold text-teal-700",children:["Nhóm ",a]}),t("span",{className:"inline-flex items-center rounded-full border border-emerald-200 bg-emerald-50 px-2.5 py-0.5 text-xs font-medium text-emerald-600",children:[r.length||i," bài viết"]})]}),t("p",{className:"text-sm text-gray-500",children:["Nhập nội dung từng bài viết — dùng chung cho tất cả câu hỏi trong Nhóm ",a,". Bạn có thể thêm hoặc xóa bài viết."]})]})]})}),t(h,{className:"space-y-5",children:[t("div",{className:"space-y-2 pb-2",children:[t(d,{htmlFor:`${w}-title`,className:"flex items-center gap-2 text-sm font-semibold text-gray-800",children:[e(b,{className:"h-4 w-4 text-teal-500"}),"Tiêu đề nhóm bài viết"," ",e("span",{className:"text-xs font-normal text-gray-500",children:"(Tùy chọn)"})]}),e(c,{id:`${w}-title`,value:n,onChange:e=>s?.(e.target.value),placeholder:"Nhập tiêu đề chung cho nhóm bài viết…",readOnly:o,className:"border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200 "+(o?"bg-gray-50 text-gray-500 cursor-not-allowed border-transparent focus:border-transparent focus:ring-0":"")})]}),C.map((a,i)=>{const r=String.fromCharCode(65+i);return t("div",{className:"rounded-xl border-2 border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4 transition-all hover:border-teal-200 hover:shadow-sm",children:[t("div",{className:"mb-3 flex items-center justify-between",children:[t("div",{className:"flex items-center gap-2",children:[e("div",{className:"flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-teal-500 to-emerald-600 text-sm font-bold text-white shadow-sm",children:r}),t("span",{className:"text-sm font-semibold text-gray-700",children:["Bài viết ",i+1]})]}),C.length>1&&!o&&e(B,{type:"button",variant:"ghost",size:"sm",onClick:()=>(e=>{if(o)return;const t=C.filter((t,a)=>a!==e);l(t)})(i),className:"h-8 w-8 p-0 text-red-500 hover:bg-red-50 hover:text-red-600",title:"Xóa bài viết",children:e(y,{className:"h-4 w-4"})})]}),t("div",{className:"space-y-3",children:[t("div",{className:"space-y-1.5",children:[t(d,{htmlFor:`${w}-name-${i}`,className:"flex items-center gap-1.5 text-sm font-medium text-gray-700",children:[e(b,{className:"h-3.5 w-3.5 text-teal-500"}),"Tên người / tiêu đề"]}),e(c,{id:`${w}-name-${i}`,value:a.name,onChange:e=>k(i,"name",e.target.value),placeholder:"VD: Ariel, Clive, Vicky...",readOnly:o,className:"border-gray-200 bg-white transition-all focus:border-teal-300 focus:ring-teal-200 "+(o?"bg-gray-50 text-gray-500 cursor-not-allowed border-transparent focus:border-transparent focus:ring-0":"")})]}),t("div",{className:"space-y-1.5",children:[t(d,{className:"flex items-center gap-1.5 text-sm font-medium text-gray-700",children:[e(v,{className:"h-3.5 w-3.5 text-teal-500"}),"Nội dung bài viết"]}),e(S,{value:a.content,onChange:e=>k(i,"content",e),isReadOnly:o})]}),t("div",{className:"space-y-1.5",children:[t(d,{className:"flex items-center gap-1.5 text-sm font-medium text-gray-700",children:[e(f,{className:"h-3.5 w-3.5 text-teal-500"}),"Hình ảnh"," ",e("span",{className:"text-xs font-normal text-gray-400",children:"(Tùy chọn)"})]}),!o&&e(p,{id:`${w}-image-${i}`,label:"",accept:"image/*",value:a.imageUrl,onChange:e=>k(i,"imageUrl",e),maxSize:5,placeholder:"Upload ảnh hoặc paste URL",autoUpload:!0,prefix:"questions"}),a.imageUrl&&e(z,{src:a.imageUrl,alt:`Article ${r} image`})]})]})]},i)}),!o&&t(B,{type:"button",variant:"outline",className:"w-full border-dashed border-teal-300 bg-teal-50/50 text-teal-700 hover:bg-teal-50 hover:text-teal-800 hover:border-teal-400",onClick:()=>{o||l([...C,{name:"",content:"",imageUrl:""}])},children:[e(N,{className:"mr-2 h-4 w-4"}),"Thêm bài viết"]})]})]})}
|
|
@@ -1,50 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { ImagePreview } from '../../../components/ui/image-preview';
|
|
4
|
-
import { Input } from '../../../components/ui/input';
|
|
5
|
-
import { Label } from '../../../components/ui/label';
|
|
6
|
-
import { Textarea } from '../../../components/ui/textarea';
|
|
7
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../../components/ui/card';
|
|
8
|
-
import { FileUpload } from '../../../components/ui/file-upload';
|
|
9
|
-
import { ImageIcon, Layers, MessageSquare, Type } from 'lucide-react';
|
|
10
|
-
import { usePresignedFileUrl } from '../../../shared/lib/hooks';
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
// Component
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
/**
|
|
15
|
-
* `BasicQuestionGroupCard`
|
|
16
|
-
*
|
|
17
|
-
* The shared group header card used by:
|
|
18
|
-
* - ANSWER_THE_QUESTION
|
|
19
|
-
* - CHOOSE_THE_CORRECT_ANSWER
|
|
20
|
-
* - FILL_IN_BLANK
|
|
21
|
-
*
|
|
22
|
-
* It renders three optional fields that are **shared across all questions in
|
|
23
|
-
* the same group**: a title, a content/passage textarea, and an image upload.
|
|
24
|
-
*
|
|
25
|
-
* This component is **purely presentational** – all state lives in the parent
|
|
26
|
-
* (managed by `useBasicQuestionGroup`).
|
|
27
|
-
*/
|
|
28
|
-
export function BasicQuestionGroupCard({ groupNumber, title, subtitle, content, imageUrl, onTitleChange, onSubtitleChange, onContentChange, onImageChange, onImagePresignedUrlChange, showContent = true, groupConfig, uploadIdPrefix = 'basic-group', isReadOnly = false, }) {
|
|
29
|
-
const hasConfig = groupConfig && groupConfig.length > 0;
|
|
30
|
-
const showTitle = !hasConfig || groupConfig.includes('title');
|
|
31
|
-
const showSubtitle = !hasConfig || groupConfig.includes('subtitle');
|
|
32
|
-
const showContentField = (!hasConfig || groupConfig.includes('content')) && showContent;
|
|
33
|
-
const showImage = !hasConfig || groupConfig.includes('imageUrl');
|
|
34
|
-
// Resolve presigned preview URL for the stored image key
|
|
35
|
-
const { previewUrl } = usePresignedFileUrl(imageUrl);
|
|
36
|
-
const displayPreviewUrl = previewUrl || '';
|
|
37
|
-
return (_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-purple-100/50", children: [_jsx("div", { className: "h-1.5 bg-gradient-to-r from-purple-500 via-violet-500 to-indigo-500" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-purple-500 to-violet-600 shadow-md", children: _jsx(Layers, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-lg font-bold text-gray-900", children: "D\u1EEF li\u1EC7u nh\u00F3m" }), _jsxs("span", { className: "inline-flex items-center rounded-full border border-purple-300 bg-purple-100 px-2.5 py-0.5 text-xs font-semibold text-purple-700", children: ["Nh\u00F3m ", groupNumber] })] }), _jsxs("p", { className: "text-sm text-gray-500", children: ["D\u00F9ng chung cho t\u1EA5t c\u1EA3 c\u00E2u h\u1ECFi trong Nh\u00F3m ", groupNumber] })] })] }) }), _jsxs(CardContent, { className: "space-y-5", children: [showTitle && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: `${uploadIdPrefix}-title`, className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(Type, { className: "h-4 w-4 text-purple-500" }), "Ti\u00EAu \u0111\u1EC1 nh\u00F3m", ' ', _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(T\u00F9y ch\u1ECDn)" })] }), _jsx(Input, { id: `${uploadIdPrefix}-title`, value: title, onChange: (e) => {
|
|
38
|
-
if (!isReadOnly)
|
|
39
|
-
onTitleChange(e.target.value);
|
|
40
|
-
}, readOnly: isReadOnly, placeholder: "Nh\u1EADp ti\u00EAu \u0111\u1EC1 nh\u00F3m c\u00E2u h\u1ECFi\u2026", className: `border-gray-200 bg-white transition-all focus:border-purple-300 focus:ring-purple-200 ${isReadOnly ? 'bg-gray-50 text-gray-500 cursor-not-allowed border-transparent focus:border-transparent focus:ring-0' : ''}` })] })), showSubtitle && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: `${uploadIdPrefix}-subtitle`, className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(Type, { className: "h-4 w-4 text-purple-500" }), "Ph\u1EE5 \u0111\u1EC1 nh\u00F3m", ' ', _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(T\u00F9y ch\u1ECDn)" })] }), _jsx(Input, { id: `${uploadIdPrefix}-subtitle`, value: subtitle, onChange: (e) => {
|
|
41
|
-
if (!isReadOnly)
|
|
42
|
-
onSubtitleChange(e.target.value);
|
|
43
|
-
}, readOnly: isReadOnly, placeholder: "Nh\u1EADp ph\u1EE5 \u0111\u1EC1 nh\u00F3m c\u00E2u h\u1ECFi\u2026", className: `border-gray-200 bg-white transition-all focus:border-purple-300 focus:ring-purple-200 ${isReadOnly ? 'bg-gray-50 text-gray-500 cursor-not-allowed border-transparent focus:border-transparent focus:ring-0' : ''}` })] })), showContentField && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: `${uploadIdPrefix}-content`, className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-4 w-4 text-purple-500" }), "N\u1ED9i dung nh\u00F3m", ' ', _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(T\u00F9y ch\u1ECDn)" })] }), _jsx(Textarea, { id: `${uploadIdPrefix}-content`, value: content, onChange: (e) => {
|
|
44
|
-
if (!isReadOnly)
|
|
45
|
-
onContentChange(e.target.value);
|
|
46
|
-
}, readOnly: isReadOnly, placeholder: "Nh\u1EADp \u0111o\u1EA1n v\u0103n / c\u00E2u chuy\u1EC7n chung cho nh\u00F3m c\u00E2u h\u1ECFi n\u00E0y\u2026", rows: 5, className: `min-h-[120px] border-gray-200 bg-white transition-all focus:border-purple-300 focus:ring-purple-200 ${isReadOnly ? 'bg-gray-50 text-gray-500 cursor-not-allowed border-transparent focus:border-transparent focus:ring-0' : ''}` })] })), showImage && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-purple-500" }), "H\u00ECnh \u1EA3nh nh\u00F3m", ' ', _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(T\u00F9y ch\u1ECDn)" })] }), !isReadOnly && (_jsx(FileUpload, { id: `${uploadIdPrefix}-image`, label: "", accept: "image/*", value: imageUrl, onChange: (url) => {
|
|
47
|
-
if (!isReadOnly)
|
|
48
|
-
onImageChange(url);
|
|
49
|
-
}, onPresignedUrlChange: onImagePresignedUrlChange, maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" })), displayPreviewUrl && (_jsx(ImagePreview, { src: displayPreviewUrl, alt: "Group image preview", className: "mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200" }))] }))] })] }));
|
|
50
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{ImagePreview as t}from"../../../components/ui/image-preview";import{Input as a}from"../../../components/ui/input";import{Label as n}from"../../../components/ui/label";import{Textarea as l}from"../../../components/ui/textarea";import{Card as s,CardContent as o,CardHeader as i,CardTitle as c}from"../../../components/ui/card";import{FileUpload as m}from"../../../components/ui/file-upload";import{ImageIcon as d,Layers as p,MessageSquare as h,Type as u}from"lucide-react";import{usePresignedFileUrl as g}from"../../../shared/lib/hooks";export function BasicQuestionGroupCard({groupNumber:x,title:f,subtitle:b,content:N,imageUrl:y,onTitleChange:w,onSubtitleChange:v,onContentChange:C,onImageChange:U,onImagePresignedUrlChange:$,showContent:T=!0,groupConfig:j,uploadIdPrefix:O="basic-group",isReadOnly:P=!1}){const F=j&&j.length>0,I=!F||j.includes("title"),k=!F||j.includes("subtitle"),D=(!F||j.includes("content"))&&T,G=!F||j.includes("imageUrl"),{previewUrl:R}=g(y),S=R||"";return r(s,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-purple-100/50",children:[e("div",{className:"h-1.5 bg-gradient-to-r from-purple-500 via-violet-500 to-indigo-500"}),e(i,{className:"pb-4",children:r("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-purple-500 to-violet-600 shadow-md",children:e(p,{className:"h-5 w-5 text-white"})}),r("div",{className:"flex-1",children:[r("div",{className:"flex items-center gap-2",children:[e(c,{className:"text-lg font-bold text-gray-900",children:"Dữ liệu nhóm"}),r("span",{className:"inline-flex items-center rounded-full border border-purple-300 bg-purple-100 px-2.5 py-0.5 text-xs font-semibold text-purple-700",children:["Nhóm ",x]})]}),r("p",{className:"text-sm text-gray-500",children:["Dùng chung cho tất cả câu hỏi trong Nhóm ",x]})]})]})}),r(o,{className:"space-y-5",children:[I&&r("div",{className:"space-y-2",children:[r(n,{htmlFor:`${O}-title`,className:"flex items-center gap-2 text-sm font-semibold text-gray-800",children:[e(u,{className:"h-4 w-4 text-purple-500"}),"Tiêu đề nhóm"," ",e("span",{className:"text-xs font-normal text-gray-500",children:"(Tùy chọn)"})]}),e(a,{id:`${O}-title`,value:f,onChange:e=>{P||w(e.target.value)},readOnly:P,placeholder:"Nhập tiêu đề nhóm câu hỏi…",className:"border-gray-200 bg-white transition-all focus:border-purple-300 focus:ring-purple-200 "+(P?"bg-gray-50 text-gray-500 cursor-not-allowed border-transparent focus:border-transparent focus:ring-0":"")})]}),k&&r("div",{className:"space-y-2",children:[r(n,{htmlFor:`${O}-subtitle`,className:"flex items-center gap-2 text-sm font-semibold text-gray-800",children:[e(u,{className:"h-4 w-4 text-purple-500"}),"Phụ đề nhóm"," ",e("span",{className:"text-xs font-normal text-gray-500",children:"(Tùy chọn)"})]}),e(a,{id:`${O}-subtitle`,value:b,onChange:e=>{P||v(e.target.value)},readOnly:P,placeholder:"Nhập phụ đề nhóm câu hỏi…",className:"border-gray-200 bg-white transition-all focus:border-purple-300 focus:ring-purple-200 "+(P?"bg-gray-50 text-gray-500 cursor-not-allowed border-transparent focus:border-transparent focus:ring-0":"")})]}),D&&r("div",{className:"space-y-2",children:[r(n,{htmlFor:`${O}-content`,className:"flex items-center gap-2 text-sm font-semibold text-gray-800",children:[e(h,{className:"h-4 w-4 text-purple-500"}),"Nội dung nhóm"," ",e("span",{className:"text-xs font-normal text-gray-500",children:"(Tùy chọn)"})]}),e(l,{id:`${O}-content`,value:N,onChange:e=>{P||C(e.target.value)},readOnly:P,placeholder:"Nhập đoạn văn / câu chuyện chung cho nhóm câu hỏi này…",rows:5,className:"min-h-[120px] border-gray-200 bg-white transition-all focus:border-purple-300 focus:ring-purple-200 "+(P?"bg-gray-50 text-gray-500 cursor-not-allowed border-transparent focus:border-transparent focus:ring-0":"")})]}),G&&r("div",{className:"space-y-2",children:[r(n,{className:"flex items-center gap-2 text-sm font-semibold text-gray-800",children:[e(d,{className:"h-4 w-4 text-purple-500"}),"Hình ảnh nhóm"," ",e("span",{className:"text-xs font-normal text-gray-500",children:"(Tùy chọn)"})]}),!P&&e(m,{id:`${O}-image`,label:"",accept:"image/*",value:y,onChange:e=>{P||U(e)},onPresignedUrlChange:$,maxSize:5,placeholder:"Upload ảnh hoặc paste URL",autoUpload:!0,prefix:"questions"}),S&&e(t,{src:S,alt:"Group image preview",className:"mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200"})]})]})]})}
|
|
@@ -1,114 +1 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useState } from 'react';
|
|
4
|
-
import { useEditor, EditorContent } from '@tiptap/react';
|
|
5
|
-
import StarterKit from '@tiptap/starter-kit';
|
|
6
|
-
import TextAlign from '@tiptap/extension-text-align';
|
|
7
|
-
import { ImagePreview } from '../../../components/ui/image-preview';
|
|
8
|
-
import { Input } from '../../../components/ui/input';
|
|
9
|
-
import { Label } from '../../../components/ui/label';
|
|
10
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../../components/ui/card';
|
|
11
|
-
import { FileUpload } from '../../../components/ui/file-upload';
|
|
12
|
-
import { usePresignedFileUrl } from '../../../shared/lib/hooks';
|
|
13
|
-
import { Layers, Type, Pilcrow, ImageIcon, Bold, Italic, Strikethrough, Heading1, Heading2, Heading3, List, ListOrdered, AlignLeft, AlignCenter, AlignRight, Minus, Undo2, Redo2, FileText, Info, } from 'lucide-react';
|
|
14
|
-
// ---------------------------------------------------------------------------
|
|
15
|
-
// Toolbar button
|
|
16
|
-
// ---------------------------------------------------------------------------
|
|
17
|
-
function ToolbarBtn({ onClick, active, disabled, title, children, }) {
|
|
18
|
-
return (_jsx("button", { type: "button", title: title, onClick: onClick, disabled: disabled, className: `flex h-7 w-7 items-center justify-center rounded-md text-sm transition-colors
|
|
19
|
-
${active
|
|
20
|
-
? 'bg-indigo-500 text-white shadow-sm'
|
|
21
|
-
: 'text-gray-600 hover:bg-gray-200 hover:text-gray-900'}
|
|
22
|
-
${disabled ? 'cursor-not-allowed opacity-40' : 'cursor-pointer'}
|
|
23
|
-
`, children: children }));
|
|
24
|
-
}
|
|
25
|
-
function ToolbarSep() {
|
|
26
|
-
return _jsx("div", { className: "mx-0.5 h-5 w-px shrink-0 bg-gray-300" });
|
|
27
|
-
}
|
|
28
|
-
/** Replace `{0}`, `{1}`, … placeholders with a visible blank underline */
|
|
29
|
-
function replacePlaceholdersWithBlanks(html) {
|
|
30
|
-
return html.replace(/\{(\d+)\}/g, (_match, num) => `<span style="display:inline-block;min-width:120px;padding:1px 5px;margin:0 4px;background:#eef2ff;border-bottom:2px solid #6366f1;color:#4f46e5;font-weight:700;font-size:0.85em;text-align:center;vertical-align:baseline;letter-spacing:2px">———(${Number(num) + 1})———</span>`);
|
|
31
|
-
}
|
|
32
|
-
function DocumentEditor({ value, onChange, isReadOnly = false, placeholder }) {
|
|
33
|
-
const [mounted, setMounted] = useState(false);
|
|
34
|
-
const editor = useEditor({
|
|
35
|
-
immediatelyRender: false,
|
|
36
|
-
editable: !isReadOnly,
|
|
37
|
-
extensions: [
|
|
38
|
-
StarterKit,
|
|
39
|
-
TextAlign.configure({ types: ['heading', 'paragraph'] }),
|
|
40
|
-
],
|
|
41
|
-
// Always use raw value — never inject replaced HTML into the editor
|
|
42
|
-
content: value || '',
|
|
43
|
-
onUpdate: ({ editor }) => {
|
|
44
|
-
onChange(editor.getHTML());
|
|
45
|
-
},
|
|
46
|
-
editorProps: {
|
|
47
|
-
attributes: {
|
|
48
|
-
class: [
|
|
49
|
-
'focus:outline-none min-h-[200px] max-h-[520px] overflow-y-auto p-4 text-sm text-gray-800 leading-relaxed',
|
|
50
|
-
'prose prose-sm max-w-none',
|
|
51
|
-
'[&_h1]:text-xl [&_h1]:font-bold [&_h1]:mb-2',
|
|
52
|
-
'[&_h2]:text-lg [&_h2]:font-bold [&_h2]:mb-1.5',
|
|
53
|
-
'[&_h3]:text-base [&_h3]:font-semibold [&_h3]:mb-1',
|
|
54
|
-
'[&_ul]:list-disc [&_ul]:ml-5 [&_ol]:list-decimal [&_ol]:ml-5',
|
|
55
|
-
'[&_li]:mb-0.5',
|
|
56
|
-
'[&_strong]:font-bold [&_em]:italic [&_s]:line-through',
|
|
57
|
-
'[&_hr]:border-gray-300 [&_hr]:my-3',
|
|
58
|
-
'bg-white',
|
|
59
|
-
].join(' '),
|
|
60
|
-
...(placeholder && !value ? { 'data-placeholder': placeholder } : {}),
|
|
61
|
-
},
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
useEffect(() => { setMounted(true); }, []);
|
|
65
|
-
useEffect(() => {
|
|
66
|
-
if (editor && value !== undefined && value !== editor.getHTML()) {
|
|
67
|
-
// Always sync raw value — replacement is handled at render time for read-only display
|
|
68
|
-
setTimeout(() => editor.commands.setContent(value, { emitUpdate: false }), 0);
|
|
69
|
-
}
|
|
70
|
-
}, [value, editor]);
|
|
71
|
-
useEffect(() => {
|
|
72
|
-
editor?.setEditable(!isReadOnly);
|
|
73
|
-
}, [isReadOnly, editor]);
|
|
74
|
-
if (!mounted)
|
|
75
|
-
return null;
|
|
76
|
-
return (_jsxs("div", { className: `overflow-hidden rounded-sm border-2 transition-all ${isReadOnly
|
|
77
|
-
? 'border-gray-200'
|
|
78
|
-
: 'border-gray-200 focus-within:border-indigo-400 focus-within:ring-2 focus-within:ring-indigo-100'}`, children: [!isReadOnly && (_jsxs("div", { className: "flex flex-wrap items-center gap-0.5 border-b border-gray-200 bg-gray-50 px-2 py-1.5", children: [_jsx(ToolbarBtn, { title: "Ho\u00E0n t\u00E1c", onClick: () => editor?.chain().focus().undo().run(), disabled: !editor?.can().undo(), children: _jsx(Undo2, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarBtn, { title: "L\u00E0m l\u1EA1i", onClick: () => editor?.chain().focus().redo().run(), disabled: !editor?.can().redo(), children: _jsx(Redo2, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarSep, {}), _jsx(ToolbarBtn, { title: "Ti\u00EAu \u0111\u1EC1 1", active: editor?.isActive('heading', { level: 1 }), onClick: () => editor?.chain().focus().toggleHeading({ level: 1 }).run(), children: _jsx(Heading1, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarBtn, { title: "Ti\u00EAu \u0111\u1EC1 2", active: editor?.isActive('heading', { level: 2 }), onClick: () => editor?.chain().focus().toggleHeading({ level: 2 }).run(), children: _jsx(Heading2, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarBtn, { title: "Ti\u00EAu \u0111\u1EC1 3", active: editor?.isActive('heading', { level: 3 }), onClick: () => editor?.chain().focus().toggleHeading({ level: 3 }).run(), children: _jsx(Heading3, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarSep, {}), _jsx(ToolbarBtn, { title: "In \u0111\u1EADm", active: editor?.isActive('bold'), onClick: () => editor?.chain().focus().toggleBold().run(), children: _jsx(Bold, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarBtn, { title: "In nghi\u00EAng", active: editor?.isActive('italic'), onClick: () => editor?.chain().focus().toggleItalic().run(), children: _jsx(Italic, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarBtn, { title: "G\u1EA1ch ngang", active: editor?.isActive('strike'), onClick: () => editor?.chain().focus().toggleStrike().run(), children: _jsx(Strikethrough, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarSep, {}), _jsx(ToolbarBtn, { title: "Danh s\u00E1ch", active: editor?.isActive('bulletList'), onClick: () => editor?.chain().focus().toggleBulletList().run(), children: _jsx(List, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarBtn, { title: "Danh s\u00E1ch s\u1ED1", active: editor?.isActive('orderedList'), onClick: () => editor?.chain().focus().toggleOrderedList().run(), children: _jsx(ListOrdered, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarSep, {}), _jsx(ToolbarBtn, { title: "C\u0103n tr\u00E1i", active: editor?.isActive({ textAlign: 'left' }), onClick: () => editor?.chain().focus().setTextAlign('left').run(), children: _jsx(AlignLeft, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarBtn, { title: "C\u0103n gi\u1EEFa", active: editor?.isActive({ textAlign: 'center' }), onClick: () => editor?.chain().focus().setTextAlign('center').run(), children: _jsx(AlignCenter, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarBtn, { title: "C\u0103n ph\u1EA3i", active: editor?.isActive({ textAlign: 'right' }), onClick: () => editor?.chain().focus().setTextAlign('right').run(), children: _jsx(AlignRight, { className: "h-3.5 w-3.5" }) }), _jsx(ToolbarSep, {}), _jsx(ToolbarBtn, { title: "\u0110\u01B0\u1EDDng k\u1EBB ngang", onClick: () => editor?.chain().focus().setHorizontalRule().run(), children: _jsx(Minus, { className: "h-3.5 w-3.5" }) })] })), isReadOnly ? (
|
|
79
|
-
/* Read-only display: render replaced HTML but never push it into the editor */
|
|
80
|
-
_jsx("div", { className: [
|
|
81
|
-
'min-h-[120px] max-h-[520px] overflow-y-auto p-4 text-sm text-gray-800 leading-relaxed bg-gray-50 cursor-not-allowed',
|
|
82
|
-
'prose prose-sm max-w-none',
|
|
83
|
-
'[&_h1]:text-xl [&_h1]:font-bold [&_h1]:mb-2',
|
|
84
|
-
'[&_h2]:text-lg [&_h2]:font-bold [&_h2]:mb-1.5',
|
|
85
|
-
'[&_h3]:text-base [&_h3]:font-semibold [&_h3]:mb-1',
|
|
86
|
-
'[&_ul]:list-disc [&_ul]:ml-5 [&_ol]:list-decimal [&_ol]:ml-5',
|
|
87
|
-
'[&_li]:mb-0.5',
|
|
88
|
-
'[&_strong]:font-bold [&_em]:italic [&_s]:line-through',
|
|
89
|
-
'[&_hr]:border-gray-300 [&_hr]:my-3',
|
|
90
|
-
].join(' '), dangerouslySetInnerHTML: { __html: replacePlaceholdersWithBlanks(value || '') } })) : (_jsx(EditorContent, { editor: editor }))] }));
|
|
91
|
-
}
|
|
92
|
-
// ---------------------------------------------------------------------------
|
|
93
|
-
// Component
|
|
94
|
-
// ---------------------------------------------------------------------------
|
|
95
|
-
/**
|
|
96
|
-
* `DocumentGroupCard`
|
|
97
|
-
*
|
|
98
|
-
* Group header card that uses TipTap for structured rich-text content.
|
|
99
|
-
* Ideal for TOEIC Part 6 / Part 7 reading passages, emails, memos,
|
|
100
|
-
* advertisements and other formatted texts shared across a question group.
|
|
101
|
-
*/
|
|
102
|
-
export function DocumentGroupCard({ groupNumber, title, subtitle = '', content, imageUrl, onTitleChange, onSubtitleChange, onContentChange, onImageChange, onImagePresignedUrlChange, groupConfig, uploadIdPrefix = 'document-group', isReadOnly = false, }) {
|
|
103
|
-
const hasConfig = groupConfig && groupConfig.length > 0;
|
|
104
|
-
const showTitle = !hasConfig || groupConfig.includes('title');
|
|
105
|
-
const showSubtitle = !hasConfig || groupConfig.includes('subtitle');
|
|
106
|
-
const showContent = !hasConfig || groupConfig.includes('content');
|
|
107
|
-
const showImage = !hasConfig || groupConfig.includes('imageUrl');
|
|
108
|
-
const { previewUrl } = usePresignedFileUrl(imageUrl);
|
|
109
|
-
const displayPreviewUrl = previewUrl || '';
|
|
110
|
-
return (_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-indigo-100/50", children: [_jsx("div", { className: "h-1.5 bg-gradient-to-r from-indigo-500 via-violet-500 to-purple-500" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-indigo-500 to-violet-600 shadow-md", children: _jsx(Layers, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-lg font-bold text-gray-900", children: "\u0110o\u1EA1n v\u0103n nh\u00F3m" }), _jsxs("span", { className: "inline-flex items-center rounded-full border border-indigo-300 bg-indigo-100 px-2.5 py-0.5 text-xs font-semibold text-indigo-700", children: ["Nh\u00F3m ", groupNumber] })] }), _jsxs("p", { className: "text-sm text-gray-500", children: ["N\u1ED9i dung \u0111\u1ECBnh d\u1EA1ng phong ph\u00FA \u2014 d\u00F9ng chung cho t\u1EA5t c\u1EA3 c\u00E2u h\u1ECFi trong Nh\u00F3m ", groupNumber] })] })] }) }), _jsxs(CardContent, { className: "space-y-5", children: [showTitle && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: `${uploadIdPrefix}-title`, className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(Type, { className: "h-4 w-4 text-indigo-500" }), "Ti\u00EAu \u0111\u1EC1 nh\u00F3m", ' ', _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(T\u00F9y ch\u1ECDn)" })] }), _jsx(Input, { id: `${uploadIdPrefix}-title`, value: title, onChange: (e) => { if (!isReadOnly)
|
|
111
|
-
onTitleChange(e.target.value); }, readOnly: isReadOnly, placeholder: "Nh\u1EADp ti\u00EAu \u0111\u1EC1 nh\u00F3m c\u00E2u h\u1ECFi\u2026", className: `border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${isReadOnly ? 'cursor-not-allowed border-transparent bg-gray-50 text-gray-500 focus:border-transparent focus:ring-0' : ''}` })] })), showSubtitle && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: `${uploadIdPrefix}-subtitle`, className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(Pilcrow, { className: "h-4 w-4 text-indigo-500" }), "Ph\u1EE5 \u0111\u1EC1", ' ', _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(T\u00F9y ch\u1ECDn)" })] }), _jsx(Input, { id: `${uploadIdPrefix}-subtitle`, value: subtitle, onChange: (e) => { if (!isReadOnly)
|
|
112
|
-
onSubtitleChange?.(e.target.value); }, readOnly: isReadOnly, placeholder: "Nh\u1EADp ph\u1EE5 \u0111\u1EC1 ho\u1EB7c m\u00F4 t\u1EA3 ng\u1EAFn\u2026", className: `border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${isReadOnly ? 'cursor-not-allowed border-transparent bg-gray-50 text-gray-500 focus:border-transparent focus:ring-0' : ''}` })] })), showContent && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(FileText, { className: "h-4 w-4 text-indigo-500" }), "N\u1ED9i dung \u0111o\u1EA1n v\u0103n", ' ', _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(T\u00F9y ch\u1ECDn)" })] }), _jsx(DocumentEditor, { value: content, onChange: onContentChange, isReadOnly: isReadOnly, placeholder: "Nh\u1EADp \u0111o\u1EA1n v\u0103n, email, th\u00F4ng b\u00E1o, qu\u1EA3ng c\u00E1o\u2026 d\u00F9ng chung cho nh\u00F3m c\u00E2u h\u1ECFi n\u00E0y", uploadIdPrefix: uploadIdPrefix }), !isReadOnly && (_jsxs("div", { className: "flex items-start gap-2.5 rounded-lg border border-amber-200 bg-amber-50 px-3.5 py-2.5", children: [_jsx(Info, { className: "mt-0.5 h-4 w-4 shrink-0 text-amber-500" }), _jsxs("div", { className: "text-xs text-amber-800 leading-relaxed", children: [_jsx("span", { className: "font-semibold", children: "G\u1EE3i \u00FD (ch\u1EC9 d\u00E0nh cho d\u1EA1ng \u0111i\u1EC1n t\u1EEB):" }), ' ', "N\u1EBFu \u0111o\u1EA1n v\u0103n c\u00F3 kho\u1EA3ng tr\u1ED1ng c\u1EA7n \u0111i\u1EC1n, h\u00E3y \u0111\u00E1nh d\u1EA5u b\u1EB1ng", ' ', _jsx("code", { className: "rounded bg-amber-100 px-1 py-0.5 font-mono font-semibold text-amber-700", children: '{0}' }), ",", ' ', _jsx("code", { className: "rounded bg-amber-100 px-1 py-0.5 font-mono font-semibold text-amber-700", children: '{1}' }), ",", ' ', _jsx("code", { className: "rounded bg-amber-100 px-1 py-0.5 font-mono font-semibold text-amber-700", children: '{2}' }), "\u2026 \u2014 m\u1ED7i s\u1ED1 t\u01B0\u01A1ng \u1EE9ng th\u1EE9 t\u1EF1 c\u00E2u h\u1ECFi trong nh\u00F3m. V\u1EDBi d\u1EA1ng b\u00E0i \u0111\u1ECDc th\u00F4ng th\u01B0\u1EDDng, b\u1EA1n kh\u00F4ng c\u1EA7n d\u00F9ng k\u00FD hi\u1EC7u n\u00E0y."] })] }))] })), showImage && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-indigo-500" }), "H\u00ECnh \u1EA3nh nh\u00F3m", ' ', _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(T\u00F9y ch\u1ECDn)" })] }), !isReadOnly && (_jsx(FileUpload, { id: `${uploadIdPrefix}-image`, label: "", accept: "image/*", value: imageUrl, onChange: (url) => { if (!isReadOnly)
|
|
113
|
-
onImageChange(url); }, onPresignedUrlChange: onImagePresignedUrlChange, maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" })), displayPreviewUrl && (_jsx(ImagePreview, { src: displayPreviewUrl, alt: "Group image preview", className: "mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200" }))] }))] })] }));
|
|
114
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as t}from"react/jsx-runtime";import{useEffect as n,useState as i}from"react";import{useEditor as a,EditorContent as l}from"@tiptap/react";import o from"@tiptap/starter-kit";import r from"@tiptap/extension-text-align";import{ImagePreview as s}from"../../../components/ui/image-preview";import{Input as c}from"../../../components/ui/input";import{Label as d}from"../../../components/ui/label";import{Card as h,CardContent as m,CardHeader as g,CardTitle as u}from"../../../components/ui/card";import{FileUpload as p}from"../../../components/ui/file-upload";import{usePresignedFileUrl as x}from"../../../shared/lib/hooks";import{Layers as b,Type as f,Pilcrow as v,ImageIcon as N,Bold as y,Italic as w,Strikethrough as _,Heading1 as C,Heading2 as k,Heading3 as A,List as T,ListOrdered as U,AlignLeft as H,AlignCenter as L,AlignRight as I,Minus as $,Undo2 as j,Redo2 as O,FileText as P,Info as R}from"lucide-react";function G({onClick:t,active:n,disabled:i,title:a,children:l}){return e("button",{type:"button",title:a,onClick:t,disabled:i,className:`flex h-7 w-7 items-center justify-center rounded-md text-sm transition-colors\n ${n?"bg-indigo-500 text-white shadow-sm":"text-gray-600 hover:bg-gray-200 hover:text-gray-900"}\n ${i?"cursor-not-allowed opacity-40":"cursor-pointer"}\n `,children:l})}function S(){return e("div",{className:"mx-0.5 h-5 w-px shrink-0 bg-gray-300"})}function z({value:s,onChange:c,isReadOnly:d=!1,placeholder:h}){const[m,g]=i(!1),u=a({immediatelyRender:!1,editable:!d,extensions:[o,r.configure({types:["heading","paragraph"]})],content:s||"",onUpdate:({editor:e})=>{c(e.getHTML())},editorProps:{attributes:{class:["focus:outline-none min-h-[200px] max-h-[520px] overflow-y-auto p-4 text-sm text-gray-800 leading-relaxed","prose prose-sm max-w-none","[&_h1]:text-xl [&_h1]:font-bold [&_h1]:mb-2","[&_h2]:text-lg [&_h2]:font-bold [&_h2]:mb-1.5","[&_h3]:text-base [&_h3]:font-semibold [&_h3]:mb-1","[&_ul]:list-disc [&_ul]:ml-5 [&_ol]:list-decimal [&_ol]:ml-5","[&_li]:mb-0.5","[&_strong]:font-bold [&_em]:italic [&_s]:line-through","[&_hr]:border-gray-300 [&_hr]:my-3","bg-white"].join(" "),...h&&!s?{"data-placeholder":h}:{}}}});return n(()=>{g(!0)},[]),n(()=>{u&&void 0!==s&&s!==u.getHTML()&&setTimeout(()=>u.commands.setContent(s,{emitUpdate:!1}),0)},[s,u]),n(()=>{u?.setEditable(!d)},[d,u]),m?t("div",{className:"overflow-hidden rounded-sm border-2 transition-all "+(d?"border-gray-200":"border-gray-200 focus-within:border-indigo-400 focus-within:ring-2 focus-within:ring-indigo-100"),children:[!d&&t("div",{className:"flex flex-wrap items-center gap-0.5 border-b border-gray-200 bg-gray-50 px-2 py-1.5",children:[e(G,{title:"Hoàn tác",onClick:()=>u?.chain().focus().undo().run(),disabled:!u?.can().undo(),children:e(j,{className:"h-3.5 w-3.5"})}),e(G,{title:"Làm lại",onClick:()=>u?.chain().focus().redo().run(),disabled:!u?.can().redo(),children:e(O,{className:"h-3.5 w-3.5"})}),e(S,{}),e(G,{title:"Tiêu đề 1",active:u?.isActive("heading",{level:1}),onClick:()=>u?.chain().focus().toggleHeading({level:1}).run(),children:e(C,{className:"h-3.5 w-3.5"})}),e(G,{title:"Tiêu đề 2",active:u?.isActive("heading",{level:2}),onClick:()=>u?.chain().focus().toggleHeading({level:2}).run(),children:e(k,{className:"h-3.5 w-3.5"})}),e(G,{title:"Tiêu đề 3",active:u?.isActive("heading",{level:3}),onClick:()=>u?.chain().focus().toggleHeading({level:3}).run(),children:e(A,{className:"h-3.5 w-3.5"})}),e(S,{}),e(G,{title:"In đậm",active:u?.isActive("bold"),onClick:()=>u?.chain().focus().toggleBold().run(),children:e(y,{className:"h-3.5 w-3.5"})}),e(G,{title:"In nghiêng",active:u?.isActive("italic"),onClick:()=>u?.chain().focus().toggleItalic().run(),children:e(w,{className:"h-3.5 w-3.5"})}),e(G,{title:"Gạch ngang",active:u?.isActive("strike"),onClick:()=>u?.chain().focus().toggleStrike().run(),children:e(_,{className:"h-3.5 w-3.5"})}),e(S,{}),e(G,{title:"Danh sách",active:u?.isActive("bulletList"),onClick:()=>u?.chain().focus().toggleBulletList().run(),children:e(T,{className:"h-3.5 w-3.5"})}),e(G,{title:"Danh sách số",active:u?.isActive("orderedList"),onClick:()=>u?.chain().focus().toggleOrderedList().run(),children:e(U,{className:"h-3.5 w-3.5"})}),e(S,{}),e(G,{title:"Căn trái",active:u?.isActive({textAlign:"left"}),onClick:()=>u?.chain().focus().setTextAlign("left").run(),children:e(H,{className:"h-3.5 w-3.5"})}),e(G,{title:"Căn giữa",active:u?.isActive({textAlign:"center"}),onClick:()=>u?.chain().focus().setTextAlign("center").run(),children:e(L,{className:"h-3.5 w-3.5"})}),e(G,{title:"Căn phải",active:u?.isActive({textAlign:"right"}),onClick:()=>u?.chain().focus().setTextAlign("right").run(),children:e(I,{className:"h-3.5 w-3.5"})}),e(S,{}),e(G,{title:"Đường kẻ ngang",onClick:()=>u?.chain().focus().setHorizontalRule().run(),children:e($,{className:"h-3.5 w-3.5"})})]}),d?e("div",{className:["min-h-[120px] max-h-[520px] overflow-y-auto p-4 text-sm text-gray-800 leading-relaxed bg-gray-50 cursor-not-allowed","prose prose-sm max-w-none","[&_h1]:text-xl [&_h1]:font-bold [&_h1]:mb-2","[&_h2]:text-lg [&_h2]:font-bold [&_h2]:mb-1.5","[&_h3]:text-base [&_h3]:font-semibold [&_h3]:mb-1","[&_ul]:list-disc [&_ul]:ml-5 [&_ol]:list-decimal [&_ol]:ml-5","[&_li]:mb-0.5","[&_strong]:font-bold [&_em]:italic [&_s]:line-through","[&_hr]:border-gray-300 [&_hr]:my-3"].join(" "),dangerouslySetInnerHTML:{__html:(p=s||"",p.replace(/\{(\d+)\}/g,(e,t)=>`<span style="display:inline-block;min-width:120px;padding:1px 5px;margin:0 4px;background:#eef2ff;border-bottom:2px solid #6366f1;color:#4f46e5;font-weight:700;font-size:0.85em;text-align:center;vertical-align:baseline;letter-spacing:2px">———(${Number(t)+1})———</span>`))}}):e(l,{editor:u})]}):null;var p}export function DocumentGroupCard({groupNumber:n,title:i,subtitle:a="",content:l,imageUrl:o,onTitleChange:r,onSubtitleChange:y,onContentChange:w,onImageChange:_,onImagePresignedUrlChange:C,groupConfig:k,uploadIdPrefix:A="document-group",isReadOnly:T=!1}){const U=k&&k.length>0,H=!U||k.includes("title"),L=!U||k.includes("subtitle"),I=!U||k.includes("content"),$=!U||k.includes("imageUrl"),{previewUrl:j}=x(o),O=j||"";return t(h,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-indigo-100/50",children:[e("div",{className:"h-1.5 bg-gradient-to-r from-indigo-500 via-violet-500 to-purple-500"}),e(g,{className:"pb-4",children:t("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-10 w-10 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-indigo-500 to-violet-600 shadow-md",children:e(b,{className:"h-5 w-5 text-white"})}),t("div",{className:"flex-1",children:[t("div",{className:"flex items-center gap-2",children:[e(u,{className:"text-lg font-bold text-gray-900",children:"Đoạn văn nhóm"}),t("span",{className:"inline-flex items-center rounded-full border border-indigo-300 bg-indigo-100 px-2.5 py-0.5 text-xs font-semibold text-indigo-700",children:["Nhóm ",n]})]}),t("p",{className:"text-sm text-gray-500",children:["Nội dung định dạng phong phú — dùng chung cho tất cả câu hỏi trong Nhóm ",n]})]})]})}),t(m,{className:"space-y-5",children:[H&&t("div",{className:"space-y-2",children:[t(d,{htmlFor:`${A}-title`,className:"flex items-center gap-2 text-sm font-semibold text-gray-800",children:[e(f,{className:"h-4 w-4 text-indigo-500"}),"Tiêu đề nhóm"," ",e("span",{className:"text-xs font-normal text-gray-500",children:"(Tùy chọn)"})]}),e(c,{id:`${A}-title`,value:i,onChange:e=>{T||r(e.target.value)},readOnly:T,placeholder:"Nhập tiêu đề nhóm câu hỏi…",className:"border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 "+(T?"cursor-not-allowed border-transparent bg-gray-50 text-gray-500 focus:border-transparent focus:ring-0":"")})]}),L&&t("div",{className:"space-y-2",children:[t(d,{htmlFor:`${A}-subtitle`,className:"flex items-center gap-2 text-sm font-semibold text-gray-800",children:[e(v,{className:"h-4 w-4 text-indigo-500"}),"Phụ đề"," ",e("span",{className:"text-xs font-normal text-gray-500",children:"(Tùy chọn)"})]}),e(c,{id:`${A}-subtitle`,value:a,onChange:e=>{T||y?.(e.target.value)},readOnly:T,placeholder:"Nhập phụ đề hoặc mô tả ngắn…",className:"border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 "+(T?"cursor-not-allowed border-transparent bg-gray-50 text-gray-500 focus:border-transparent focus:ring-0":"")})]}),I&&t("div",{className:"space-y-2",children:[t(d,{className:"flex items-center gap-2 text-sm font-semibold text-gray-800",children:[e(P,{className:"h-4 w-4 text-indigo-500"}),"Nội dung đoạn văn"," ",e("span",{className:"text-xs font-normal text-gray-500",children:"(Tùy chọn)"})]}),e(z,{value:l,onChange:w,isReadOnly:T,placeholder:"Nhập đoạn văn, email, thông báo, quảng cáo… dùng chung cho nhóm câu hỏi này",uploadIdPrefix:A}),!T&&t("div",{className:"flex items-start gap-2.5 rounded-lg border border-amber-200 bg-amber-50 px-3.5 py-2.5",children:[e(R,{className:"mt-0.5 h-4 w-4 shrink-0 text-amber-500"}),t("div",{className:"text-xs text-amber-800 leading-relaxed",children:[e("span",{className:"font-semibold",children:"Gợi ý (chỉ dành cho dạng điền từ):"})," ","Nếu đoạn văn có khoảng trống cần điền, hãy đánh dấu bằng"," ",e("code",{className:"rounded bg-amber-100 px-1 py-0.5 font-mono font-semibold text-amber-700",children:"{0}"}),","," ",e("code",{className:"rounded bg-amber-100 px-1 py-0.5 font-mono font-semibold text-amber-700",children:"{1}"}),","," ",e("code",{className:"rounded bg-amber-100 px-1 py-0.5 font-mono font-semibold text-amber-700",children:"{2}"}),"… — mỗi số tương ứng thứ tự câu hỏi trong nhóm. Với dạng bài đọc thông thường, bạn không cần dùng ký hiệu này."]})]})]}),$&&t("div",{className:"space-y-2",children:[t(d,{className:"flex items-center gap-2 text-sm font-semibold text-gray-800",children:[e(N,{className:"h-4 w-4 text-indigo-500"}),"Hình ảnh nhóm"," ",e("span",{className:"text-xs font-normal text-gray-500",children:"(Tùy chọn)"})]}),!T&&e(p,{id:`${A}-image`,label:"",accept:"image/*",value:o,onChange:e=>{T||_(e)},onPresignedUrlChange:C,maxSize:5,placeholder:"Upload ảnh hoặc paste URL",autoUpload:!0,prefix:"questions"}),O&&e(s,{src:O,alt:"Group image preview",className:"mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200"})]})]})]})}
|
|
@@ -1,25 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useState } from 'react';
|
|
4
|
-
import { ImagePreview } from '../../../components/ui/image-preview';
|
|
5
|
-
import { Button } from '../../../components/ui/button';
|
|
6
|
-
import { Label } from '../../../components/ui/label';
|
|
7
|
-
import { Input } from '../../../components/ui/input';
|
|
8
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../../components/ui/card';
|
|
9
|
-
import { FileUpload } from '../../../components/ui/file-upload';
|
|
10
|
-
import { usePresignedFileUrl } from '../../../shared/lib/hooks';
|
|
11
|
-
import { ImageIcon, Layers, Plus, Type, X } from 'lucide-react';
|
|
12
|
-
function OptionRow({ option, index, canRemove, onUpdate, onRemove, groupConfig, }) {
|
|
13
|
-
const [localPreviewUrl, setLocalPreviewUrl] = useState('');
|
|
14
|
-
const { previewUrl: presignedUrl } = usePresignedFileUrl(option.imageUrl || '');
|
|
15
|
-
const displayUrl = localPreviewUrl || presignedUrl || '';
|
|
16
|
-
const showAnswer = !groupConfig || groupConfig.includes('answer');
|
|
17
|
-
const showContent = groupConfig?.includes('content') ?? false;
|
|
18
|
-
const showImageUrl = !groupConfig || groupConfig.includes('imageUrl');
|
|
19
|
-
return (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-purple-100 bg-gradient-to-r from-purple-50/50 to-violet-50/50 p-4", children: [_jsx("div", { className: "flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-purple-500 to-violet-600 text-base font-bold text-white shadow-sm", children: index + 1 }), _jsxs("div", { className: "flex-1 space-y-3", children: [_jsxs("div", { className: "flex items-end gap-3", children: [showAnswer && (_jsx("div", { className: "flex-1", children: _jsx(Input, { value: option.answer, onChange: (e) => onUpdate(index, 'answer', e.target.value), placeholder: `Nhãn (ví dụ: ${String.fromCharCode(65 + index)} hoặc "a pineapple")`, className: "border-purple-200 bg-white focus:border-purple-400 focus:ring-purple-200" }) })), showContent && (_jsx("div", { className: "flex-1", children: _jsx(Input, { value: option.content || '', onChange: (e) => onUpdate(index, 'content', e.target.value), placeholder: "N\u1ED9i dung (m\u00F4 t\u1EA3 th\u00EAm cho \u0111\u00E1p \u00E1n)", className: "border-purple-200 bg-white focus:border-purple-400 focus:ring-purple-200" }) })), showImageUrl && (_jsx("div", { className: "flex-1", children: _jsx(FileUpload, { id: `option-image-${index}`, label: "", accept: "image/*", value: option.imageUrl || '', onChange: (url) => onUpdate(index, 'imageUrl', url), onPresignedUrlChange: setLocalPreviewUrl, maxSize: 5, placeholder: "Upload \u1EA3nh cho \u0111\u00E1p \u00E1n", autoUpload: true, prefix: "questions" }) }))] }), showImageUrl && displayUrl && (_jsx("div", { className: "relative h-32 w-32 overflow-hidden rounded-lg border border-purple-200 bg-white shadow-sm", children: _jsx("img", { src: displayUrl, alt: option.answer || `Option ${index + 1}`, className: "h-full w-full object-contain" }) }))] }), canRemove && (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => onRemove(index), className: "flex-shrink-0 text-red-500 hover:bg-red-50 hover:text-red-700", children: _jsx(X, { className: "h-4 w-4" }) }))] }));
|
|
20
|
-
}
|
|
21
|
-
export function MatchAnswerGroupCard({ currentGroupNumber, groupTitle, groupImageUrl, groupImagePreviewUrl, options, groupConfig, showGroupTitle, showGroupImageUrl, onGroupTitleChange, onGroupImageChange, onGroupImagePreviewUrlChange, onUpdateOption, onRemoveOption, onAddOption, }) {
|
|
22
|
-
const { previewUrl: groupImagePresignedUrl } = usePresignedFileUrl(groupImageUrl || '');
|
|
23
|
-
const displayGroupImageUrl = groupImagePreviewUrl || groupImagePresignedUrl || '';
|
|
24
|
-
return (_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-purple-100/50", children: [_jsx(CardHeader, { className: "pb-3", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-purple-500 to-violet-600 shadow-md", children: _jsx(Layers, { className: "h-5 w-5 text-white" }) }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-lg font-bold text-gray-900", children: "\u0110\u00E1p \u00E1n gh\u00E9p n\u1ED1i" }), _jsxs("span", { className: "inline-flex items-center rounded-full border border-purple-300 bg-purple-100 px-2.5 py-0.5 text-xs font-semibold text-purple-700", children: ["Nh\u00F3m ", currentGroupNumber] })] }), _jsx("p", { className: "text-sm text-gray-500", children: "C\u00E1c \u0111\u00E1p \u00E1n (h\u00ECnh \u1EA3nh + nh\u00E3n) d\u00F9ng \u0111\u1EC3 gh\u00E9p n\u1ED1i v\u1EDBi t\u1EEBng c\u00E2u h\u1ECFi trong nh\u00F3m" })] })] }) }), _jsxs(CardContent, { className: "space-y-4", children: [showGroupTitle && (_jsxs("div", { className: "space-y-2 rounded-xl border border-purple-100 bg-gradient-to-r from-purple-50/30 to-violet-50/30 p-4", children: [_jsxs(Label, { htmlFor: "group-title", className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(Type, { className: "h-4 w-4 text-purple-500" }), "Ti\u00EAu \u0111\u1EC1 nh\u00F3m", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(d\u00F9ng chung cho c\u1EA3 nh\u00F3m)" })] }), _jsx(Input, { id: "group-title", value: groupTitle, onChange: (e) => onGroupTitleChange(e.target.value), placeholder: "Nh\u1EADp ti\u00EAu \u0111\u1EC1 nh\u00F3m...", className: "border-purple-200 bg-white focus:border-purple-400 focus:ring-purple-200" })] })), showGroupImageUrl && (_jsxs("div", { className: "space-y-2 rounded-xl border border-purple-100 bg-gradient-to-r from-purple-50/30 to-violet-50/30 p-4", children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-800", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-purple-500" }), "H\u00ECnh \u1EA3nh nh\u00F3m ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(T\u00F9y ch\u1ECDn - d\u00F9ng chung cho c\u1EA3 nh\u00F3m)" })] }), _jsx(FileUpload, { id: "group-image", label: "", accept: "image/*", value: groupImageUrl || '', onChange: onGroupImageChange, onPresignedUrlChange: onGroupImagePreviewUrlChange, maxSize: 5, placeholder: "Upload \u1EA3nh nh\u00F3m ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" }), displayGroupImageUrl && (_jsx(ImagePreview, { src: displayGroupImageUrl, alt: "Group image preview", className: "mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200" }))] })), _jsx("div", { className: "space-y-4", children: options.map((option, index) => (_jsx(OptionRow, { option: option, index: index, canRemove: options.length > 2, onUpdate: onUpdateOption, onRemove: onRemoveOption, groupConfig: groupConfig }, index))) }), _jsxs(Button, { variant: "outline", size: "sm", onClick: onAddOption, className: "gap-2 border-purple-300 text-purple-600 hover:bg-purple-50 hover:text-purple-700", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm \u0111\u00E1p \u00E1n (", options.length + 1, ")"] })] })] }));
|
|
25
|
-
}
|
|
1
|
+
"use client";import{jsx as e,jsxs as r}from"react/jsx-runtime";import{useState as o}from"react";import{ImagePreview as l}from"../../../components/ui/image-preview";import{Button as n}from"../../../components/ui/button";import{Label as a}from"../../../components/ui/label";import{Input as t}from"../../../components/ui/input";import{Card as i,CardContent as s,CardHeader as p,CardTitle as c}from"../../../components/ui/card";import{FileUpload as d}from"../../../components/ui/file-upload";import{usePresignedFileUrl as m}from"../../../shared/lib/hooks";import{ImageIcon as h,Layers as u,Plus as g,Type as f,X as x}from"lucide-react";function v({option:l,index:a,canRemove:i,onUpdate:s,onRemove:p,groupConfig:c}){const[h,u]=o(""),{previewUrl:g}=m(l.imageUrl||""),f=h||g||"",v=!c||c.includes("answer"),b=c?.includes("content")??!1,N=!c||c.includes("imageUrl");return r("div",{className:"flex items-start gap-3 rounded-xl border border-purple-100 bg-gradient-to-r from-purple-50/50 to-violet-50/50 p-4",children:[e("div",{className:"flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-xl bg-gradient-to-br from-purple-500 to-violet-600 text-base font-bold text-white shadow-sm",children:a+1}),r("div",{className:"flex-1 space-y-3",children:[r("div",{className:"flex items-end gap-3",children:[v&&e("div",{className:"flex-1",children:e(t,{value:l.answer,onChange:e=>s(a,"answer",e.target.value),placeholder:`Nhãn (ví dụ: ${String.fromCharCode(65+a)} hoặc "a pineapple")`,className:"border-purple-200 bg-white focus:border-purple-400 focus:ring-purple-200"})}),b&&e("div",{className:"flex-1",children:e(t,{value:l.content||"",onChange:e=>s(a,"content",e.target.value),placeholder:"Nội dung (mô tả thêm cho đáp án)",className:"border-purple-200 bg-white focus:border-purple-400 focus:ring-purple-200"})}),N&&e("div",{className:"flex-1",children:e(d,{id:`option-image-${a}`,label:"",accept:"image/*",value:l.imageUrl||"",onChange:e=>s(a,"imageUrl",e),onPresignedUrlChange:u,maxSize:5,placeholder:"Upload ảnh cho đáp án",autoUpload:!0,prefix:"questions"})})]}),N&&f&&e("div",{className:"relative h-32 w-32 overflow-hidden rounded-lg border border-purple-200 bg-white shadow-sm",children:e("img",{src:f,alt:l.answer||`Option ${a+1}`,className:"h-full w-full object-contain"})})]}),i&&e(n,{variant:"ghost",size:"sm",onClick:()=>p(a),className:"flex-shrink-0 text-red-500 hover:bg-red-50 hover:text-red-700",children:e(x,{className:"h-4 w-4"})})]})}export function MatchAnswerGroupCard({currentGroupNumber:o,groupTitle:x,groupImageUrl:b,groupImagePreviewUrl:N,options:w,groupConfig:U,showGroupTitle:C,showGroupImageUrl:y,onGroupTitleChange:G,onGroupImageChange:j,onGroupImagePreviewUrlChange:R,onUpdateOption:T,onRemoveOption:k,onAddOption:I}){const{previewUrl:z}=m(b||""),O=N||z||"";return r(i,{className:"overflow-hidden border-0 bg-white shadow-lg shadow-purple-100/50",children:[e(p,{className:"pb-3",children:r("div",{className:"flex items-center gap-3",children:[e("div",{className:"flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-purple-500 to-violet-600 shadow-md",children:e(u,{className:"h-5 w-5 text-white"})}),r("div",{className:"flex-1",children:[r("div",{className:"flex items-center gap-2",children:[e(c,{className:"text-lg font-bold text-gray-900",children:"Đáp án ghép nối"}),r("span",{className:"inline-flex items-center rounded-full border border-purple-300 bg-purple-100 px-2.5 py-0.5 text-xs font-semibold text-purple-700",children:["Nhóm ",o]})]}),e("p",{className:"text-sm text-gray-500",children:"Các đáp án (hình ảnh + nhãn) dùng để ghép nối với từng câu hỏi trong nhóm"})]})]})}),r(s,{className:"space-y-4",children:[C&&r("div",{className:"space-y-2 rounded-xl border border-purple-100 bg-gradient-to-r from-purple-50/30 to-violet-50/30 p-4",children:[r(a,{htmlFor:"group-title",className:"flex items-center gap-2 text-sm font-semibold text-gray-800",children:[e(f,{className:"h-4 w-4 text-purple-500"}),"Tiêu đề nhóm",e("span",{className:"text-xs font-normal text-gray-500",children:"(dùng chung cho cả nhóm)"})]}),e(t,{id:"group-title",value:x,onChange:e=>G(e.target.value),placeholder:"Nhập tiêu đề nhóm...",className:"border-purple-200 bg-white focus:border-purple-400 focus:ring-purple-200"})]}),y&&r("div",{className:"space-y-2 rounded-xl border border-purple-100 bg-gradient-to-r from-purple-50/30 to-violet-50/30 p-4",children:[r(a,{className:"flex items-center gap-2 text-sm font-semibold text-gray-800",children:[e(h,{className:"h-4 w-4 text-purple-500"}),"Hình ảnh nhóm ",e("span",{className:"text-xs font-normal text-gray-500",children:"(Tùy chọn - dùng chung cho cả nhóm)"})]}),e(d,{id:"group-image",label:"",accept:"image/*",value:b||"",onChange:j,onPresignedUrlChange:R,maxSize:5,placeholder:"Upload ảnh nhóm hoặc paste URL",autoUpload:!0,prefix:"questions"}),O&&e(l,{src:O,alt:"Group image preview",className:"mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200"})]}),e("div",{className:"space-y-4",children:w.map((r,o)=>e(v,{option:r,index:o,canRemove:w.length>2,onUpdate:T,onRemove:k,groupConfig:U},o))}),r(n,{variant:"outline",size:"sm",onClick:I,className:"gap-2 border-purple-300 text-purple-600 hover:bg-purple-50 hover:text-purple-700",children:[e(g,{className:"h-4 w-4"}),"Thêm đáp án (",w.length+1,")"]})]})]})}
|
|
@@ -1,10 +1 @@
|
|
|
1
|
-
export
|
|
2
|
-
export * from './creator/dedicated-component-router';
|
|
3
|
-
export * from './creator/question-type-registry';
|
|
4
|
-
export * from './components';
|
|
5
|
-
export * from './groups/ArticlesGroupCard';
|
|
6
|
-
export * from './groups/BasicQuestionGroupCard';
|
|
7
|
-
export * from './groups/DocumentGroupCard';
|
|
8
|
-
export * from './groups/MatchAnswerGroupCard';
|
|
9
|
-
export * from './viewer';
|
|
10
|
-
export * from './_shared';
|
|
1
|
+
export*from"./creator";export*from"./creator/dedicated-component-router";export*from"./creator/question-type-registry";export*from"./components";export*from"./groups/ArticlesGroupCard";export*from"./groups/BasicQuestionGroupCard";export*from"./groups/DocumentGroupCard";export*from"./groups/MatchAnswerGroupCard";export*from"./viewer";export*from"./_shared";
|