@tinyweb_dev/oe-exam-sdk 1.0.6 → 1.0.8
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/choose-the-correct-answer-group.type.d.ts +2 -2
- package/dist/components/questions/_shared/types/choose-the-correct-answer-group.type.js +5 -1
- 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.d.ts +7 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseAnswerGroupImageUpload.js +5 -2
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +9 -5
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupCreator.js +65 -62
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupItemEditor.d.ts +6 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupItemEditor.js +145 -0
- package/dist/components/questions/types/choose-the-correct-answer-group/map-choose-the-correct-answer-group-data.js +6 -15
- package/dist/components/questions/types/choose-the-correct-answer-group/transform.js +5 -4
- 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.d.ts +1 -1
- package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.js +79 -3
- package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.js +13 -11
- package/dist/components/questions/types/spontaneous-qa-group/index.d.ts +1 -0
- package/dist/components/questions/types/spontaneous-qa-group/index.js +1 -0
- package/dist/components/questions/types/spontaneous-qa-group/map-spontaneous-qa-group-data.d.ts +2 -0
- package/dist/components/questions/types/spontaneous-qa-group/map-spontaneous-qa-group-data.js +74 -0
- 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/questions/viewer/SpontaneousQaGroupViewer.js +4 -79
- 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 +4 -23
- 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/choose-the-correct-answer-group.d.ts +1 -1
- 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
|
@@ -10,7 +10,8 @@ import { Card, CardContent, CardHeader, CardTitle } from '../../../../components
|
|
|
10
10
|
import { Switch } from '../../../../components/ui/switch';
|
|
11
11
|
import { RichTextEditor } from '../../../../components/shared/RichTextEditor';
|
|
12
12
|
import { useDebouncedCallback } from '../../../../shared/lib/hooks';
|
|
13
|
-
import {
|
|
13
|
+
import { HelpCircle, Plus, Star, Trash2 } from 'lucide-react';
|
|
14
|
+
import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
|
|
14
15
|
import { FillInBlankGroupClient } from './FillInBlankGroupClient';
|
|
15
16
|
import { FillInBlankGroupMediaField } from './FillInBlankGroupMediaField';
|
|
16
17
|
import { createEmptyFillInBlankGroupItem } from './map-fill-in-blank-group-data';
|
|
@@ -168,23 +169,26 @@ export function FillInBlankGroupCreator({ initialData, onChange, externalErrors,
|
|
|
168
169
|
};
|
|
169
170
|
const currentPayload = buildPayload();
|
|
170
171
|
const displayErrors = externalErrors && externalErrors.length > 0 ? externalErrors : errors;
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
172
|
+
if (isClientMode) {
|
|
173
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(FillInBlankGroupClient, { questionData: currentPayload })] }));
|
|
174
|
+
}
|
|
175
|
+
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(HelpCircle, { className: "h-3.5 w-3.5" }), title: "\u0110i\u1EC1n ch\u1ED7 tr\u1ED1ng (Nh\u00F3m)", preview: { onClick: () => setIsClientMode(true) } }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsx(Label, { htmlFor: "instruction", className: "text-sm font-semibold text-gray-800", children: "H\u01B0\u1EDBng d\u1EABn b\u00E0i l\u00E0m" }), _jsx(Input, { id: "instruction", value: instruction, onChange: (e) => setInstruction(e.target.value), placeholder: FILL_IN_BLANK_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]" })] }), _jsxs("div", { className: "grid gap-2.5 md:grid-cols-2", children: [_jsx(FillInBlankGroupMediaField, { id: "fib-group-audio", label: "Audio chung (optional)", accept: "audio/*", value: audioUrl, onChange: setAudioUrl }), _jsx(FillInBlankGroupMediaField, { id: "fib-group-image", label: "\u1EA2nh chung (optional)", accept: "image/*", value: toFillInBlankImageUrls(imageUrl)[0] || '', onChange: setImageUrl })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: "show-hints", checked: showHints, onCheckedChange: setShowHints }), _jsx(Label, { htmlFor: "show-hints", children: "Hi\u1EC7n h\u1ED9p t\u1EEB g\u1EE3i \u00FD chung" })] }), showHints && (_jsx(Input, { value: hintsText, onChange: (e) => setHintsText(e.target.value), placeholder: "am / is / are", className: "h-10 border-gray-200 bg-white" }))] }), _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: ["Danh s\u00E1ch c\u00E2u (", 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: "flex items-center gap-2 text-sm font-medium text-gray-700", children: [_jsx("span", { className: "flex h-7 w-7 items-center justify-center rounded-full bg-blue-100 text-xs font-semibold text-blue-700", children: item.questionNumber || index + 1 }), "C\u00E2u ", index + 1] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { id: `example-${index}`, checked: item.isExample || false, onCheckedChange: (checked) => updateItem(index, { isExample: checked, points: checked ? 0 : 1 }) }), _jsxs(Label, { htmlFor: `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, {
|
|
176
|
+
points: parsePointsValue(event.target.value, true),
|
|
177
|
+
}) }) })), 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", { children: [_jsxs(Label, { htmlFor: `question-${index}`, children: ["Stem (d\u00F9ng ", '{0}', ", ", '{1}', "...)"] }), _jsx(Textarea, { id: `question-${index}`, value: item.question, onChange: (e) => updateItem(index, { question: e.target.value }), placeholder: "{0}", className: "mt-1 min-h-[56px] border-gray-200 bg-white" })] }), extractPlaceholderIndices(item.question).map((blankIndex) => (_jsxs("div", { className: "grid gap-2.5 md:grid-cols-2", children: [_jsxs("div", { children: [_jsxs(Label, { htmlFor: `answer-${index}-${blankIndex}`, children: ["\u0110\u00E1p \u00E1n blank ", '{' + blankIndex + '}', " (c\u00E1ch nhau b\u1EDFi /)"] }), _jsx(Input, { id: `answer-${index}-${blankIndex}`, value: joinSlashAnswers(item.answers[blankIndex]), onChange: (e) => {
|
|
178
|
+
const next = [...item.answers];
|
|
179
|
+
next[blankIndex] = parseSlashAnswers(e.target.value);
|
|
180
|
+
updateItem(index, { answers: next });
|
|
181
|
+
}, placeholder: "is / 's", className: "mt-1 h-10 border-gray-200 bg-white" })] }), _jsxs("div", { children: [_jsxs(Label, { htmlFor: `options-${index}-${blankIndex}`, children: ["Options blank ", '{' + blankIndex + '}', " (dropdown, c\u00E1ch nhau /)"] }), _jsx(Input, { id: `options-${index}-${blankIndex}`, value: joinSlashAnswers(item.wordBank?.[blankIndex]?.options), onChange: (e) => {
|
|
182
|
+
const requiredLength = blankSlotCount(item.question);
|
|
183
|
+
const next = [...(item.wordBank || [])];
|
|
184
|
+
while (next.length < requiredLength)
|
|
185
|
+
next.push(null);
|
|
186
|
+
const options = parseSlashAnswers(e.target.value);
|
|
187
|
+
next[blankIndex] = options.length >= 2 ? { options } : null;
|
|
188
|
+
updateItem(index, {
|
|
189
|
+
wordBank: next.some((entry) => entry && entry.options.length >= 2)
|
|
190
|
+
? next
|
|
191
|
+
: undefined,
|
|
192
|
+
});
|
|
193
|
+
}, placeholder: "can / can't", className: "mt-1 h-10 border-gray-200 bg-white" })] })] }, blankIndex))), _jsxs("div", { className: "grid gap-2.5 md:grid-cols-2", children: [_jsx(FillInBlankGroupMediaField, { id: `fib-item-image-${index}`, label: "\u1EA2nh item (optional)", accept: "image/*", value: Array.isArray(item.imageUrl) ? (item.imageUrl[0] || '') : (item.imageUrl || ''), onChange: (url) => updateItem(index, { imageUrl: url }) }), !audioUrl && (_jsx(FillInBlankGroupMediaField, { id: `fib-item-audio-${index}`, label: "Audio item (khi kh\u00F4ng c\u00F3 audio nh\u00F3m)", accept: "audio/*", value: item.audioUrl || '', onChange: (url) => updateItem(index, { audioUrl: url }) }))] })] }, index))), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })] })] }));
|
|
190
194
|
}
|
package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js
CHANGED
|
@@ -78,13 +78,13 @@ function FillMissingWordsInGridClientContent({ questionData, onSubmit, isReviewM
|
|
|
78
78
|
}, [missingPositions, userAnswers, correctAnswers, caseSensitive]);
|
|
79
79
|
const score = isReviewMode ? calculateScore() : null;
|
|
80
80
|
const scorePercentage = score ? Math.round((score.correct / score.total) * 100) : 0;
|
|
81
|
-
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-
|
|
81
|
+
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-3", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl bg-gradient-to-br from-teal-500 to-cyan-600 text-white shadow-sm", children: _jsx(Grid3X3, { className: "h-5 w-5" }) }), _jsxs("div", { children: [_jsx("h3", { className: "text-base font-semibold text-gray-900", children: "\u0110i\u1EC1n t\u1EEB c\u00F2n thi\u1EBFu v\u00E0o b\u1EA3ng" }), _jsx("p", { className: "text-xs text-gray-500", children: "\u0110i\u1EC1n c\u00E1c t\u1EEB c\u00F2n thi\u1EBFu v\u00E0o c\u00E1c \u00F4 \u0111\u01B0\u1EE3c \u0111\u00E1nh d\u1EA5u m\u00E0u xanh" })] })] }), isReviewMode && score && (_jsxs("div", { className: cn('flex items-center gap-3 rounded-xl px-4 py-2', scorePercentage >= 80 ? 'bg-green-50 border border-green-200' :
|
|
82
82
|
scorePercentage >= 50 ? 'bg-yellow-50 border border-yellow-200' :
|
|
83
83
|
'bg-red-50 border border-red-200'), children: [_jsx(Trophy, { className: cn('h-5 w-5', scorePercentage >= 80 ? 'text-green-500' :
|
|
84
84
|
scorePercentage >= 50 ? 'text-yellow-500' :
|
|
85
85
|
'text-red-500') }), _jsxs("div", { className: "text-right", children: [_jsxs("div", { className: cn('text-lg font-bold', scorePercentage >= 80 ? 'text-green-600' :
|
|
86
86
|
scorePercentage >= 50 ? 'text-yellow-600' :
|
|
87
|
-
'text-red-600'), children: [score.correct, "/", score.total] }), _jsxs("div", { className: "text-xs text-gray-500", children: [scorePercentage, "% ch\u00EDnh x\u00E1c"] })] })] }))] }), _jsxs("div", { className: "p-
|
|
87
|
+
'text-red-600'), children: [score.correct, "/", score.total] }), _jsxs("div", { className: "text-xs text-gray-500", children: [scorePercentage, "% ch\u00EDnh x\u00E1c"] })] })] }))] }), _jsxs("div", { className: "p-3 space-y-3", children: [_jsx("div", { className: "overflow-x-auto rounded-lg border border-gray-200 bg-gradient-to-br from-slate-50 to-gray-100 p-4", children: _jsxs("div", { className: "inline-block min-w-full", children: [_jsxs("div", { className: "grid gap-1.5 mb-2", style: { gridTemplateColumns: `48px repeat(${columns}, minmax(80px, 1fr))` }, children: [_jsx("div", { className: "h-8" }), Array.from({ length: columns }, (_, i) => (_jsx("div", { className: "flex h-8 items-center justify-center rounded-lg bg-gradient-to-br from-gray-100 to-gray-200 text-xs font-bold text-gray-600 shadow-sm", children: i + 1 }, `col-header-${i}`)))] }), _jsx("div", { className: "grid gap-1.5", children: Array.from({ length: rows }, (_, rowIndex) => (_jsxs("div", { className: "grid gap-1.5", style: { gridTemplateColumns: `48px repeat(${columns}, minmax(80px, 1fr))` }, children: [_jsx("div", { className: "flex h-14 items-center justify-center rounded-lg bg-gradient-to-br from-gray-100 to-gray-200 text-xs font-bold text-gray-600 shadow-sm", children: rowIndex + 1 }), Array.from({ length: columns }, (_, colIndex) => {
|
|
88
88
|
const isMissing = isMissingCell(rowIndex, colIndex);
|
|
89
89
|
const isDisabled = isDisabledCell(rowIndex, colIndex);
|
|
90
90
|
const cellValue = grid[rowIndex]?.[colIndex] || '';
|
package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridCreator.js
CHANGED
|
@@ -4,10 +4,8 @@ import { useState, useRef, useEffect, useCallback } 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 {
|
|
8
|
-
import {
|
|
9
|
-
import { PointsInput } from '../../../../components/ui/points-input';
|
|
10
|
-
import { Pencil, Eye, Trash2, Grid3X3, Lightbulb, CheckCircle2, BookOpen } from 'lucide-react';
|
|
7
|
+
import { Trash2, Grid3X3, CheckCircle2 } from 'lucide-react';
|
|
8
|
+
import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
|
|
11
9
|
import { toast } from 'sonner';
|
|
12
10
|
import { t } from '../../../../shared/lib/i18n';
|
|
13
11
|
import { FillMissingWordsInGridClient } from './FillMissingWordsInGridClient';
|
|
@@ -594,74 +592,79 @@ onChange, onUnsavedChangesChange, externalErrors = [], }) {
|
|
|
594
592
|
acc[`${item.row + 1}-${item.column + 1}`] = item.correctAnswer;
|
|
595
593
|
return acc;
|
|
596
594
|
}, {});
|
|
597
|
-
return (
|
|
595
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(FillMissingWordsInGridClient, { questionData: questionData, isReviewMode: true, userAnswer: correctAnswers, explanation: explanation })] }));
|
|
598
596
|
}
|
|
599
|
-
return (_jsxs("div", { className: "space-y-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
597
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Grid3X3, { className: "h-3.5 w-3.5" }), title: "\u0110i\u1EC1n t\u1EEB c\u00F2n thi\u1EBFu v\u00E0o grid", points: {
|
|
598
|
+
value: points,
|
|
599
|
+
onValueChange: setPoints,
|
|
600
|
+
}, preview: {
|
|
601
|
+
onClick: handlePreview,
|
|
602
|
+
} }), children: [_jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "flex items-center justify-between", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100", children: _jsx(Grid3X3, { className: "h-3.5 w-3.5 text-indigo-600" }) }), _jsx(Label, { className: "text-base font-semibold text-gray-800", children: "Grid - Nh\u1EADp t\u1EEB v\u00E0o c\u00E1c \u00F4" })] }) }), _jsx("div", { className: "overflow-x-auto rounded-xl border-2 border-indigo-200 bg-gradient-to-br from-indigo-50/50 to-blue-50/50 p-5 shadow-sm", children: _jsxs("div", { className: "inline-block min-w-full", children: [_jsxs("div", { className: "grid gap-1 mb-1", style: { gridTemplateColumns: `auto repeat(${columns}, minmax(0, 1fr))` }, children: [_jsx("div", { className: "h-8" }), Array.from({ length: columns }, (_, i) => (_jsx("div", { className: "flex h-8 items-center justify-center text-xs font-semibold text-gray-600", children: i + 1 }, `col-header-${i}`)))] }), _jsx("div", { className: "grid gap-1", children: grid.map((row, rowIndex) => (_jsxs("div", { className: "grid gap-1", style: { gridTemplateColumns: `auto repeat(${columns}, minmax(0, 1fr))` }, children: [_jsx("div", { className: "flex h-12 items-center justify-center text-xs font-semibold text-gray-600 border-2 border-transparent", children: rowIndex + 1 }), row.map((cell, colIndex) => {
|
|
603
|
+
const isMissing = missingLetters.some((item) => item.row === rowIndex && item.column === colIndex);
|
|
604
|
+
const isDisabled = disabledCells.includes(`${rowIndex + 1}-${colIndex + 1}`); // NEW
|
|
605
|
+
const isInvalid = shouldShowValidation() && isCellInvalid(rowIndex, colIndex);
|
|
606
|
+
return (_jsxs("div", { className: `relative flex h-12 items-center justify-center border-2 rounded transition-colors ${isDisabled
|
|
607
|
+
? 'border-red-400 bg-red-100 hover:bg-red-150 cursor-pointer' // NEW: Red styling for disabled
|
|
608
|
+
: isMissing
|
|
609
|
+
? 'border-blue-500 bg-blue-50 hover:bg-blue-100 cursor-pointer'
|
|
610
|
+
: isInvalid
|
|
611
|
+
? 'border-red-500 bg-red-50 hover:bg-red-100'
|
|
612
|
+
: cell
|
|
613
|
+
? 'border-gray-400 bg-white hover:bg-gray-50'
|
|
614
|
+
: 'border-gray-300 bg-gray-100 hover:bg-gray-200'}`, onClick: (e) => {
|
|
615
|
+
// Triple-click to toggle disabled
|
|
616
|
+
if (e.detail === 3) {
|
|
617
|
+
e.preventDefault();
|
|
618
|
+
e.stopPropagation();
|
|
619
|
+
toggleDisabledCell(rowIndex, colIndex);
|
|
620
|
+
}
|
|
621
|
+
}, onDoubleClick: (e) => {
|
|
622
|
+
e.preventDefault();
|
|
623
|
+
e.stopPropagation();
|
|
624
|
+
if (!isDisabled) {
|
|
625
|
+
toggleMissingLetter(rowIndex, colIndex);
|
|
626
|
+
}
|
|
627
|
+
}, children: [_jsx(Input, { type: "text", maxLength: 10, value: cell || '', onChange: (e) => {
|
|
628
|
+
if (!isMissing && !isDisabled) { // NEW: Also check disabled
|
|
629
|
+
// Trim whitespace and limit length
|
|
630
|
+
const value = e.target.value.trim().slice(0, 10);
|
|
631
|
+
updateGridCell(rowIndex, colIndex, value);
|
|
617
632
|
}
|
|
618
633
|
}, onDoubleClick: (e) => {
|
|
619
634
|
e.preventDefault();
|
|
620
635
|
e.stopPropagation();
|
|
621
|
-
if (!isDisabled) {
|
|
636
|
+
if (!isDisabled) { // NEW: Only toggle missing if not disabled
|
|
622
637
|
toggleMissingLetter(rowIndex, colIndex);
|
|
623
638
|
}
|
|
624
|
-
},
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
setValidationErrors((prev) => ({
|
|
654
|
-
...prev,
|
|
655
|
-
[letter.id]: 'Đáp án không được vượt quá 100 ký tự',
|
|
656
|
-
}));
|
|
657
|
-
}
|
|
658
|
-
}, placeholder: "Nh\u1EADp \u0111\u00E1p \u00E1n \u0111\u00FAng...", className: `mt-1.5 border-gray-200 bg-white transition-all focus:border-blue-300 focus:ring-blue-200 ${validationErrors[letter.id]
|
|
659
|
-
? 'border-red-400 focus:border-red-500 focus:ring-red-200'
|
|
660
|
-
: ''}` }), validationErrors[letter.id] && (_jsx("p", { className: "mt-1.5 text-sm text-red-600", children: validationErrors[letter.id] }))] })] }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeMissingLetter(letter.id), className: "h-8 w-8 text-red-400 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-4 w-4" }) })] }) }, letter.id));
|
|
661
|
-
}) })] }))] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-lg shadow-amber-100/50", children: [_jsx("div", { className: "h-1 bg-gradient-to-r from-amber-400 to-orange-400" }), _jsx(CardHeader, { className: "pb-4", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-lg bg-gradient-to-br from-amber-500 to-orange-500 shadow-sm", children: _jsx(BookOpen, { className: "h-4 w-4 text-white" }) }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(CardTitle, { className: "text-lg font-semibold text-gray-800", children: "Gi\u1EA3i th\u00EDch" }), _jsx("span", { className: "inline-flex items-center rounded-full border border-amber-200 bg-amber-50 px-2.5 py-0.5 text-xs font-medium text-amber-600", children: "Kh\u00F4ng b\u1EAFt bu\u1ED9c" })] }), _jsx("p", { className: "text-sm text-gray-500 mt-0.5", children: "Gi\u1EA3i th\u00EDch cho \u0111\u00E1p \u00E1n \u0111\u00FAng \u2014 hi\u1EC3n th\u1ECB sau khi h\u1ECDc sinh ho\u00E0n th\u00E0nh c\u00E2u h\u1ECFi" })] })] }) }), _jsx(CardContent, { children: _jsx(Textarea, { id: "explanation", value: explanation, onChange: (e) => {
|
|
662
|
-
setExplanation(e.target.value);
|
|
663
|
-
onUnsavedChangesChange?.(true);
|
|
664
|
-
}, placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng l\u00E0... v\u00EC...", rows: 3, className: "min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none" }) })] })] }));
|
|
639
|
+
}, disabled: isMissing || isDisabled, readOnly: isMissing || isDisabled, className: `h-full border-0 bg-transparent text-center text-lg font-semibold focus:ring-2 focus:ring-blue-500 ${isMissing || isDisabled ? 'cursor-not-allowed pointer-events-none' : ''} ${isDisabled ? 'text-gray-500' : isMissing ? 'text-blue-600' : ''}`, placeholder: "" }), isMissing && (_jsx("div", { className: "absolute -top-1 -right-1 flex h-5 w-5 items-center justify-center rounded-full bg-blue-500 text-xs font-bold text-white", children: "?" })), isDisabled && (_jsx("div", { className: "absolute -top-1 -right-1 flex h-5 w-5 items-center justify-center rounded-full bg-red-500 text-xs font-bold text-white", children: "\uD83D\uDD12" }))] }, `${rowIndex}-${colIndex}`));
|
|
640
|
+
})] }, `row-${rowIndex}`))) })] }) })] }), missingLetters.length > 0 && (_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-blue-100", children: _jsx(CheckCircle2, { className: "h-3.5 w-3.5 text-blue-600" }) }), _jsxs(Label, { className: "text-base font-semibold text-gray-800", children: ["Danh s\u00E1ch \u00F4 c\u00F2n thi\u1EBFu", _jsx("span", { className: "ml-2 rounded-full bg-blue-100 px-2 py-0.5 text-xs font-medium text-blue-700", children: missingLetters.length })] })] }), _jsx("div", { className: "space-y-3", children: [...missingLetters]
|
|
641
|
+
.sort((a, b) => {
|
|
642
|
+
if (a.row !== b.row)
|
|
643
|
+
return a.row - b.row;
|
|
644
|
+
return a.column - b.column;
|
|
645
|
+
})
|
|
646
|
+
.map((letter, index) => {
|
|
647
|
+
const cellValue = grid[letter.row]?.[letter.column];
|
|
648
|
+
return (_jsx("div", { className: "rounded-xl border-2 border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 p-4 shadow-sm transition-all hover:border-blue-300 hover:shadow-md", children: _jsxs("div", { className: "flex items-start gap-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-blue-500 text-sm font-bold text-white shadow-sm", children: index + 1 }), _jsxs("div", { className: "flex-1 space-y-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("span", { className: "text-sm font-semibold text-gray-700", children: ["V\u1ECB tr\u00ED (", letter.row + 1, ", ", letter.column + 1, ")"] }), cellValue && (_jsxs("span", { className: "rounded-full bg-gray-100 px-2 py-0.5 text-xs text-gray-500", children: ["Hi\u1EC7n t\u1EA1i: \"", cellValue, "\""] }))] }), _jsxs("div", { children: [_jsxs(Label, { htmlFor: `answer-${letter.id}`, className: "text-sm font-medium text-gray-700", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: `answer-${letter.id}`, type: "text", maxLength: 100, value: letter.correctAnswer, onChange: (e) => updateMissingLetter(letter.id, 'correctAnswer', e.target.value.trim()), onBlur: () => {
|
|
649
|
+
if (!letter.correctAnswer || letter.correctAnswer.trim() === '') {
|
|
650
|
+
setValidationErrors((prev) => ({
|
|
651
|
+
...prev,
|
|
652
|
+
[letter.id]: 'Vui lòng nhập đáp án đúng',
|
|
653
|
+
}));
|
|
654
|
+
}
|
|
655
|
+
else if (letter.correctAnswer.length > 100) {
|
|
656
|
+
setValidationErrors((prev) => ({
|
|
657
|
+
...prev,
|
|
658
|
+
[letter.id]: 'Đáp án không được vượt quá 100 ký tự',
|
|
659
|
+
}));
|
|
660
|
+
}
|
|
661
|
+
}, placeholder: "Nh\u1EADp \u0111\u00E1p \u00E1n \u0111\u00FAng...", className: `mt-1.5 border-gray-200 bg-white transition-all focus:border-blue-300 focus:ring-blue-200 ${validationErrors[letter.id]
|
|
662
|
+
? 'border-red-400 focus:border-red-500 focus:ring-red-200'
|
|
663
|
+
: ''}` }), validationErrors[letter.id] && (_jsx("p", { className: "mt-1.5 text-sm text-red-600", children: validationErrors[letter.id] }))] })] }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: () => removeMissingLetter(letter.id), className: "h-8 w-8 text-red-400 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-4 w-4" }) })] }) }, letter.id));
|
|
664
|
+
}) })] }))] }), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: (value) => {
|
|
665
|
+
setExplanation(value);
|
|
666
|
+
onUnsavedChangesChange?.(true);
|
|
667
|
+
} })] }));
|
|
665
668
|
}
|
|
666
669
|
// Export with both new and legacy names for backward compatibility
|
|
667
670
|
export function FillMissingWordsInGridCreator(props) {
|
|
@@ -39,7 +39,7 @@ export function FindWordsInMatrixClient({ content, correctAnswer, showSolution =
|
|
|
39
39
|
});
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
|
-
return (_jsxs("div", { className: "space-y-
|
|
42
|
+
return (_jsxs("div", { className: "space-y-3", children: [content.title && (_jsx("h3", { className: "text-lg font-bold text-gray-800 dark:text-gray-100", children: content.title })), isCategorize ? (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4", children: categories.map((cat) => {
|
|
43
43
|
const wordsInCat = wordBank.filter((w) => w.categoryId === cat.id);
|
|
44
44
|
return (_jsxs(Card, { className: "p-3.5 bg-gray-50/70 dark:bg-gray-800/40 border", children: [_jsxs("div", { className: "font-bold text-sm text-gray-800 dark:text-gray-200 border-b pb-1.5 mb-2.5 flex items-center justify-between", children: [_jsx("span", { children: cat.name }), _jsx(Badge, { variant: "outline", className: "text-xs", children: wordsInCat.length })] }), _jsx("div", { className: "flex flex-wrap gap-1.5", children: wordsInCat.map((w, idx) => {
|
|
45
45
|
const isExample = Boolean(w.isExample);
|
|
@@ -9,7 +9,9 @@ import { Badge } from '../../../../components/ui/badge';
|
|
|
9
9
|
import { Checkbox } from '../../../../components/ui/checkbox';
|
|
10
10
|
import { Textarea } from '../../../../components/ui/textarea';
|
|
11
11
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../../../components/ui/select';
|
|
12
|
-
import { Plus, Trash2, Sparkles, Clipboard, MousePointer, RotateCcw, Layers, ListFilter, FolderPlus, } from 'lucide-react';
|
|
12
|
+
import { Plus, Trash2, Sparkles, Clipboard, MousePointer, RotateCcw, Layers, ListFilter, FolderPlus, Search, } from 'lucide-react';
|
|
13
|
+
import { CompactCreatorHeader, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
|
|
14
|
+
import { FindWordsInMatrixClient } from './FindWordsInMatrixClient';
|
|
13
15
|
const PALETTE = [
|
|
14
16
|
'#f59e0b', '#10b981', '#0ea5e9', '#8b5cf6', '#f43f5e',
|
|
15
17
|
'#6366f1', '#14b8a6', '#eab308', '#ec4899', '#06b6d4',
|
|
@@ -83,6 +85,7 @@ export function FindWordsInMatrixCreator({ initialData, onChange, onUnsavedChang
|
|
|
83
85
|
const [isPickingPath, setIsPickingPath] = useState(false);
|
|
84
86
|
const [pasteModalOpen, setPasteModalOpen] = useState(false);
|
|
85
87
|
const [pasteText, setPasteText] = useState('');
|
|
88
|
+
const [isClientMode, setIsClientMode] = useState(false);
|
|
86
89
|
const updateState = useCallback((updater) => {
|
|
87
90
|
setState((prev) => {
|
|
88
91
|
const next = updater(prev);
|
|
@@ -231,17 +234,54 @@ export function FindWordsInMatrixCreator({ initialData, onChange, onUnsavedChang
|
|
|
231
234
|
cellColorMap[`${coord.row}-${coord.col}`] = { color, word: w.word };
|
|
232
235
|
});
|
|
233
236
|
});
|
|
234
|
-
|
|
237
|
+
if (isClientMode) {
|
|
238
|
+
const exampleWord = state.words.find((w) => w.isExample && w.path && w.path.length > 0);
|
|
239
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(FindWordsInMatrixClient, { content: {
|
|
240
|
+
type: state.type,
|
|
241
|
+
title: state.title,
|
|
242
|
+
gridSize: { rows: state.rows, cols: state.cols },
|
|
243
|
+
grid: state.grid,
|
|
244
|
+
...(state.type === 'CATEGORIZE' ? { categories: state.categories } : {}),
|
|
245
|
+
wordBank: state.words.map((w) => ({
|
|
246
|
+
id: w.id,
|
|
247
|
+
word: w.word,
|
|
248
|
+
isExample: w.isExample,
|
|
249
|
+
...(w.categoryId ? { categoryId: w.categoryId } : {}),
|
|
250
|
+
})),
|
|
251
|
+
...(exampleWord
|
|
252
|
+
? {
|
|
253
|
+
exampleSelection: {
|
|
254
|
+
wordId: exampleWord.id,
|
|
255
|
+
...(exampleWord.categoryId ? { categoryId: exampleWord.categoryId } : {}),
|
|
256
|
+
path: exampleWord.path || [],
|
|
257
|
+
},
|
|
258
|
+
}
|
|
259
|
+
: {}),
|
|
260
|
+
}, correctAnswer: {
|
|
261
|
+
words: state.words
|
|
262
|
+
.filter((w) => !w.isExample && w.path && w.path.length > 0)
|
|
263
|
+
.map((w) => ({
|
|
264
|
+
wordId: w.id,
|
|
265
|
+
word: w.word,
|
|
266
|
+
...(w.categoryId ? { categoryId: w.categoryId } : {}),
|
|
267
|
+
path: w.path || [],
|
|
268
|
+
})),
|
|
269
|
+
}, showSolution: true })] }));
|
|
270
|
+
}
|
|
271
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Search, { className: "h-3.5 w-3.5" }), title: "T\u00ECm t\u1EEB trong ma tr\u1EADn", points: {
|
|
272
|
+
value: state.points,
|
|
273
|
+
onValueChange: (value) => updateState((p) => ({ ...p, points: value })),
|
|
274
|
+
}, preview: { onClick: () => setIsClientMode(true) } }), children: [_jsxs("div", { children: [_jsx(Label, { className: "font-semibold text-sm mb-2 block", children: "D\u1EA1ng b\u00E0i t\u1EADp (Type / Mode)" }), _jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-3", children: [_jsxs("button", { type: "button", onClick: () => updateState((p) => ({ ...p, type: 'WORD_BANK' })), className: `flex items-center gap-3 p-3 rounded-xl border text-left transition-all ${state.type === 'WORD_BANK'
|
|
235
275
|
? 'border-blue-500 bg-blue-50/60 dark:bg-blue-950/30 text-blue-900 dark:text-blue-200 ring-2 ring-blue-400'
|
|
236
276
|
: 'border-gray-200 hover:bg-gray-50 dark:border-gray-700 text-gray-700 dark:text-gray-300'}`, children: [_jsx("div", { className: "p-2 rounded-lg bg-blue-100 dark:bg-blue-900 text-blue-600", children: _jsx(ListFilter, { className: "h-5 w-5" }) }), _jsxs("div", { children: [_jsx("div", { className: "font-bold text-sm", children: "1. Danh s\u00E1ch t\u1EEB (Word Bank)" }), _jsx("div", { className: "text-xs text-gray-500", children: "T\u00ECm c\u00E1c t\u1EEB c\u00F3 s\u1EB5n trong b\u1EA3ng ch\u1EEF c\u00E1i (VD: Gadgets)." })] })] }), _jsxs("button", { type: "button", onClick: () => updateState((p) => ({ ...p, type: 'CATEGORIZE' })), className: `flex items-center gap-3 p-3 rounded-xl border text-left transition-all ${state.type === 'CATEGORIZE'
|
|
237
277
|
? 'border-emerald-500 bg-emerald-50/60 dark:bg-emerald-950/30 text-emerald-900 dark:text-emerald-200 ring-2 ring-emerald-400'
|
|
238
|
-
: 'border-gray-200 hover:bg-gray-50 dark:border-gray-700 text-gray-700 dark:text-gray-300'}`, children: [_jsx("div", { className: "p-2 rounded-lg bg-emerald-100 dark:bg-emerald-900 text-emerald-600", children: _jsx(Layers, { className: "h-5 w-5" }) }), _jsxs("div", { children: [_jsx("div", { className: "font-bold text-sm", children: "2. T\u00ECm t\u1EEB & Ph\u00E2n nh\u00F3m (Categorize)" }), _jsx("div", { className: "text-xs text-gray-500", children: "T\u00ECm t\u1EEB \u1EA9n v\u00E0 x\u1EBFp v\u00E0o c\u00E1c nh\u00F3m ti\u00EAu \u0111\u1EC1 (VD: Plants / Animals)." })] })] })] })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4 pt-2 border-t", children: [_jsxs("div", { className: "md:col-span-2", children: [_jsx(Label, { className: "font-semibold", children: "Ti\u00EAu \u0111\u1EC1 b\u00E0i t\u1EADp" }), _jsx(Input, { value: state.title || '', onChange: (e) => updateState((p) => ({ ...p, title: e.target.value })), placeholder: state.type === 'CATEGORIZE' ? 'VD: Plants and Animals Word Search' : 'VD: Technology Gadgets Word Search', className: "mt-1" })] }), _jsxs("div", { children: [_jsx(Label, { className: "font-semibold", children: "K\u00EDch th\u01B0\u1EDBc ma tr\u1EADn (H\u00E0ng x C\u1ED9t)" }), _jsxs("div", { className: "flex items-center gap-2 mt-1", children: [_jsx(Input, { type: "number", min: 4, max: 25, value: state.rows, onChange: (e) => handleResize(Number(e.target.value), state.cols), className: "w-20 text-center font-bold" }), _jsx("span", { className: "font-bold text-gray-400", children: "\u00D7" }), _jsx(Input, { type: "number", min: 4, max: 25, value: state.cols, onChange: (e) => handleResize(state.rows, Number(e.target.value)), className: "w-20 text-center font-bold" })] })] })] })] }), state.type === 'CATEGORIZE' && (_jsxs(Card, { className: "
|
|
278
|
+
: 'border-gray-200 hover:bg-gray-50 dark:border-gray-700 text-gray-700 dark:text-gray-300'}`, children: [_jsx("div", { className: "p-2 rounded-lg bg-emerald-100 dark:bg-emerald-900 text-emerald-600", children: _jsx(Layers, { className: "h-5 w-5" }) }), _jsxs("div", { children: [_jsx("div", { className: "font-bold text-sm", children: "2. T\u00ECm t\u1EEB & Ph\u00E2n nh\u00F3m (Categorize)" }), _jsx("div", { className: "text-xs text-gray-500", children: "T\u00ECm t\u1EEB \u1EA9n v\u00E0 x\u1EBFp v\u00E0o c\u00E1c nh\u00F3m ti\u00EAu \u0111\u1EC1 (VD: Plants / Animals)." })] })] })] })] }), _jsxs("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4 pt-2 border-t", children: [_jsxs("div", { className: "md:col-span-2", children: [_jsx(Label, { className: "font-semibold", children: "Ti\u00EAu \u0111\u1EC1 b\u00E0i t\u1EADp" }), _jsx(Input, { value: state.title || '', onChange: (e) => updateState((p) => ({ ...p, title: e.target.value })), placeholder: state.type === 'CATEGORIZE' ? 'VD: Plants and Animals Word Search' : 'VD: Technology Gadgets Word Search', className: "mt-1" })] }), _jsxs("div", { children: [_jsx(Label, { className: "font-semibold", children: "K\u00EDch th\u01B0\u1EDBc ma tr\u1EADn (H\u00E0ng x C\u1ED9t)" }), _jsxs("div", { className: "flex items-center gap-2 mt-1", children: [_jsx(Input, { type: "number", min: 4, max: 25, value: state.rows, onChange: (e) => handleResize(Number(e.target.value), state.cols), className: "w-20 text-center font-bold" }), _jsx("span", { className: "font-bold text-gray-400", children: "\u00D7" }), _jsx(Input, { type: "number", min: 4, max: 25, value: state.cols, onChange: (e) => handleResize(state.rows, Number(e.target.value)), className: "w-20 text-center font-bold" })] })] })] })] }), state.type === 'CATEGORIZE' && (_jsxs(Card, { className: "overflow-hidden border-0 bg-white p-4 space-y-3 shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsxs("h4", { className: "font-bold text-sm text-emerald-900 dark:text-emerald-300 flex items-center gap-1.5", children: [_jsx(Layers, { className: "h-4 w-4 text-emerald-600" }), "C\u00E1c nh\u00F3m danh m\u1EE5c (Categories / Headings)"] }), _jsx("p", { className: "text-xs text-gray-500", children: "T\u1EA1o c\u00E1c c\u1ED9t ti\u00EAu \u0111\u1EC1 \u0111\u1EC3 h\u1ECDc sinh ph\u00E2n lo\u1EA1i t\u1EEB v\u00E0o (t\u1ED1i thi\u1EC3u 2 nh\u00F3m)." })] }), _jsxs(Button, { type: "button", size: "sm", variant: "outline", onClick: handleAddCategory, className: "gap-1 text-xs", children: [_jsx(FolderPlus, { className: "h-3.5 w-3.5" }), " Th\u00EAm nh\u00F3m"] })] }), _jsx("div", { className: "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-3", children: state.categories.map((cat, idx) => (_jsxs("div", { className: "flex items-center gap-2 p-2.5 bg-white dark:bg-gray-900 rounded-lg border shadow-xs", children: [_jsx(Badge, { variant: "secondary", className: "font-mono text-xs", children: idx + 1 }), _jsx(Input, { value: cat.name, onChange: (e) => {
|
|
239
279
|
const val = e.target.value;
|
|
240
280
|
updateState((p) => ({
|
|
241
281
|
...p,
|
|
242
282
|
categories: p.categories.map((c, i) => (i === idx ? { ...c, name: val } : c)),
|
|
243
283
|
}));
|
|
244
|
-
}, placeholder: "T\u00EAn nh\u00F3m (VD: Plants)", className: "h-8 text-sm font-semibold" }), state.categories.length > 2 && (_jsx(Button, { type: "button", size: "sm", variant: "ghost", onClick: () => handleRemoveCategory(cat.id), className: "h-8 w-8 p-0 text-red-500 hover:text-red-700", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }, cat.id || idx))) })] })), _jsxs(Card, { className: "p-4 space-y-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 border-b pb-3", children: [_jsxs("div", { children: [_jsxs("h4", { className: "font-bold text-base text-gray-800 dark:text-gray-200", children: ["Ma tr\u1EADn ch\u1EEF c\u00E1i (", state.rows, " h\u00E0ng \u00D7 ", state.cols, " c\u1ED9t)"] }), _jsx("p", { className: "text-xs text-gray-500", children: isPickingPath
|
|
284
|
+
}, placeholder: "T\u00EAn nh\u00F3m (VD: Plants)", className: "h-8 text-sm font-semibold" }), state.categories.length > 2 && (_jsx(Button, { type: "button", size: "sm", variant: "ghost", onClick: () => handleRemoveCategory(cat.id), className: "h-8 w-8 p-0 text-red-500 hover:text-red-700", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) }))] }, cat.id || idx))) })] })), _jsxs(Card, { className: "overflow-hidden border-0 bg-white p-4 space-y-4 shadow-sm", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2 border-b pb-3", children: [_jsxs("div", { children: [_jsxs("h4", { className: "font-bold text-base text-gray-800 dark:text-gray-200", children: ["Ma tr\u1EADn ch\u1EEF c\u00E1i (", state.rows, " h\u00E0ng \u00D7 ", state.cols, " c\u1ED9t)"] }), _jsx("p", { className: "text-xs text-gray-500", children: isPickingPath
|
|
245
285
|
? `👉 Đang gán vị trí cho từ: "${state.words[selectedWordIdx]?.word}". Hãy click các ô trên lưới theo thứ tự.`
|
|
246
286
|
: 'Nhập trực tiếp ký tự vào ô, hoặc dùng các nút công cụ bên phải.' })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setPasteModalOpen(!pasteModalOpen), className: "gap-1 text-xs", children: [_jsx(Clipboard, { className: "h-3.5 w-3.5" }), "Paste ma tr\u1EADn"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: handleRandomFill, className: "gap-1 text-xs text-emerald-600 hover:text-emerald-700", children: [_jsx(Sparkles, { className: "h-3.5 w-3.5" }), "L\u1EA5p ng\u1EABu nhi\u00EAn (A-Z)"] })] })] }), pasteModalOpen && (_jsxs("div", { className: "p-3 bg-blue-50 dark:bg-blue-950/30 rounded-lg border border-blue-200 space-y-2", children: [_jsx(Label, { className: "text-xs font-bold text-blue-900 dark:text-blue-200", children: "D\u00E1n v\u0103n b\u1EA3n ma tr\u1EADn (m\u1ED7i d\u00F2ng 1 h\u00E0ng):" }), _jsx(Textarea, { value: pasteText, onChange: (e) => setPasteText(e.target.value), placeholder: `SEMONOFFP\nCROCODILE\n...`, rows: 4, className: "font-mono text-xs" }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { size: "sm", variant: "ghost", onClick: () => setPasteModalOpen(false), children: "H\u1EE7y" }), _jsx(Button, { size: "sm", onClick: handleApplyPaste, children: "\u00C1p d\u1EE5ng v\u00E0o ma tr\u1EADn" })] })] })), _jsx("div", { className: "overflow-x-auto pb-2 flex justify-center", children: _jsx("div", { className: "inline-grid gap-1 p-2 bg-gray-100 dark:bg-gray-900 rounded-lg border border-gray-300", style: {
|
|
247
287
|
gridTemplateColumns: `repeat(${state.cols}, minmax(0, 1fr))`,
|
|
@@ -255,7 +295,7 @@ export function FindWordsInMatrixCreator({ initialData, onChange, onUnsavedChang
|
|
|
255
295
|
? 'border-2 text-white font-extrabold shadow-sm'
|
|
256
296
|
: 'bg-white dark:bg-gray-800 text-gray-800 dark:text-gray-100 border-gray-300'}`, style: highlight ? { backgroundColor: highlight.color, borderColor: highlight.color } : {}, title: highlight ? `${highlight.word} (${r},${c})` : `(${r},${c})` }) }, `${r}-${c}`));
|
|
257
297
|
});
|
|
258
|
-
}) }) })] }), _jsxs(Card, { className: "p-4 space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between border-b pb-2", children: [_jsxs("div", { children: [_jsx("h4", { className: "font-bold text-base", children: state.type === 'CATEGORIZE' ? 'Danh sách từ & Nhóm phân loại' : 'Danh sách từ cần tìm (Word Bank)' }), _jsxs("p", { className: "text-xs text-gray-500", children: ["Nh\u1EADp t\u1EEB v\u1EF1ng, ", state.type === 'CATEGORIZE' ? 'chọn nhóm thuộc tính, ' : '', "nh\u1EA5n \"G\u00E1n v\u1ECB tr\u00ED\" v\u00E0 click c\u00E1c \u00F4 t\u01B0\u01A1ng \u1EE9ng tr\u00EAn ma tr\u1EADn."] })] }), _jsxs(Button, { type: "button", size: "sm", onClick: handleAddWord, className: "gap-1", children: [_jsx(Plus, { className: "h-4 w-4" }), " Th\u00EAm t\u1EEB"] })] }), _jsx("div", { className: "space-y-3", children: state.words.map((w, idx) => {
|
|
298
|
+
}) }) })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white p-4 space-y-4 shadow-sm", children: [_jsxs("div", { className: "flex items-center justify-between border-b pb-2", children: [_jsxs("div", { children: [_jsx("h4", { className: "font-bold text-base", children: state.type === 'CATEGORIZE' ? 'Danh sách từ & Nhóm phân loại' : 'Danh sách từ cần tìm (Word Bank)' }), _jsxs("p", { className: "text-xs text-gray-500", children: ["Nh\u1EADp t\u1EEB v\u1EF1ng, ", state.type === 'CATEGORIZE' ? 'chọn nhóm thuộc tính, ' : '', "nh\u1EA5n \"G\u00E1n v\u1ECB tr\u00ED\" v\u00E0 click c\u00E1c \u00F4 t\u01B0\u01A1ng \u1EE9ng tr\u00EAn ma tr\u1EADn."] })] }), _jsxs(Button, { type: "button", size: "sm", onClick: handleAddWord, className: "gap-1", children: [_jsx(Plus, { className: "h-4 w-4" }), " Th\u00EAm t\u1EEB"] })] }), _jsx("div", { className: "space-y-3", children: state.words.map((w, idx) => {
|
|
259
299
|
const isSelected = selectedWordIdx === idx;
|
|
260
300
|
const color = PALETTE[idx % PALETTE.length];
|
|
261
301
|
const pathLength = w.path?.length || 0;
|
|
@@ -68,7 +68,7 @@ export function LabelThePictureClient({ questionData, userAnswer = {}, isReviewM
|
|
|
68
68
|
});
|
|
69
69
|
// Sort labels by number
|
|
70
70
|
const sortedLabels = [...labels].sort((a, b) => a.number - b.number);
|
|
71
|
-
return (_jsxs("div", { className: "space-y-
|
|
71
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "flex items-center justify-between", children: _jsxs("div", { children: [_jsx("h2", { className: "text-lg font-semibold text-gray-900", children: "Label the Picture" }), _jsx("p", { className: "text-sm text-gray-500", children: "Nh\u00ECn tranh v\u00E0 \u0111i\u1EC1n t\u1EEB v\u00E0o c\u00E1c v\u1ECB tr\u00ED t\u01B0\u01A1ng \u1EE9ng" })] }) }), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsx("div", { className: "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: "h-2.5 w-2.5 rounded-full bg-red-400" }), _jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-yellow-400" }), _jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-green-400" }), _jsx("span", { className: "ml-3 text-sm font-medium text-gray-500", children: "H\u00ECnh \u1EA3nh" })] }) }), _jsx("div", { className: "flex justify-center bg-gradient-to-br from-slate-50 to-gray-100 p-4", children: _jsxs("div", { className: "relative max-h-[400px] w-full max-w-2xl overflow-hidden rounded-lg", children: [isLoadingUrl && (_jsx("div", { className: "flex h-64 items-center justify-center", children: _jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-blue-500" }) })), !isLoadingUrl && (previewUrl || imageUrl) ? (
|
|
72
72
|
/* eslint-disable-next-line @next/next/no-img-element */
|
|
73
73
|
_jsx("img", { src: previewUrl || imageUrl, alt: "Label the picture", className: "h-full w-full object-contain", onError: (e) => {
|
|
74
74
|
console.error('Failed to load image:', imageUrl, 'previewUrl:', previewUrl);
|
|
@@ -76,7 +76,7 @@ export function LabelThePictureClient({ questionData, userAnswer = {}, isReviewM
|
|
|
76
76
|
const target = e.target;
|
|
77
77
|
target.onerror = null; // Prevent infinite loop
|
|
78
78
|
target.src = '/images/placeholder-image.svg';
|
|
79
|
-
} })) : (_jsxs("div", { className: "flex h-64 flex-col items-center justify-center gap-3 bg-gray-50 rounded-lg border-2 border-dashed border-gray-200", children: [_jsx("svg", { className: "h-16 w-16 text-gray-300", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" }) }), _jsx("p", { className: "text-gray-400 font-medium", children: "Ch\u01B0a c\u00F3 h\u00ECnh \u1EA3nh" })] }))] }) })] }), wordBank.length > 0 && (_jsxs("div", { className: "overflow-hidden rounded-lg border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-gray-100 bg-gradient-to-r from-amber-50 to-orange-50 px-
|
|
79
|
+
} })) : (_jsxs("div", { className: "flex h-64 flex-col items-center justify-center gap-3 bg-gray-50 rounded-lg border-2 border-dashed border-gray-200", children: [_jsx("svg", { className: "h-16 w-16 text-gray-300", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 1.5, d: "M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" }) }), _jsx("p", { className: "text-gray-400 font-medium", children: "Ch\u01B0a c\u00F3 h\u00ECnh \u1EA3nh" })] }))] }) })] }), wordBank.length > 0 && (_jsxs("div", { className: "overflow-hidden rounded-lg border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-2 border-b border-gray-100 bg-gradient-to-r from-amber-50 to-orange-50 px-3 py-2", children: [_jsx("svg", { className: "h-5 w-5 text-amber-600", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: _jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M7 7h.01M7 3h5c.512 0 1.024.195 1.414.586l7 7a2 2 0 010 2.828l-7 7a2 2 0 01-2.828 0l-7-7A1.994 1.994 0 013 12V7a4 4 0 014-4z" }) }), _jsx("h3", { className: "text-sm font-semibold text-gray-800", children: "Word Bank" })] }), _jsx("div", { className: "p-3 space-y-3", children: _jsx("div", { className: "flex flex-wrap gap-2", children: wordBank.map((word, index) => {
|
|
80
80
|
const isUsedAsPreFilled = labels.some((label) => label.isPreFilled && label.correctAnswer === word);
|
|
81
81
|
const isUsedByUser = Object.values(answers).includes(word);
|
|
82
82
|
return (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-lg border-2 px-3 py-1.5 text-sm font-semibold transition-all', isUsedAsPreFilled
|