@tinyweb_dev/oe-exam-sdk 1.0.5 → 1.0.7
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/components/exams/ExamCreator.js +1 -16
- package/dist/components/exams/take/ExamTakingPageContainer.js +0 -12
- package/dist/components/exams/take/components/QuestionRenderer.js +2 -15
- package/dist/components/exams/take/components/question-renderers/index.d.ts +0 -3
- package/dist/components/exams/take/components/question-renderers/index.js +0 -3
- package/dist/components/exams/take/components/speaking/renderers/SpeakingQuestionRenderer.js +0 -4
- package/dist/components/exams/take/types/speaking.types.d.ts +0 -31
- package/dist/components/exams/take/types.d.ts +0 -65
- package/dist/components/exams/take/utils/answer-transformers.js +6 -60
- package/dist/components/exams/take/utils/question-transformers.d.ts +2 -15
- package/dist/components/exams/take/utils/question-transformers.js +0 -143
- package/dist/components/questions/_shared/components/compact/CompactCreatorHeader.d.ts +23 -0
- package/dist/components/questions/_shared/components/compact/CompactCreatorHeader.js +9 -0
- package/dist/components/questions/_shared/components/compact/CompactExampleToggle.d.ts +6 -0
- package/dist/components/questions/_shared/components/compact/CompactExampleToggle.js +8 -0
- package/dist/components/questions/_shared/components/compact/CompactExplanationToggle.d.ts +7 -0
- package/dist/components/questions/_shared/components/compact/CompactExplanationToggle.js +14 -0
- package/dist/components/questions/_shared/components/compact/CompactFieldCard.d.ts +9 -0
- package/dist/components/questions/_shared/components/compact/CompactFieldCard.js +7 -0
- package/dist/components/questions/_shared/components/compact/CompactPreviewBanner.d.ts +7 -0
- package/dist/components/questions/_shared/components/compact/CompactPreviewBanner.js +7 -0
- package/dist/components/questions/_shared/components/compact/CompactPreviewButton.d.ts +6 -0
- package/dist/components/questions/_shared/components/compact/CompactPreviewButton.js +7 -0
- package/dist/components/questions/_shared/components/compact/index.d.ts +6 -0
- package/dist/components/questions/_shared/components/compact/index.js +6 -0
- package/dist/components/questions/_shared/config/question-types.config.js +0 -24
- package/dist/components/questions/_shared/hooks/useQuestionInitialization.js +0 -9
- package/dist/components/questions/_shared/types/index.d.ts +0 -1
- package/dist/components/questions/_shared/types/index.js +0 -1
- package/dist/components/questions/_shared/types/look-and-choose-answer.type.d.ts +0 -13
- package/dist/components/questions/_shared/utils/answer-builder.js +0 -11
- package/dist/components/questions/components/ColorRegionCreator.js +66 -63
- package/dist/components/questions/components/QuestionSearchDropdown.js +0 -8
- package/dist/components/questions/components/QuestionTypeSelector.js +0 -2
- package/dist/components/questions/components/index.d.ts +1 -0
- package/dist/components/questions/components/index.js +1 -0
- package/dist/components/questions/creator/question-type-registry.js +0 -8
- package/dist/components/questions/groups/ArticlesGroupCard.js +2 -2
- package/dist/components/questions/groups/BasicQuestionGroupCard.js +1 -1
- package/dist/components/questions/groups/DocumentGroupCard.js +1 -1
- package/dist/components/questions/groups/ImageLabelGroupCard.js +1 -1
- package/dist/components/questions/groups/MatchAnswerGroupCard.js +2 -2
- package/dist/components/questions/question-bank/QuestionEditorRenderer.d.ts +0 -3
- package/dist/components/questions/question-bank/QuestionEditorRenderer.js +1 -10
- package/dist/components/questions/question-bank/editor-adapters/choice-adapters.d.ts +0 -2
- package/dist/components/questions/question-bank/editor-adapters/choice-adapters.js +0 -41
- package/dist/components/questions/question-bank/editor-adapters/index.d.ts +2 -2
- package/dist/components/questions/question-bank/editor-adapters/index.js +2 -2
- package/dist/components/questions/question-bank/editor-adapters/media-adapters.d.ts +0 -1
- package/dist/components/questions/question-bank/editor-adapters/media-adapters.js +0 -28
- package/dist/components/questions/question-bank/question-bank-picker.utils.js +0 -7
- package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +16 -16
- package/dist/components/questions/types/answer-the-question/AnswerTheQuestionCreator.js +36 -30
- package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupClient.js +1 -1
- package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupCreator.js +7 -6
- package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +2 -2
- package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js +11 -7
- package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +1 -1
- package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js +11 -5
- package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +5 -5
- package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js +34 -28
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseAnswerGroupImageUpload.js +2 -2
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +1 -1
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupCreator.js +44 -28
- package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupClient.js +7 -7
- package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupCreator.js +8 -5
- package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +2 -2
- package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +20 -16
- package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.js +1 -1
- package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleCreator.js +66 -29
- package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +5 -5
- package/dist/components/questions/types/fill-in-blank/FillInBlankCreator.js +45 -37
- package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupCreator.js +24 -20
- package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +2 -2
- package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js +68 -65
- package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixClient.js +1 -1
- package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixCreator.js +45 -5
- package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +2 -2
- package/dist/components/questions/types/label-the-picture/LabelThePictureCreator.js +18 -14
- package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +2 -2
- package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupCreator.js +30 -30
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneClient.js +2 -2
- package/dist/components/questions/types/listen-and-choose-objects-in-scene/ListenAndChooseObjectsInSceneCreator.js +24 -31
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneClient.js +2 -2
- package/dist/components/questions/types/listen-and-drag-objects-into-scene/ListenAndDragObjectsIntoSceneCreator.js +29 -32
- package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +1 -1
- package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerCreator.js +27 -29
- package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +1 -1
- package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesCreator.js +11 -15
- package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +1 -1
- package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupCreator.js +22 -24
- package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +1 -1
- package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeCreator.js +17 -24
- package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +1 -1
- package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutCreator.js +31 -31
- package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +1 -1
- package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListCreator.js +11 -15
- package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +1 -1
- package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesCreator.js +14 -15
- package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +2 -2
- package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js +27 -25
- package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesClient.js +12 -12
- package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesCreator.js +8 -5
- package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureClient.js +8 -8
- package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureCreator.js +25 -20
- package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupClient.js +1 -1
- package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupCreator.js +13 -10
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupCreator.js +8 -5
- package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js +7 -11
- package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +1 -1
- package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsCreator.js +15 -12
- package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesClient.js +1 -1
- package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesCreator.js +5 -5
- package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js +35 -39
- package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +2 -2
- package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardClient.js +1 -1
- package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardCreator.js +7 -8
- package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +1 -1
- package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageCreator.js +30 -23
- package/dist/components/questions/types/spontaneous-qa/SpontaneousQAClient.js +1 -1
- package/dist/components/questions/types/spontaneous-qa/SpontaneousQACreator.js +26 -19
- package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.js +1 -1
- package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.js +13 -11
- package/dist/components/questions/types/true-false/TrueFalseClient.js +2 -2
- package/dist/components/questions/types/true-false/TrueFalseCreator.js +35 -29
- package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupClient.js +1 -1
- package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupCreator.js +11 -7
- package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +1 -1
- package/dist/components/questions/types/true-false-group/TrueFalseGroupCreator.js +11 -7
- package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +2 -2
- package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphCreator.js +21 -56
- package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +1 -1
- package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormCreator.js +30 -24
- package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.js +3 -3
- package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupCreator.js +12 -14
- package/dist/components/questions/types/word-ordering/WordOrderingClient.js +1 -1
- package/dist/components/questions/types/word-ordering/WordOrderingCreator.js +27 -18
- package/dist/components/questions/types/word-ordering-group/WordOrderingGroupClient.js +7 -7
- package/dist/components/questions/types/word-ordering-group/WordOrderingGroupCreator.js +16 -13
- package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterClient.js +2 -2
- package/dist/components/questions/types/write-a-short-letter/WriteAShortLetterCreator.js +34 -28
- package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +2 -2
- package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormCreator.js +22 -22
- package/dist/components/questions/types/write-sentences/WriteSentencesCreator.js +21 -18
- package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphClient.js +1 -1
- package/dist/components/questions/types/write-short-paragraph/WriteShortParagraphCreator.js +7 -4
- package/dist/components/questions/viewer/QuestionViewer.js +0 -98
- package/dist/components/results/renderers/review-question-body-movers.js +2 -26
- package/dist/components/results/review-data.js +0 -4
- package/dist/components/ui/points-input.d.ts +1 -0
- package/dist/components/ui/points-input.js +12 -2
- package/dist/shared/constants/question-skills.js +0 -11
- package/dist/shared/lib/i18n/messages/en/admin.d.ts +0 -3
- package/dist/shared/lib/i18n/messages/en/admin.js +0 -3
- package/dist/shared/lib/i18n/messages/en.d.ts +0 -3
- package/dist/shared/lib/i18n/messages/vi/admin.d.ts +0 -3
- package/dist/shared/lib/i18n/messages/vi/admin.js +0 -3
- package/dist/shared/lib/i18n/messages/vi.d.ts +0 -3
- package/dist/shared/lib/utils/question-reverse-transform.js +0 -19
- package/dist/shared/lib/utils/question-transform-types.d.ts +1 -23
- package/dist/shared/lib/utils/question-transform-validation.js +0 -40
- package/dist/shared/lib/utils/question-transform.js +0 -9
- package/dist/shared/types/common.types.d.ts +1 -1
- package/dist/shared/types/entities/question.types.d.ts +1 -11
- package/dist/shared/types/questions/index.d.ts +0 -3
- package/dist/shared/types/questions/index.js +0 -6
- package/package.json +1 -1
- package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.d.ts +0 -2
- package/dist/components/exams/take/components/question-renderers/MoversFillWordHintRenderer.js +0 -131
- package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.d.ts +0 -2
- package/dist/components/exams/take/components/question-renderers/MoversPictureChooseRenderer.js +0 -76
- package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.d.ts +0 -2
- package/dist/components/exams/take/components/question-renderers/MoversPictureFillBlankChooseRenderer.js +0 -68
- package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.d.ts +0 -16
- package/dist/components/exams/take/components/speaking/renderers/SpeakingGNInterviewRenderer.js +0 -212
- package/dist/components/questions/_shared/types/gn-speaking-interview.type.d.ts +0 -32
- package/dist/components/questions/_shared/types/gn-speaking-interview.type.js +0 -1
- package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.d.ts +0 -47
- package/dist/components/questions/_shared/types/look-picture-fill-word-hint.type.js +0 -1
- package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.d.ts +0 -2
- package/dist/components/questions/types/gn-speaking-interview/GNSpeakingInterviewCreator.js +0 -262
- package/dist/components/questions/types/gn-speaking-interview/register.d.ts +0 -2
- package/dist/components/questions/types/gn-speaking-interview/register.js +0 -121
- package/dist/components/questions/types/gn-speaking-interview/transform.d.ts +0 -2
- package/dist/components/questions/types/gn-speaking-interview/transform.js +0 -48
- package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.d.ts +0 -20
- package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerClient.js +0 -72
- package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.d.ts +0 -15
- package/dist/components/questions/types/look-picture-choose-correct-answer/LookPictureChooseCorrectAnswerCreator.js +0 -230
- package/dist/components/questions/types/look-picture-choose-correct-answer/index.d.ts +0 -1
- package/dist/components/questions/types/look-picture-choose-correct-answer/index.js +0 -1
- package/dist/components/questions/types/look-picture-choose-correct-answer/register.d.ts +0 -2
- package/dist/components/questions/types/look-picture-choose-correct-answer/register.js +0 -66
- package/dist/components/questions/types/look-picture-choose-correct-answer/transform.d.ts +0 -2
- package/dist/components/questions/types/look-picture-choose-correct-answer/transform.js +0 -42
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.d.ts +0 -6
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerClient.js +0 -96
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.d.ts +0 -15
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/LookPictureFillBlankChooseAnswerCreator.js +0 -238
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.d.ts +0 -2
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/index.js +0 -2
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.d.ts +0 -2
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/register.js +0 -76
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.d.ts +0 -2
- package/dist/components/questions/types/look-picture-fill-blank-choose-answer/transform.js +0 -19
- package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.d.ts +0 -7
- package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintClient.js +0 -134
- package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.d.ts +0 -3
- package/dist/components/questions/types/look-picture-fill-word-hint/LookPictureFillWordHintCreator.js +0 -617
- package/dist/components/questions/types/look-picture-fill-word-hint/index.d.ts +0 -2
- package/dist/components/questions/types/look-picture-fill-word-hint/index.js +0 -2
- package/dist/components/questions/types/look-picture-fill-word-hint/register.d.ts +0 -2
- package/dist/components/questions/types/look-picture-fill-word-hint/register.js +0 -82
- package/dist/components/questions/types/look-picture-fill-word-hint/transform.d.ts +0 -2
- package/dist/components/questions/types/look-picture-fill-word-hint/transform.js +0 -29
- package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.d.ts +0 -9
- package/dist/shared/lib/validation/look-picture-choose-correct-answer.validation.js +0 -12
- package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.d.ts +0 -17
- package/dist/shared/lib/validation/look-picture-fill-blank-choose-answer.validation.js +0 -47
- package/dist/shared/types/questions/look-picture-choose-correct-answer.d.ts +0 -38
- package/dist/shared/types/questions/look-picture-choose-correct-answer.js +0 -5
- package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.d.ts +0 -63
- package/dist/shared/types/questions/look-picture-fill-blank-choose-answer.js +0 -9
- package/dist/shared/types/questions/look-picture-fill-word-hint.d.ts +0 -57
- package/dist/shared/types/questions/look-picture-fill-word-hint.js +0 -7
package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerCreator.js
CHANGED
|
@@ -2,15 +2,11 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { ImagePreview } from '../../../../components/ui/image-preview';
|
|
4
4
|
import { useState, useRef, useEffect } from 'react';
|
|
5
|
-
import { Button } from '../../../../components/ui/button';
|
|
6
5
|
import { Label } from '../../../../components/ui/label';
|
|
7
6
|
import { Input } from '../../../../components/ui/input';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
|
|
11
|
-
import { Pencil, Eye, ImageIcon, MessageSquare, HelpCircle, BookOpen, Grid3X3, Users } from 'lucide-react';
|
|
7
|
+
import { ImageIcon, MessageSquare, HelpCircle, Grid3X3, Users } from 'lucide-react';
|
|
8
|
+
import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
|
|
12
9
|
import { FileUpload } from '../../../../components/ui/file-upload';
|
|
13
|
-
import { Checkbox } from '../../../../components/ui/checkbox';
|
|
14
10
|
import { MatchByWritingAnswerClient } from './MatchByWritingAnswerClient';
|
|
15
11
|
import { MatchAnswerGroupCard } from '../../groups/MatchAnswerGroupCard';
|
|
16
12
|
import { useDebouncedCallback, usePresignedFileUrl } from '../../../../shared/lib/hooks';
|
|
@@ -85,6 +81,7 @@ function MatchByWritingAnswerContent({ initialData, onSave, onSaveToQuestionBank
|
|
|
85
81
|
const [isClientMode, setIsClientMode] = useState(false);
|
|
86
82
|
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
|
|
87
83
|
const [imagePreviewUrl, setImagePreviewUrl] = useState('');
|
|
84
|
+
const [isImageInputVisible, setIsImageInputVisible] = useState(() => Boolean(initialData?.imageUrl));
|
|
88
85
|
// Inline validation errors
|
|
89
86
|
const [validationErrors, setValidationErrors] = useState({});
|
|
90
87
|
// Refs
|
|
@@ -332,27 +329,32 @@ function MatchByWritingAnswerContent({ initialData, onSave, onSaveToQuestionBank
|
|
|
332
329
|
isExample,
|
|
333
330
|
options,
|
|
334
331
|
};
|
|
335
|
-
return (_jsxs("div", { className: "space-y-4", children: [_jsx(
|
|
332
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(MatchByWritingAnswerClient, { questionData: questionData, isReviewMode: true, autoFillCorrectAnswers: true, explanation: explanation })] }));
|
|
336
333
|
}
|
|
337
334
|
// Creator UI
|
|
338
|
-
return (_jsxs("div", { className: "space-y-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
335
|
+
return (_jsxs("div", { className: "space-y-3", children: [groups && groups.length > 0 && (_jsx(MatchAnswerGroupCard, { currentGroupNumber: currentGroupNumber, groupTitle: groupTitle, groupImageUrl: groupImageUrl, groupImagePreviewUrl: groupImagePreviewUrl, options: options, groupConfig: groupConfig, showGroupTitle: showGroupTitle, showGroupImageUrl: showGroupImageUrl, onGroupTitleChange: setGroupTitle, onGroupImageChange: setGroupImageUrl, onGroupImagePreviewUrlChange: setGroupImagePreviewUrl, onUpdateOption: updateOption, onRemoveOption: removeOption, onAddOption: addOption })), _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Grid3X3, { className: "h-3.5 w-3.5" }), title: "Gh\u00E9p \u0111\u00E1p \u00E1n b\u1EB1ng c\u00E1ch vi\u1EBFt", points: { value: points || 1, onValueChange: setPoints }, example: { checked: isExample || false, onChange: setIsExample }, headerExtra: headerExtra, preview: {
|
|
336
|
+
onClick: () => setIsClientMode(true),
|
|
337
|
+
disabled: !question,
|
|
338
|
+
} }), children: [viewMode === 'CONVERSATION' && (_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-1.5 text-sm font-semibold text-gray-800", children: [_jsx(Users, { className: "h-3.5 w-3.5 text-indigo-500" }), "H\u1ED9i tho\u1EA1i ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(tu\u1EF3 ch\u1ECDn)" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-2.5 rounded-lg border border-gray-200 bg-gray-50 p-2.5", children: [_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "askerName", className: "text-xs font-medium text-gray-600", children: "T\u00EAn ng\u01B0\u1EDDi h\u1ECFi" }), _jsx(Input, { id: "askerName", value: askerName, onChange: (e) => setAskerName(e.target.value), placeholder: "VD: John, Mary, Teacher...", className: "h-8 border-gray-200 bg-white" })] }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "answererName", className: "text-xs font-medium text-gray-600", children: "T\u00EAn ng\u01B0\u1EDDi tr\u1EA3 l\u1EDDi" }), _jsx(Input, { id: "answererName", value: answererName, onChange: (e) => setAnswererName(e.target.value), placeholder: "VD: Tom, Anna, Student...", className: "h-8 border-gray-200 bg-white" })] })] })] })), showQuestionField && (_jsxs("div", { className: "space-y-1.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs(Label, { htmlFor: "question", className: "flex items-center gap-1.5 text-sm font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-3.5 w-3.5 text-teal-500" }), "T\u00EAn / C\u00E2u h\u1ECFi ", _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(t\u00F9y ch\u1ECDn)" })] }), showImageUrlField && (_jsx("button", { type: "button", title: "Th\u00EAm h\u00ECnh \u1EA3nh", onClick: () => setIsImageInputVisible(true), className: `inline-flex h-8 w-8 items-center justify-center rounded-md transition-colors ${imageUrl || isImageInputVisible
|
|
339
|
+
? 'bg-indigo-600 text-white shadow-sm'
|
|
340
|
+
: 'bg-indigo-50 text-indigo-600 hover:bg-indigo-100'}`, children: _jsx(ImageIcon, { className: "h-4 w-4" }) }))] }), _jsx(Input, { id: "question", value: question, onChange: (e) => {
|
|
341
|
+
setQuestion(e.target.value);
|
|
342
|
+
if (validationErrors.question) {
|
|
343
|
+
setValidationErrors((prev) => ({ ...prev, question: undefined }));
|
|
344
|
+
}
|
|
345
|
+
}, placeholder: "Nh\u1EADp t\u00EAn ho\u1EB7c c\u00E2u h\u1ECFi... (vd: Mum, Dad, Julia)", className: `h-8 border-gray-200 bg-white ${validationErrors.question ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.question && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.question }))] })), showImageUrlField && !showQuestionField && !isImageInputVisible && (_jsxs("button", { type: "button", title: "Th\u00EAm h\u00ECnh \u1EA3nh", onClick: () => setIsImageInputVisible(true), className: "inline-flex h-8 items-center gap-1.5 rounded-md border border-gray-200 bg-white px-2.5 text-xs font-medium text-gray-500 hover:text-gray-700", children: [_jsx(ImageIcon, { className: "h-3.5 w-3.5" }), "Th\u00EAm h\u00ECnh \u1EA3nh"] })), showImageUrlField && isImageInputVisible && (_jsxs("div", { className: "space-y-2", children: [_jsx(FileUpload, { id: "question-image", label: "", accept: "image/*", value: imageUrl || '', onChange: (url) => {
|
|
346
|
+
setImageUrl(url);
|
|
347
|
+
if (validationErrors.imageUrl) {
|
|
348
|
+
setValidationErrors((prev) => ({ ...prev, imageUrl: undefined }));
|
|
349
|
+
}
|
|
350
|
+
}, onPresignedUrlChange: setImagePreviewUrl, maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" }), displayPreviewUrl && (_jsx(ImagePreview, { src: displayPreviewUrl, alt: "Preview", className: "mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200" }))] })), showAnswerField && (_jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { htmlFor: "correctAnswer", className: "flex items-center gap-1.5 text-sm font-semibold text-gray-800", children: [_jsx(HelpCircle, { className: "h-3.5 w-3.5 text-green-500" }), "\u0110\u00E1p \u00E1n \u0111\u00FAng ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsxs("select", { id: "correctAnswer", value: correctAnswer, onChange: (e) => {
|
|
351
|
+
setCorrectAnswer(e.target.value);
|
|
352
|
+
if (validationErrors.correctAnswer) {
|
|
353
|
+
setValidationErrors((prev) => ({ ...prev, correctAnswer: undefined }));
|
|
354
|
+
}
|
|
355
|
+
}, className: `flex h-8 w-full rounded-md border px-3 py-1 text-sm bg-white focus:outline-none focus:ring-2 focus:ring-offset-0 ${validationErrors.correctAnswer ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : 'border-gray-200 focus:border-green-300 focus:ring-green-200'}`, children: [_jsx("option", { value: "", children: "-- Ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng --" }), options
|
|
356
|
+
.filter((opt) => opt.answer && opt.answer.trim() !== '')
|
|
357
|
+
.map((opt, idx) => (_jsx("option", { value: opt.answer, children: opt.answer }, idx)))] }), validationErrors.correctAnswer && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.correctAnswer }))] })), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-red-200 bg-red-50 p-2 text-xs text-red-700", children: [_jsx("span", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("span", { children: externalErrors.join(' · ') })] })), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] }));
|
|
356
358
|
}
|
|
357
359
|
export function MatchByWritingAnswerCreator(props) {
|
|
358
360
|
return _jsx(MatchByWritingAnswerContent, { ...props });
|
|
@@ -58,17 +58,17 @@ export function MatchColumnsToMakeSentencesClient({ questionData, isReviewMode =
|
|
|
58
58
|
|| '—';
|
|
59
59
|
const isCorrect = isReviewMode &&
|
|
60
60
|
current.sentence.trim().toLowerCase() === item.sentence.trim().toLowerCase();
|
|
61
|
-
return (_jsxs("div", { className: "rounded-xl border border-gray-200 bg-white
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
61
|
+
return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", item.questionNumber || itemIndex + 1] }), item.isExample && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-0.5 text-xs font-semibold text-amber-700", children: [_jsx(Star, { className: "h-3 w-3" }), "Example"] }))] }), _jsxs("span", { className: "text-xs text-gray-500", children: [item.points || 0, " \u0111i\u1EC3m"] })] }), _jsxs("div", { className: "space-y-3 p-3", children: [_jsxs("div", { className: "grid gap-3 md:grid-cols-[minmax(0,1fr)_repeat(2,minmax(0,1fr))]", children: [_jsx("div", { className: "rounded-lg border border-slate-200 bg-slate-50 px-3 py-2 text-sm font-semibold text-slate-800", children: stemText }), choiceColumns.map((column, choiceIndex) => {
|
|
62
|
+
const selectedId = current.fragmentIds[choiceIndex + 1] || '';
|
|
63
|
+
return (_jsxs("select", { className: cn('rounded-lg border-2 px-3 py-2 text-sm', isReviewMode && selectedId === item.fragmentIds[choiceIndex + 1]
|
|
64
|
+
? 'border-green-400 bg-green-50'
|
|
65
|
+
: selectedId
|
|
66
|
+
? 'border-blue-400 bg-blue-50'
|
|
67
|
+
: 'border-gray-200 bg-white'), disabled: !canSelect, value: selectedId, onChange: (event) => handleChoiceChange(itemIndex, choiceIndex, event.target.value), children: [_jsxs("option", { value: "", children: ["Ch\u1ECDn ", column.label || column.id] }), column.fragments.map((fragment) => (_jsxs("option", { value: fragment.id, children: [fragment.id, ". ", fragment.text] }, fragment.id)))] }, `${itemIndex}-${column.id}`));
|
|
68
|
+
})] }), _jsx("p", { className: cn('text-sm', isReviewMode
|
|
69
|
+
? isCorrect
|
|
70
|
+
? 'text-green-700'
|
|
71
|
+
: 'text-red-700'
|
|
72
|
+
: 'text-slate-600'), children: current.sentence || 'Chưa ghép câu' }), isReviewMode && !item.isExample && (_jsxs("p", { className: "text-xs text-slate-500", children: ["\u0110\u00E1p \u00E1n: ", item.sentence] }))] })] }, `${item.questionNumber}-${itemIndex}`));
|
|
73
73
|
}) }), isReviewMode && questionData.explanation && (_jsxs("div", { className: "flex items-start gap-2 rounded-xl border border-amber-100 bg-amber-50 px-4 py-3 text-sm text-amber-800", children: [_jsx(Lightbulb, { className: "mt-0.5 h-4 w-4 flex-shrink-0" }), questionData.explanation] }))] }));
|
|
74
74
|
}
|
|
@@ -5,11 +5,11 @@ import { Button } from '../../../../components/ui/button';
|
|
|
5
5
|
import { Label } from '../../../../components/ui/label';
|
|
6
6
|
import { Input } from '../../../../components/ui/input';
|
|
7
7
|
import { PointsInput } from '../../../../components/ui/points-input';
|
|
8
|
-
import { Textarea } from '../../../../components/ui/textarea';
|
|
9
8
|
import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
|
|
10
9
|
import { Switch } from '../../../../components/ui/switch';
|
|
11
10
|
import { useDebouncedCallback } from '../../../../shared/lib/hooks';
|
|
12
|
-
import {
|
|
11
|
+
import { Columns3, Plus, Star, Trash2 } from 'lucide-react';
|
|
12
|
+
import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
|
|
13
13
|
import { MatchColumnsToMakeSentencesClient } from './MatchColumnsToMakeSentencesClient';
|
|
14
14
|
import { MATCH_COLUMNS_DEFAULT_INSTRUCTION } from '../../_shared/types/match-columns-to-make-sentences.type';
|
|
15
15
|
function parsePointsValue(raw, allowZero = false) {
|
|
@@ -216,7 +216,10 @@ export function MatchColumnsToMakeSentencesCreator({ initialData, onChange, exte
|
|
|
216
216
|
const displayErrors = externalErrors && externalErrors.length > 0 ? externalErrors : errors;
|
|
217
217
|
const stemColumn = getMatchColumnsStemColumn(columns);
|
|
218
218
|
const choiceColumns = getMatchColumnsChoiceColumns(columns);
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
219
|
+
if (isClientMode) {
|
|
220
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(MatchColumnsToMakeSentencesClient, { questionData: currentPayload })] }));
|
|
221
|
+
}
|
|
222
|
+
return (_jsxs("div", { className: "space-y-3", children: [displayErrors.length > 0 && (_jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-2 text-xs text-red-700", children: _jsx("ul", { className: "list-disc space-y-1 pl-4", children: displayErrors.map((err, i) => _jsx("li", { children: err }, i)) }) })), _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Columns3, { className: "h-3.5 w-3.5" }), title: "Gh\u00E9p c\u1ED9t th\u00E0nh c\u00E2u", preview: { onClick: () => setIsClientMode(true) } }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "match-columns-title", className: "text-sm font-semibold text-gray-800", children: "Ti\u00EAu \u0111\u1EC1" }), _jsx(Input, { id: "match-columns-title", className: "h-10 border-gray-200 bg-white", value: title, onChange: (e) => setTitle(e.target.value) })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "match-columns-instruction", className: "text-sm font-semibold text-gray-800", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Input, { id: "match-columns-instruction", className: "h-10 border-gray-200 bg-white", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: MATCH_COLUMNS_DEFAULT_INSTRUCTION })] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsx(CardTitle, { className: "text-sm font-semibold text-gray-800", children: "C\u1ED9t fragment" }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addChoiceColumn, className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm c\u1ED9t choice"] })] }) }), _jsx(CardContent, { className: "grid gap-2.5 px-3 pb-3 md:grid-cols-3", children: columns.map((column, columnIndex) => (_jsxs("div", { className: "space-y-2.5 rounded-md border border-gray-200 bg-white px-3 py-2.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(Input, { value: column.label || '', onChange: (e) => updateColumn(columnIndex, { label: e.target.value }), placeholder: "Nh\u00E3n c\u1ED9t", className: "h-10 border-gray-200 bg-white" }), _jsx("span", { className: "text-xs text-slate-500", children: column.role }), column.role === 'choice' && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeColumn(columnIndex), className: "h-7 w-7", children: _jsx(Trash2, { className: "h-3.5 w-3.5 text-red-500" }) }))] }), column.fragments.map((fragment, fragmentIndex) => (_jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { className: "h-10 w-16 border-gray-200 bg-white", value: fragment.id, onChange: (e) => updateFragment(columnIndex, fragmentIndex, { id: e.target.value }) }), _jsx(Input, { value: fragment.text, onChange: (e) => updateFragment(columnIndex, fragmentIndex, { text: e.target.value }), placeholder: "Text", className: "h-10 border-gray-200 bg-white" }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeFragment(columnIndex, fragmentIndex), className: "h-7 w-7", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }, `${column.id}-${fragmentIndex}`))), _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: () => addFragment(columnIndex), className: "h-8 gap-1.5 px-2.5 text-xs", children: "Th\u00EAm fragment" })] }, column.id))) })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs(CardTitle, { className: "text-sm font-semibold text-gray-800", children: ["C\u00E2u h\u1ECFi (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addItem, className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm c\u00E2u"] })] }) }), _jsxs(CardContent, { className: "space-y-2.5 px-3 pb-3", children: [items.map((item, index) => (_jsxs("div", { className: "space-y-2.5 rounded-md border border-gray-200 bg-white px-3 py-2.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", item.questionNumber || index + 1] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: `match-columns-example-${index}`, checked: item.isExample || false, onCheckedChange: (checked) => updateItem(index, { isExample: checked, points: checked ? 0 : 1 }) }), _jsxs(Label, { htmlFor: `match-columns-example-${index}`, className: "flex items-center gap-1 text-xs", children: [_jsx(Star, { className: "h-3 w-3 text-amber-500" }), "Example"] }), !item.isExample && (_jsx("div", { className: "w-24", children: _jsx(PointsInput, { value: item.points, allowZero: true, onChange: (event) => updateItem(index, {
|
|
223
|
+
points: parsePointsValue(event.target.value, true),
|
|
224
|
+
}) }) })), items.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeItem(index), className: "h-7 w-7 text-red-500 hover:bg-red-50", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] })] }), _jsxs("div", { className: "grid gap-2.5 md:grid-cols-3", children: [_jsxs("select", { className: "rounded-md border px-3 py-2 text-sm", value: item.stemFragmentId, onChange: (e) => updateItem(index, { stemFragmentId: e.target.value }), children: [_jsx("option", { value: "", children: "Ch\u1ECDn stem" }), (stemColumn?.fragments || []).map((fragment) => (_jsxs("option", { value: fragment.id, children: [fragment.id, ". ", fragment.text || '(trống)'] }, fragment.id)))] }), choiceColumns.map((column, choiceIndex) => (_jsxs("select", { className: "rounded-md border px-3 py-2 text-sm", value: item.fragmentIds[choiceIndex + 1] || '', onChange: (e) => setItemChoice(index, choiceIndex, e.target.value), children: [_jsxs("option", { value: "", children: ["Ch\u1ECDn ", column.label || column.id] }), column.fragments.map((fragment) => (_jsxs("option", { value: fragment.id, children: [fragment.id, ". ", fragment.text || '(trống)'] }, fragment.id)))] }, `${index}-${column.id}`)))] }), _jsx("p", { className: "text-sm text-slate-600", children: item.sentence || 'Câu sẽ tự ghép từ fragment đã chọn.' })] }, index))), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] })] }));
|
|
222
225
|
}
|
|
@@ -20,12 +20,12 @@ export function MatchWordToPictureClient({ questionData, onSubmit, isReviewMode
|
|
|
20
20
|
const isCorrect = isReviewMode && userAnswer !== undefined
|
|
21
21
|
? userAnswer.trim().toLowerCase() === questionData.correctAnswer.trim().toLowerCase()
|
|
22
22
|
: undefined;
|
|
23
|
-
return (
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
return (_jsx(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: _jsxs(CardContent, { className: "space-y-3 p-3", children: [_jsx("p", { className: "text-sm font-semibold uppercase tracking-wide text-violet-700", children: questionData.instruction || MATCH_WORD_TO_PICTURE_DEFAULT_INSTRUCTION }), wordBank.length > 0 && (_jsxs("div", { className: "rounded-xl border border-violet-200 bg-gradient-to-r from-violet-50 to-indigo-50 p-4", children: [_jsx("p", { className: "mb-3 text-sm font-semibold text-violet-700", children: "Word bank:" }), _jsx("div", { className: "flex flex-wrap gap-2", children: wordBank.map((word, index) => (_jsx("span", { className: "rounded-full border border-violet-200 bg-white px-3 py-1 text-sm font-semibold text-slate-700", children: word }, `${word}-${index}`))) })] })), _jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-5", children: [isExample && (_jsxs("div", { className: "mb-2 inline-flex items-center gap-1.5 rounded-full border border-amber-300 bg-amber-100 px-3 py-1", children: [_jsx(BookOpen, { className: "h-3.5 w-3.5 text-amber-600" }), _jsx("span", { className: "text-xs font-semibold uppercase tracking-wide text-amber-700", children: "Example" })] })), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx("div", { className: "relative h-24 w-24 flex-shrink-0 overflow-hidden rounded-lg border border-gray-200 bg-gray-50 shadow-sm", children: _jsx("img", { src: questionImageUrl || questionData.imageUrl, alt: questionData.correctAnswer || 'Picture', className: "h-full w-full object-contain" }) }), _jsxs("div", { className: "flex-1 space-y-2", children: [_jsx(Label, { className: "text-base font-semibold text-gray-800", children: "Ch\u1ECDn t\u1EEB ph\u00F9 h\u1EE3p" }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsxs("select", { value: displayedAnswer, onChange: (event) => {
|
|
24
|
+
setAnswer(event.target.value);
|
|
25
|
+
onSubmit?.(event.target.value);
|
|
26
|
+
}, disabled: isReviewMode || isExample, className: `h-10 max-w-xs rounded-md border-2 bg-white px-3 text-sm font-medium ${isReviewMode
|
|
27
|
+
? isCorrect
|
|
28
|
+
? 'border-green-400 bg-green-50 text-green-700'
|
|
29
|
+
: 'border-red-400 bg-red-50 text-red-700'
|
|
30
|
+
: 'border-violet-300 focus:border-violet-500 focus:ring-violet-200'}`, children: [_jsx("option", { value: "", children: "-- Ch\u1ECDn t\u1EEB --" }), wordBank.map((word, index) => (_jsx("option", { value: word, children: word }, `${word}-${index}`)))] }), isReviewMode && isCorrect !== undefined && (_jsx("span", { className: "flex items-center gap-1", children: isCorrect ? (_jsx(CheckCircle2, { className: "h-5 w-5 text-green-500" })) : (_jsx(XCircle, { className: "h-5 w-5 text-red-500" })) }))] })] })] }), isReviewMode && isCorrect === false && (_jsxs("div", { className: "mt-3 flex items-center gap-2 rounded-lg border border-green-200 bg-green-50 px-4 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), _jsxs("span", { className: "text-sm text-green-700", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng: ", _jsx("strong", { children: questionData.correctAnswer })] })] }))] }), explanation && isReviewMode && (_jsxs("div", { className: "rounded-xl border border-blue-200 bg-blue-50 p-4", children: [_jsx("p", { className: "mb-1 text-sm font-semibold text-blue-800", children: "Gi\u1EA3i th\u00EDch:" }), _jsx("p", { className: "text-sm text-blue-700", children: explanation })] }))] }) }));
|
|
31
31
|
}
|
|
@@ -2,14 +2,10 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { ImagePreview } from '../../../../components/ui/image-preview';
|
|
4
4
|
import { useEffect, useRef, useState } from 'react';
|
|
5
|
-
import { Button } from '../../../../components/ui/button';
|
|
6
5
|
import { Label } from '../../../../components/ui/label';
|
|
7
|
-
import {
|
|
8
|
-
import { PointsInput } from '../../../../components/ui/points-input';
|
|
9
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
|
|
10
|
-
import { BookOpen, Eye, HelpCircle, ImageIcon, Pencil } from 'lucide-react';
|
|
6
|
+
import { HelpCircle, ImageIcon } from 'lucide-react';
|
|
11
7
|
import { FileUpload } from '../../../../components/ui/file-upload';
|
|
12
|
-
import {
|
|
8
|
+
import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
|
|
13
9
|
import { MATCH_WORD_TO_PICTURE_DEFAULT_INSTRUCTION, } from '../../_shared/types/match-word-to-picture.type';
|
|
14
10
|
import { MatchWordToPictureClient } from './MatchWordToPictureClient';
|
|
15
11
|
import { WordBankEditor } from './WordBankEditor';
|
|
@@ -238,7 +234,7 @@ function MatchWordToPictureContent({ initialData, onChange, onUnsavedChangesChan
|
|
|
238
234
|
debouncedOnChange,
|
|
239
235
|
]);
|
|
240
236
|
if (isClientMode) {
|
|
241
|
-
return (_jsxs("div", { className: "space-y-4", children: [_jsx(
|
|
237
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(MatchWordToPictureClient, { questionData: {
|
|
242
238
|
imageUrl: displayPreviewUrl || imageUrl,
|
|
243
239
|
correctAnswer,
|
|
244
240
|
points,
|
|
@@ -248,19 +244,28 @@ function MatchWordToPictureContent({ initialData, onChange, onUnsavedChangesChan
|
|
|
248
244
|
instruction,
|
|
249
245
|
}, isReviewMode: true, autoFillCorrectAnswers: true, explanation: explanation })] }));
|
|
250
246
|
}
|
|
251
|
-
return (_jsxs("div", { className: "space-y-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
247
|
+
return (_jsxs("div", { className: "space-y-3", children: [groups && groups.length > 0 && (_jsx(WordBankEditor, { wordBank: wordBank, instruction: instruction, onWordBankChange: setWordBank, onInstructionChange: setInstruction })), _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(ImageIcon, { className: "h-3.5 w-3.5" }), title: "N\u1ED1i t\u1EEB v\u1EDBi tranh", points: {
|
|
248
|
+
value: points || 1,
|
|
249
|
+
onValueChange: setPoints,
|
|
250
|
+
}, example: {
|
|
251
|
+
checked: isExample || false,
|
|
252
|
+
onChange: setIsExample,
|
|
253
|
+
}, preview: {
|
|
254
|
+
onClick: () => setIsClientMode(true),
|
|
255
|
+
disabled: !imageUrl,
|
|
256
|
+
} }), children: [(!groups || groups.length === 0) && (_jsx(WordBankEditor, { wordBank: wordBank, instruction: instruction, onWordBankChange: setWordBank, onInstructionChange: setInstruction })), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(ImageIcon, { className: "h-4 w-4 text-violet-500" }), "H\u00ECnh \u1EA3nh ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(FileUpload, { id: "mwtp-question-image", label: "", accept: "image/*", value: imageUrl || '', onChange: (url) => {
|
|
257
|
+
setImageUrl(url);
|
|
258
|
+
if (validationErrors.imageUrl) {
|
|
259
|
+
setValidationErrors((prev) => ({ ...prev, imageUrl: undefined }));
|
|
260
|
+
}
|
|
261
|
+
}, onPresignedUrlChange: setImagePreviewUrl, maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" }), displayPreviewUrl && (_jsx(ImagePreview, { src: displayPreviewUrl, alt: "Preview", className: "mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200" })), validationErrors.imageUrl && _jsx("p", { className: "text-sm text-red-600", children: validationErrors.imageUrl })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "mwtp-correctAnswer", className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(HelpCircle, { className: "h-4 w-4 text-green-500" }), "\u0110\u00E1p \u00E1n \u0111\u00FAng ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsxs("select", { id: "mwtp-correctAnswer", value: correctAnswer, onChange: (event) => {
|
|
262
|
+
setCorrectAnswer(event.target.value);
|
|
263
|
+
if (validationErrors.correctAnswer) {
|
|
264
|
+
setValidationErrors((prev) => ({ ...prev, correctAnswer: undefined }));
|
|
265
|
+
}
|
|
266
|
+
}, className: `flex h-10 w-full rounded-md border bg-white px-3 py-2 text-sm ${validationErrors.correctAnswer
|
|
267
|
+
? 'border-red-400 focus:border-red-500'
|
|
268
|
+
: 'border-gray-200 focus:border-green-300'}`, children: [_jsx("option", { value: "", children: "-- Ch\u1ECDn t\u1EEB trong word bank --" }), usableWords.map((word, index) => (_jsx("option", { value: word, children: word }, `${word}-${index}`)))] }), validationErrors.correctAnswer && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.correctAnswer })), validationErrors.wordBank && _jsx("p", { className: "text-sm text-red-600", children: validationErrors.wordBank })] }), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-red-200 bg-red-50 p-2 text-xs text-red-700", children: [_jsx("span", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("span", { children: externalErrors.join(' · ') })] }))] }), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation })] }));
|
|
264
269
|
}
|
|
265
270
|
export function MatchWordToPictureCreator(props) {
|
|
266
271
|
return _jsx(MatchWordToPictureContent, { ...props });
|
package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupClient.js
CHANGED
|
@@ -81,7 +81,7 @@ export function MatchWordToPictureGroupClient({ questionData, isReviewMode = fal
|
|
|
81
81
|
const canSelect = !isReviewMode && !item.isExample && Boolean(onAnswerChange);
|
|
82
82
|
const isCorrect = normalizeWord(selectedWord) === normalizeWord(item.correctAnswer) &&
|
|
83
83
|
Boolean(item.correctAnswer);
|
|
84
|
-
return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-
|
|
84
|
+
return (_jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-violet-500 to-indigo-600 text-white", children: _jsx(CircleHelp, { className: "h-3.5 w-3.5" }) }), _jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u00E2u ", item.questionNumber || itemIndex + 1] }), item.isExample && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-amber-100 px-2 py-0.5 text-xs font-semibold text-amber-700", children: [_jsx(Star, { className: "h-3 w-3" }), "Example"] }))] }), _jsxs("span", { className: "text-xs text-gray-500", children: [item.points || 0, " \u0111i\u1EC3m"] })] }), _jsxs("div", { className: "flex flex-col space-y-3 p-3 sm:flex-row sm:items-center sm:space-y-0 sm:gap-3", children: [toMatchWordToPictureImageUrls(item.imageUrl).length > 0 ? (_jsx("div", { className: "flex flex-wrap gap-2", children: toMatchWordToPictureImageUrls(item.imageUrl).map((url, imageIndex) => (_jsx(ClientImageItem, { url: url, alt: `Hình ảnh câu ${item.questionNumber || itemIndex + 1}${imageIndex > 0 ? ` (${imageIndex + 1})` : ''}` }, `${item.questionNumber}-${imageIndex}`))) })) : null, _jsxs("div", { className: "min-w-0 flex-1 space-y-2", children: [_jsxs("p", { className: "text-sm font-semibold text-gray-800", children: [item.questionNumber || itemIndex + 1, ". Ch\u1ECDn t\u1EEB ph\u00F9 h\u1EE3p"] }), _jsxs("div", { className: "flex items-center gap-3", children: [item.isExample ? (_jsx("span", { className: "text-sm italic text-gray-800 underline decoration-gray-400", children: item.correctAnswer })) : (_jsxs("select", { value: selectedWord, disabled: !canSelect, onChange: (event) => handleSelect(itemIndex, event.target.value), className: cn('h-10 max-w-xs rounded-md border-2 bg-white px-3 text-sm font-medium', isReviewMode && isCorrect
|
|
85
85
|
? 'border-green-400 bg-green-50 text-green-700'
|
|
86
86
|
: isReviewMode && selectedWord && !isCorrect
|
|
87
87
|
? 'border-red-400 bg-red-50 text-red-700'
|
|
@@ -5,7 +5,6 @@ import { Button } from '../../../../components/ui/button';
|
|
|
5
5
|
import { Label } from '../../../../components/ui/label';
|
|
6
6
|
import { Input } from '../../../../components/ui/input';
|
|
7
7
|
import { PointsInput } from '../../../../components/ui/points-input';
|
|
8
|
-
import { Textarea } from '../../../../components/ui/textarea';
|
|
9
8
|
import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
|
|
10
9
|
import { Switch } from '../../../../components/ui/switch';
|
|
11
10
|
import { useDebouncedCallback } from '../../../../shared/lib/hooks';
|
|
@@ -18,7 +17,8 @@ function parsePointsValue(raw, allowZero = false) {
|
|
|
18
17
|
const min = allowZero ? 0 : 1;
|
|
19
18
|
return Math.max(min, parsed);
|
|
20
19
|
}
|
|
21
|
-
import {
|
|
20
|
+
import { ImageIcon, Plus, Star, Trash2 } from 'lucide-react';
|
|
21
|
+
import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
|
|
22
22
|
import { RichTextEditor } from '../../../../components/shared/RichTextEditor';
|
|
23
23
|
import { MatchWordToPictureGroupClient } from './MatchWordToPictureGroupClient';
|
|
24
24
|
import { MatchWordToPictureGroupImageField } from './MatchWordToPictureGroupImageField';
|
|
@@ -99,12 +99,15 @@ export function MatchWordToPictureGroupCreator({ initialData, onChange, external
|
|
|
99
99
|
};
|
|
100
100
|
const currentPayload = buildPayload();
|
|
101
101
|
const displayErrors = externalErrors && externalErrors.length > 0 ? externalErrors : errors;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
102
|
+
if (isClientMode) {
|
|
103
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(MatchWordToPictureGroupClient, { questionData: currentPayload })] }));
|
|
104
|
+
}
|
|
105
|
+
return (_jsxs("div", { className: "space-y-3", children: [displayErrors.length > 0 && (_jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-2 text-xs text-red-700", children: _jsx("ul", { className: "list-disc space-y-1 pl-4", children: displayErrors.map((err, i) => _jsx("li", { children: err }, i)) }) })), _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(ImageIcon, { className: "h-3.5 w-3.5" }), title: "N\u1ED1i t\u1EEB v\u1EDBi tranh (Nh\u00F3m)", preview: { onClick: () => setIsClientMode(true) } }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "mwtp-group-title", className: "text-sm font-semibold text-gray-800", children: "Ti\u00EAu \u0111\u1EC1 (optional)" }), _jsx(Input, { id: "mwtp-group-title", className: "h-10 border-gray-200 bg-white", value: title, onChange: (e) => setTitle(e.target.value), placeholder: "VOCABULARY" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "mwtp-group-instruction", className: "text-sm font-semibold text-gray-800", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Input, { id: "mwtp-group-instruction", className: "h-10 border-gray-200 bg-white", value: instruction, onChange: (e) => setInstruction(e.target.value) })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "N\u1ED9i dung b\u00E0i \u0111\u1ECDc (Passage)" }), _jsx(RichTextEditor, { value: passage, onChange: setPassage, minHeightClassName: "min-h-[160px]" })] }), _jsx(MatchWordToPictureGroupImageField, { id: "mwtp-group-shared-image", label: "\u1EA2nh chung (optional \u2014 1 b\u1EE9c tranh nhi\u1EC1u \u00F4 s\u1ED1)", value: imageUrl, onChange: setImageUrl }), wordBank.map((word, index) => (_jsxs("div", { className: "flex gap-2", children: [_jsx(Input, { value: word, onChange: (e) => setWordBank((prev) => prev.map((item, i) => (i === index ? e.target.value : item))), placeholder: `Từ ${index + 1}`, className: "h-10 border-gray-200 bg-white" }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => setWordBank((prev) => prev.filter((_, i) => i !== index)), className: "h-7 w-7", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }, index))), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setWordBank((prev) => [...prev, '']), className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm t\u1EEB"] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs(CardTitle, { className: "text-sm font-semibold text-gray-800", children: ["Tranh (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setItems((prev) => [...prev, createEmptyMatchWordToPictureGroupItem(prev.length + 1)]), className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm tranh"] })] }) }), _jsxs(CardContent, { className: "space-y-2.5 px-3 pb-3", children: [items.map((item, index) => (_jsxs("div", { className: "space-y-2.5 rounded-md border border-gray-200 bg-white px-3 py-2.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("span", { className: "text-sm font-medium", children: ["C\u00E2u ", item.questionNumber || index + 1] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: item.isExample || false, onCheckedChange: (checked) => updateItem(index, { isExample: checked, points: checked ? 0 : 1 }) }), _jsxs(Label, { className: "flex items-center gap-1 text-xs", children: [_jsx(Star, { className: "h-3 w-3 text-amber-500" }), "Example"] }), !item.isExample && (_jsx("div", { className: "w-24", children: _jsx(PointsInput, { value: item.points, allowZero: true, onChange: (event) => updateItem(index, {
|
|
106
|
+
points: parsePointsValue(event.target.value, true),
|
|
107
|
+
}) }) })), items.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => setItems((prev) => prev.filter((_, i) => i !== index).map((entry, i) => ({ ...entry, questionNumber: i + 1 }))), className: "h-7 w-7 text-red-500 hover:bg-red-50", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] })] }), _jsx(MatchWordToPictureGroupImageField, { id: `mwtp-group-image-${index}`, label: "\u1EA2nh", value: toMatchWordToPictureImageUrls(item.imageUrl)[0] || '', onChange: (url) => updateItem(index, {
|
|
108
|
+
imageUrl: fromMatchWordToPictureImageUrls(url),
|
|
109
|
+
}) }), _jsxs("select", { className: "rounded-md border px-3 py-2 text-sm", value: item.correctAnswer, onChange: (e) => updateItem(index, { correctAnswer: e.target.value }), children: [_jsx("option", { value: "", children: "Ch\u1ECDn t\u1EEB \u0111\u00FAng" }), Array.from(new Set([
|
|
110
|
+
...wordBank.map((word) => word.trim()).filter(Boolean),
|
|
111
|
+
item.correctAnswer.trim(),
|
|
112
|
+
].filter(Boolean))).map((word) => (_jsx("option", { value: word, children: word }, word)))] })] }, index))), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] })] }));
|
|
110
113
|
}
|
package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupCreator.js
CHANGED
|
@@ -4,12 +4,12 @@ import { useEffect, useRef, useState } from 'react';
|
|
|
4
4
|
import { Button } from '../../../../components/ui/button';
|
|
5
5
|
import { Label } from '../../../../components/ui/label';
|
|
6
6
|
import { Input } from '../../../../components/ui/input';
|
|
7
|
-
import { Textarea } from '../../../../components/ui/textarea';
|
|
8
7
|
import { PointsInput } from '../../../../components/ui/points-input';
|
|
9
8
|
import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
|
|
10
9
|
import { Switch } from '../../../../components/ui/switch';
|
|
11
10
|
import { useDebouncedCallback } from '../../../../shared/lib/hooks';
|
|
12
|
-
import {
|
|
11
|
+
import { Link2, Plus, Star, Trash2 } from 'lucide-react';
|
|
12
|
+
import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
|
|
13
13
|
import { RichTextEditor } from '../../../../components/shared/RichTextEditor';
|
|
14
14
|
import { MatchingWithLinesGroupClient } from './MatchingWithLinesGroupClient';
|
|
15
15
|
import { MatchingWithLinesImageField } from './MatchingWithLinesImageField';
|
|
@@ -186,7 +186,10 @@ export function MatchingWithLinesGroupCreator({ initialData, onChange, externalE
|
|
|
186
186
|
};
|
|
187
187
|
const currentPayload = buildPayload();
|
|
188
188
|
const displayErrors = externalErrors && externalErrors.length > 0 ? externalErrors : errors;
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
189
|
+
if (isClientMode) {
|
|
190
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(MatchingWithLinesGroupClient, { questionData: currentPayload, userAnswers: currentPayload.items.map((item) => item.correctAnswer) })] }));
|
|
191
|
+
}
|
|
192
|
+
return (_jsxs("div", { className: "space-y-3", children: [displayErrors.length > 0 && (_jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-2 text-xs text-red-700", children: _jsx("ul", { className: "list-disc space-y-1 pl-4", children: displayErrors.map((err, i) => (_jsx("li", { children: err }, i))) }) })), _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Link2, { className: "h-3.5 w-3.5" }), title: "N\u1ED1i b\u1EB1ng \u0111\u01B0\u1EDDng k\u1EBB (Nh\u00F3m)", preview: { onClick: () => setIsClientMode(true) } }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "mwl-title", className: "text-sm font-semibold text-gray-800", children: "Ti\u00EAu \u0111\u1EC1 (optional)" }), _jsx(Input, { id: "mwl-title", value: title, onChange: (e) => setTitle(e.target.value), placeholder: "VOCABULARY", className: "h-10 border-gray-200 bg-white" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "mwl-instruction", className: "text-sm font-semibold text-gray-800", children: "H\u01B0\u1EDBng d\u1EABn b\u00E0i l\u00E0m" }), _jsx(Input, { id: "mwl-instruction", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: MATCHING_WITH_LINES_GROUP_DEFAULT_INSTRUCTION, className: "h-10 border-gray-200 bg-white" })] }), _jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { className: "text-sm font-semibold text-gray-800", children: "N\u1ED9i dung b\u00E0i \u0111\u1ECDc (Passage)" }), _jsx(RichTextEditor, { value: passage, onChange: setPassage, minHeightClassName: "min-h-[160px]" })] }), _jsx(MatchingWithLinesImageField, { id: "mwl-group-image", label: "\u1EA2nh chung (optional)", value: imageUrl, onChange: setImageUrl })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs(CardTitle, { className: "text-sm font-semibold text-gray-800", children: ["C\u1EB7p n\u1ED1i (", items.length, ")"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addPair, className: "h-8 gap-1.5 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm c\u1EB7p"] })] }) }), _jsxs(CardContent, { className: "space-y-2.5 px-3 pb-3", children: [items.map((item, index) => (_jsxs("div", { className: "space-y-2.5 rounded-md border border-gray-200 bg-white px-3 py-2.5", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsxs("span", { className: "text-sm font-medium text-gray-700", children: ["C\u1EB7p ", index + 1] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: `mwl-example-${index}`, checked: item.isExample || false, onCheckedChange: (checked) => updateItem(index, { isExample: checked, points: checked ? 0 : 1 }) }), _jsxs(Label, { htmlFor: `mwl-example-${index}`, className: "flex cursor-pointer items-center gap-1 text-xs", children: [_jsx(Star, { className: "h-3 w-3 text-amber-500" }), "Example"] })] }), !item.isExample && (_jsx("div", { className: "w-24", children: _jsx(PointsInput, { value: item.points, allowZero: true, onChange: (event) => updateItem(index, {
|
|
193
|
+
points: parsePointsValue(event.target.value, true),
|
|
194
|
+
}) }) })), items.length > 2 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removePair(index), className: "h-7 w-7 text-red-500 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] })] }), _jsxs("div", { className: "grid gap-2.5 md:grid-cols-2", children: [_jsxs("div", { className: "space-y-2.5", children: [_jsx(MatchingWithLinesImageField, { id: `mwl-image-${index}`, label: "\u1EA2nh c\u1ED9t tr\u00E1i", value: item.imageUrl || '', onChange: (url) => updateItem(index, { imageUrl: url }) }), _jsxs("div", { children: [_jsx(Label, { htmlFor: `mwl-left-text-${index}`, children: "Ch\u1EEF c\u1ED9t tr\u00E1i (text-to-text)" }), _jsx(Input, { id: `mwl-left-text-${index}`, value: item.text || '', onChange: (e) => updateItem(index, { text: e.target.value }), placeholder: "ball", className: "mt-1 bg-white" })] })] }), _jsxs("div", { className: "space-y-2.5", children: [_jsx(MatchingWithLinesImageField, { id: `mwl-right-image-${index}`, label: "\u1EA2nh c\u1ED9t ph\u1EA3i (image-to-image)", value: rightItems[index]?.imageUrl || '', onChange: (url) => updateRightItem(index, { imageUrl: url || undefined }) }), _jsxs("div", { children: [_jsx(Label, { htmlFor: `mwl-right-text-${index}`, children: "T\u1EEB c\u1ED9t ph\u1EA3i" }), _jsx(Input, { id: `mwl-right-text-${index}`, value: rightItems[index]?.text || '', onChange: (e) => updateRightItem(index, { text: e.target.value }), placeholder: "paintbrush", className: "mt-1 bg-white" })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: `mwl-answer-${index}`, children: "\u0110\u00E1p \u00E1n \u0111\u00FAng (id t\u1EEB)" }), _jsxs("select", { id: `mwl-answer-${index}`, value: item.correctAnswer, onChange: (e) => updateItem(index, { correctAnswer: e.target.value }), className: "mt-1 w-full rounded-md border border-gray-300 bg-white px-3 py-2 text-sm", children: [_jsx("option", { value: "", children: "Ch\u1ECDn t\u1EEB \u0111\u00FAng" }), rightItems.map((right) => (_jsxs("option", { value: right.id, children: [right.text || right.id, " (", right.id, ")"] }, right.id)))] })] })] })] })] }, `${rightItems[index]?.id || index}`))), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] })] }));
|
|
192
195
|
}
|
package/dist/components/questions/types/read-displayed-content/ReadDisplayedContentCreator.js
CHANGED
|
@@ -3,12 +3,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useRef, useEffect } from 'react';
|
|
4
4
|
import { Label } from '../../../../components/ui/label';
|
|
5
5
|
import { Textarea } from '../../../../components/ui/textarea';
|
|
6
|
-
import { PointsInput } from '../../../../components/ui/points-input';
|
|
7
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
|
|
8
6
|
import { useDebouncedCallback } from '../../../../shared/lib/hooks';
|
|
9
7
|
import { useReactHookForm } from '../../../../shared/lib/hooks/useReactHookForm';
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
8
|
+
import { FileText, Info, MessageSquare } from 'lucide-react';
|
|
9
|
+
import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, } from '../../_shared/components/compact';
|
|
12
10
|
import { readDisplayedContentFormSchema, } from '../../../../shared/lib/validation/read-displayed-content.validation';
|
|
13
11
|
export function ReadDisplayedContentCreator({ initialData, onSave, onChange, externalErrors, validationRef, }) {
|
|
14
12
|
const apiData = initialData;
|
|
@@ -115,11 +113,9 @@ export function ReadDisplayedContentCreator({ initialData, onSave, onChange, ext
|
|
|
115
113
|
...data,
|
|
116
114
|
});
|
|
117
115
|
});
|
|
118
|
-
return (_jsxs("div", { className: "space-y-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
'
|
|
123
|
-
'AI sẽ so sánh giọng đọc của học sinh với văn bản gốc (Phonetic Distance).',
|
|
124
|
-
] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white", children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsx("div", { className: "flex items-center justify-between", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gray-100", children: _jsx(FileText, { className: "h-4 w-4 text-gray-600" }) }), _jsxs("div", { children: [_jsx(CardTitle, { className: "text-base font-bold text-gray-900", children: "\u0110o\u1EA1n v\u0103n b\u1EA3n c\u1EA7n \u0111\u1ECDc" }), _jsx("p", { className: "text-xs text-gray-500", children: "T\u1EA1o c\u00E2u h\u1ECFi \u0111\u1ECDc \u0111o\u1EA1n v\u0103n" })] })] }) }) }), _jsxs(CardContent, { className: "space-y-3 px-4 pb-4", children: [_jsx("div", { className: "flex items-center gap-6", children: _jsx(PointsInput, { ...form.register('points', { valueAsNumber: true }), error: form.hasError('points') ? form.getFieldError('points') : undefined }) }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "referenceText", className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-4 w-4 text-indigo-500" }), _jsx("span", { className: "flex items-center gap-1", children: "\u0110o\u1EA1n v\u0103n b\u1EA3n" }), _jsx("span", { className: "text-sm font-normal text-red-500", children: "*" })] }), _jsx(Textarea, { id: "referenceText", ...form.register('referenceText'), placeholder: "VD: The fat cat sat on a red mat. It has a small bell on its neck. The bell rings when the cat jumps.", rows: 4, className: `min-h-[120px] border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 resize-none ${form.hasError('referenceText') ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), form.hasError('referenceText') && (_jsx("p", { className: "text-sm text-red-600", children: form.getFieldError('referenceText') })), _jsxs("div", { className: "mt-2 flex items-start gap-3 rounded-xl border border-blue-100 bg-gradient-to-r from-blue-50 to-indigo-50 p-4 text-sm text-blue-800", children: [_jsx(Info, { className: "mt-0.5 h-4 w-4 flex-shrink-0 text-indigo-500" }), _jsx("p", { children: "\u0110\u00E2y l\u00E0 \u0111o\u1EA1n v\u0103n s\u1EBD hi\u1EC3n th\u1ECB tr\u00EAn m\u00E0n h\u00ECnh cho h\u1ECDc sinh \u0111\u1ECDc to. AI s\u1EBD so s\u00E1nh ph\u00E1t \u00E2m c\u1EE7a h\u1ECDc sinh v\u1EDBi \u0111o\u1EA1n v\u0103n n\u00E0y." })] })] }), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100", children: _jsx("span", { className: "text-lg", children: "\u274C" }) }), _jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("ul", { className: "mt-1 list-inside list-disc text-red-700", children: externalErrors.map((error, index) => (_jsx("li", { children: error }, index))) })] })] }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white", children: [_jsx(CardHeader, { className: "px-4 py-3", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm", children: _jsx(BookOpen, { className: "h-3.5 w-3.5 text-white" }) }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-base font-semibold text-gray-800", children: "Gi\u1EA3i th\u00EDch" }), _jsx("span", { className: "inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2 py-0.5 text-xs font-medium text-amber-600", children: "Kh\u00F4ng b\u1EAFt bu\u1ED9c" })] }), _jsx("p", { className: "text-xs text-gray-500 mt-0.5", children: "Gi\u1EA3i th\u00EDch cho \u0111\u00E1p \u00E1n \u0111\u00FAng \u2014 hi\u1EC3n th\u1ECB sau khi h\u1ECDc sinh ho\u00E0n th\u00E0nh c\u00E2u h\u1ECFi" })] })] }) }), _jsx(CardContent, { className: "px-4 pb-4", children: _jsx(Textarea, { value: watchedExplanation || '', onChange: (e) => form.setValue('explanation', e.target.value, { shouldValidate: true }), placeholder: "VD: \u0110o\u1EA1n v\u0103n t\u1EADp trung v\u00E0o c\u00E1c \u00E2m ng\u1EAFn /\u00E6/, /e/, /\u026A/...", rows: 3, className: "min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none" }) })] })] }));
|
|
116
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(FileText, { className: "h-3.5 w-3.5" }), title: "\u0110o\u1EA1n v\u0103n b\u1EA3n c\u1EA7n \u0111\u1ECDc", points: {
|
|
117
|
+
value: watchedPoints,
|
|
118
|
+
onValueChange: (value) => form.setValue('points', value, { shouldValidate: true }),
|
|
119
|
+
error: form.hasError('points') ? form.getFieldError('points') : undefined,
|
|
120
|
+
} }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { htmlFor: "referenceText", className: "flex items-center gap-1.5 text-sm font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-3.5 w-3.5 text-indigo-500" }), _jsx("span", { children: "\u0110o\u1EA1n v\u0103n b\u1EA3n" }), _jsx("span", { className: "text-xs font-normal text-red-500", children: "*" })] }), _jsx(Textarea, { id: "referenceText", ...form.register('referenceText'), placeholder: "VD: The fat cat sat on a red mat. It has a small bell on its neck. The bell rings when the cat jumps.", rows: 4, className: `min-h-[120px] border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 resize-none ${form.hasError('referenceText') ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), form.hasError('referenceText') && (_jsx("p", { className: "text-sm text-red-600", children: form.getFieldError('referenceText') })), _jsxs("div", { className: "flex items-start gap-2 rounded-md border border-blue-100 bg-blue-50 p-2 text-xs text-blue-800", children: [_jsx(Info, { className: "mt-0.5 h-3.5 w-3.5 flex-shrink-0 text-indigo-500" }), _jsx("p", { children: "\u0110\u00E2y l\u00E0 \u0111o\u1EA1n v\u0103n s\u1EBD hi\u1EC3n th\u1ECB tr\u00EAn m\u00E0n h\u00ECnh cho h\u1ECDc sinh \u0111\u1ECDc to. AI s\u1EBD so s\u00E1nh ph\u00E1t \u00E2m c\u1EE7a h\u1ECDc sinh v\u1EDBi \u0111o\u1EA1n v\u0103n n\u00E0y." })] })] }), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-red-200 bg-red-50 p-2 text-xs text-red-700", children: [_jsx("span", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("span", { children: externalErrors.join(' · ') })] }))] }), _jsx(CompactExplanationToggle, { value: watchedExplanation || '', onValueChange: (value) => form.setValue('explanation', value, { shouldValidate: true }) })] }));
|
|
125
121
|
}
|