@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/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js
CHANGED
|
@@ -122,13 +122,13 @@ export function ArrangeLettersIntoWordsClient({ questionData, onSubmit, isReview
|
|
|
122
122
|
return false;
|
|
123
123
|
return userAnswerValue.trim().toLowerCase() === subQuestion.correctWord.trim().toLowerCase();
|
|
124
124
|
};
|
|
125
|
-
return (_jsx("div", { className: "space-y-
|
|
125
|
+
return (_jsx("div", { className: "space-y-3", children: questionData.subQuestions.map((subQuestion, sqIndex) => {
|
|
126
126
|
const userAnswerValue = answers[subQuestion.id] || '';
|
|
127
127
|
const isCorrect = isReviewMode && isAnswerCorrect(subQuestion.id, userAnswerValue);
|
|
128
128
|
const available = availableLetters[subQuestion.id] || [];
|
|
129
129
|
const arranged = arrangedLetters[subQuestion.id] || [];
|
|
130
130
|
const imagePreviewUrl = getImagePreviewUrl(subQuestion);
|
|
131
|
-
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs(CardTitle, { className: "text-
|
|
131
|
+
return (_jsxs(Card, { children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsxs(CardTitle, { className: "text-sm", children: ["C\u00E2u ", sqIndex + 1] }) }), _jsxs(CardContent, { className: "space-y-3 p-3", children: [imagePreviewUrl && (_jsx("div", { className: "flex justify-center", children: _jsx("div", { className: "relative h-48 w-full max-w-md overflow-hidden rounded-lg", children: _jsx("img", { src: imagePreviewUrl, alt: `Hint for question ${sqIndex + 1}`, className: "h-full w-full object-contain", onError: () => {
|
|
132
132
|
console.error('Failed to load image:', subQuestion.imageUrl);
|
|
133
133
|
} }) }) })), _jsxs("div", { children: [_jsx("p", { className: "mb-2 text-sm font-medium text-gray-700", children: "Ch\u1ECDn c\u00E1c ch\u1EEF c\u00E1i:" }), _jsx("div", { className: "flex flex-wrap gap-2", children: available.map((letter, index) => (_jsx("button", { onClick: () => handleLetterClick(subQuestion.id, index), disabled: isReviewMode, className: "flex h-12 w-12 items-center justify-center rounded-full border-2 border-blue-500 bg-white text-xl font-bold text-blue-600 transition-all hover:bg-blue-50 hover:shadow-md disabled:cursor-not-allowed disabled:opacity-50", children: letter }, `${letter}-${index}`))) })] }), _jsxs("div", { children: [_jsx("p", { className: "mb-2 text-sm font-medium text-gray-700", children: "S\u1EAFp x\u1EBFp th\u00E0nh t\u1EEB:" }), _jsx("div", { className: `min-h-[60px] rounded-lg border-2 p-4 ${isReviewMode
|
|
134
134
|
? isCorrect
|
package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsCreator.js
CHANGED
|
@@ -5,7 +5,8 @@ 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 { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
|
|
8
|
-
import {
|
|
8
|
+
import { Plus, Trash2, Type } from 'lucide-react';
|
|
9
|
+
import { CompactCreatorHeader, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
|
|
9
10
|
import { toast } from 'sonner';
|
|
10
11
|
import { FileUpload } from '../../../../components/ui/file-upload';
|
|
11
12
|
import { ArrangeLettersIntoWordsClient } from './ArrangeLettersIntoWordsClient';
|
|
@@ -105,13 +106,16 @@ onChange, }) {
|
|
|
105
106
|
imageUrl: imagePreviewUrls[sq.id] || sq.imageUrl,
|
|
106
107
|
})),
|
|
107
108
|
};
|
|
108
|
-
return (_jsxs("div", { className: "space-y-
|
|
109
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(ArrangeLettersIntoWordsClient, { questionData: questionData })] }));
|
|
109
110
|
}
|
|
110
|
-
return (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
111
|
+
return (_jsx("div", { className: "space-y-3", children: _jsx(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Type, { className: "h-3.5 w-3.5" }), title: "S\u1EAFp x\u1EBFp ch\u1EEF c\u00E1i th\u00E0nh t\u1EEB", extraActions: _jsxs(Button, { onClick: addSubQuestion, disabled: subQuestions.length >= 5, variant: "outline", size: "sm", className: "h-8", children: [_jsx(Plus, { className: "mr-1.5 h-3.5 w-3.5" }), "Th\u00EAm c\u00E2u h\u1ECFi (", subQuestions.length, "/5)"] }), preview: {
|
|
112
|
+
onClick: () => setIsClientMode(true),
|
|
113
|
+
disabled: subQuestions.length < 1,
|
|
114
|
+
} }), children: _jsxs("div", { className: "space-y-3", children: [subQuestions.map((subQuestion, sqIndex) => (_jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs(CardTitle, { className: "text-sm font-semibold text-gray-800", children: ["C\u00E2u ", sqIndex + 1] }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => removeSubQuestion(subQuestion.id), disabled: subQuestions.length <= 1, children: _jsx(Trash2, { className: "h-4 w-4 text-red-500" }) })] }) }), _jsxs(CardContent, { className: "space-y-2.5 px-3 pb-3", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: `correctWord-${subQuestion.id}`, children: "T\u1EEB \u0111\u00FAng *" }), _jsx(Input, { id: `correctWord-${subQuestion.id}`, value: subQuestion.correctWord, onChange: (e) => updateSubQuestion(subQuestion.id, 'correctWord', e.target.value.toUpperCase()), placeholder: "Nh\u1EADp t\u1EEB \u0111\u00FAng (vd: COMPUTER)", className: "mt-1", disabled: isClientMode }), _jsx("p", { className: "mt-1 text-xs text-gray-500", children: "T\u1EEB n\u00E0y s\u1EBD \u0111\u01B0\u1EE3c x\u00E1o tr\u1ED9n th\u00E0nh c\u00E1c k\u00FD t\u1EF1 ng\u1EABu nhi\u00EAn khi hi\u1EC3n th\u1ECB" })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: `image-${subQuestion.id}`, children: "H\u00ECnh \u1EA3nh g\u1EE3i \u00FD *" }), _jsx(FileUpload, { id: `image-${subQuestion.id}`, label: "", accept: "image/*", value: subQuestion.imageUrl, onChange: (key) => updateSubQuestion(subQuestion.id, 'imageUrl', key), onPresignedUrlChange: (presignedUrl) => {
|
|
115
|
+
updateImagePreviewUrl(subQuestion.id, presignedUrl);
|
|
116
|
+
}, disabled: isClientMode, maxSize: 5, placeholder: "Upload \u1EA3nh", required: true, autoUpload: true, prefix: "questions" }), subQuestion.imageUrl && (_jsx("div", { className: "relative mt-2 h-32 w-full max-w-md overflow-hidden rounded border", children: _jsx("img", { src: imagePreviewUrls[subQuestion.id] || subQuestion.imageUrl, alt: `Preview for question ${sqIndex + 1}`, className: "h-full w-full object-contain", onError: () => {
|
|
117
|
+
console.error('Failed to load image:', subQuestion.imageUrl);
|
|
118
|
+
} }) }))] })] })] }, subQuestion.id))), subQuestions.length === 0 && (_jsx("div", { className: "py-8 text-center text-gray-500", children: _jsx("p", { children: "Ch\u01B0a c\u00F3 c\u00E2u h\u1ECFi con n\u00E0o. Nh\u1EA5n \"Th\u00EAm c\u00E2u h\u1ECFi\" \u0111\u1EC3 b\u1EAFt \u0111\u1EA7u." }) }))] }) }) }));
|
|
115
119
|
}
|
|
116
120
|
export function ArrangeLettersIntoWordsCreator(props) {
|
|
117
121
|
return _jsx(ArrangeLettersIntoWordsCreatorContent, { ...props });
|
package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js
CHANGED
|
@@ -30,7 +30,7 @@ export function ChooseCorrectAdjectiveClient({ questionData, onSubmit, isReviewM
|
|
|
30
30
|
};
|
|
31
31
|
// Get the correct answer text
|
|
32
32
|
const correctAnswerText = getOptionText(questionData.options[questionData.correctAnswer]);
|
|
33
|
-
return (_jsx("div", { className: "space-y-
|
|
33
|
+
return (_jsx("div", { className: "space-y-3", children: _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: cn('flex items-center justify-between border-b px-3 py-2', isReviewMode && userAnswerCorrect && 'border-green-200 bg-gradient-to-r from-green-50 to-emerald-50', isReviewMode && hasAnswered && !userAnswerCorrect && 'border-red-200 bg-gradient-to-r from-red-50 to-rose-50', (!isReviewMode || !hasAnswered) && 'border-gray-100 bg-gradient-to-r from-gray-50 to-white'), children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: cn('flex h-8 w-8 items-center justify-center rounded-lg text-sm font-bold text-white shadow-sm', isReviewMode && userAnswerCorrect && 'bg-gradient-to-br from-green-500 to-emerald-600', isReviewMode && hasAnswered && !userAnswerCorrect && 'bg-gradient-to-br from-red-500 to-rose-600', (!isReviewMode || !hasAnswered) && 'bg-gradient-to-br from-teal-500 to-cyan-600'), children: _jsx(ListChecks, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Ch\u1ECDn t\u00EDnh t\u1EEB \u0111\u00FAng" })] }), isReviewMode && hasAnswered && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold', userAnswerCorrect ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'), children: [userAnswerCorrect ? _jsx(Check, { className: "h-3.5 w-3.5" }) : _jsx(X, { className: "h-3.5 w-3.5" }), userAnswerCorrect ? 'Đúng' : 'Sai'] }))] }), _jsxs("div", { className: "p-3 space-y-3", children: [_jsxs("div", { className: "rounded-xl bg-gradient-to-br from-slate-50 to-gray-50 border border-gray-100 p-5", children: [_jsx("div", { className: "text-lg leading-relaxed flex flex-wrap items-center gap-1", children: questionData.sentence.split('{0}').map((part, index, arr) => (_jsxs("span", { children: [_jsx("span", { className: "text-gray-800", children: part }), index < arr.length - 1 && (_jsx("span", { className: "inline-flex items-center mx-1", children: _jsx("span", { className: cn('inline-block min-w-[100px] px-3 py-1 text-center font-bold rounded-lg border-2 border-dashed', hasAnswered
|
|
34
34
|
? isReviewMode
|
|
35
35
|
? userAnswerCorrect
|
|
36
36
|
? 'border-green-400 bg-green-100 text-green-800'
|
package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveCreator.js
CHANGED
|
@@ -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 { Textarea } from '../../../../components/ui/textarea';
|
|
8
|
-
import { PointsInput } from '../../../../components/ui/points-input';
|
|
9
8
|
import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
|
|
10
9
|
import { RadioGroup } from '../../../../components/ui/radio-group';
|
|
11
10
|
import { toast } from 'sonner';
|
|
12
|
-
import {
|
|
11
|
+
import { Plus, Trash2, Lightbulb, CheckCircle2, MessageSquare, Type } from 'lucide-react';
|
|
12
|
+
import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
|
|
13
13
|
import { useDebouncedCallback } from '../../../../shared/lib/hooks';
|
|
14
14
|
import { ChooseCorrectAdjectiveClient } from './ChooseCorrectAdjectiveClient';
|
|
15
15
|
function ChooseCorrectAdjectiveCreatorContent({ initialData, onSave, onCancel, onChange, externalErrors, optionCount = 3, }) {
|
|
@@ -151,10 +151,16 @@ function ChooseCorrectAdjectiveCreatorContent({ initialData, onSave, onCancel, o
|
|
|
151
151
|
correctAnswer,
|
|
152
152
|
explanation,
|
|
153
153
|
};
|
|
154
|
-
return (_jsxs("div", { className: "space-y-
|
|
154
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(ChooseCorrectAdjectiveClient, { questionData: questionData, isReviewMode: true, userAnswer: correctAnswer !== -1 ? correctAnswer : undefined })] }));
|
|
155
155
|
}
|
|
156
156
|
// ============ CREATOR UI ============
|
|
157
|
-
return (_jsxs("div", { className: "space-y-
|
|
157
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Type, { className: "h-3.5 w-3.5" }), title: "Ch\u1ECDn t\u00EDnh t\u1EEB \u0111\u00FAng", points: {
|
|
158
|
+
value: points,
|
|
159
|
+
onValueChange: setPoints,
|
|
160
|
+
}, preview: {
|
|
161
|
+
onClick: () => setIsClientMode(true),
|
|
162
|
+
disabled: !sentence.trim() || options.filter((o) => o.trim()).length < 1,
|
|
163
|
+
} }), children: [_jsxs("div", { className: "space-y-1.5", children: [_jsxs(Label, { htmlFor: "sentence", 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-purple-500" }), "C\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Textarea, { id: "sentence", value: sentence, onChange: (e) => setSentence(e.target.value), placeholder: "V\u00ED d\u1EE5: The elephant is a very {0} animal.", className: `min-h-[80px] border-gray-200 bg-white transition-all focus:border-purple-300 focus:ring-purple-200 ${!sentence.includes('{0}') && sentence ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}`, rows: 2 }), !sentence.includes('{0}') && sentence && (_jsxs("p", { className: "text-sm text-red-600", children: ["\u26A0\uFE0F C\u00E2u c\u1EA7n c\u00F3 ch\u1ED7 tr\u1ED1ng ", _jsx("code", { className: "rounded bg-red-100 px-1", children: '{0}' })] }))] }), 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(' · ') })] }))] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-violet-100", children: _jsx(Lightbulb, { className: "h-3.5 w-3.5 text-violet-600" }) }), _jsxs(CardTitle, { className: "text-lg font-semibold text-gray-800", children: ["C\u00E1c l\u1EF1a ch\u1ECDn ", _jsxs("span", { className: "text-sm font-normal text-gray-500", children: ["(", options.length, " \u0111\u00E1p \u00E1n)"] })] })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: handleAddOption, className: "gap-2 border-green-200 text-green-600 hover:bg-green-50 hover:text-green-700", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm \u0111\u00E1p \u00E1n"] })] }) }), _jsxs(CardContent, { className: "space-y-2.5 px-3 pb-3", children: [_jsx(RadioGroup, { value: correctAnswer.toString(), onValueChange: (value) => setCorrectAnswer(Number(value)), children: options.map((option, index) => {
|
|
158
164
|
const isCorrect = correctAnswer === index;
|
|
159
165
|
const optionLetter = String.fromCharCode(65 + index);
|
|
160
166
|
return (_jsx("div", { className: `group relative rounded-xl border-2 p-4 transition-all duration-200 ${isCorrect
|
|
@@ -162,7 +168,7 @@ function ChooseCorrectAdjectiveCreatorContent({ initialData, onSave, onCancel, o
|
|
|
162
168
|
: 'border-gray-200 bg-white hover:border-gray-300 hover:shadow-sm'}`, children: _jsxs("div", { className: "flex items-start gap-4", children: [_jsx("div", { className: `flex h-10 w-10 flex-shrink-0 cursor-pointer items-center justify-center rounded-xl font-bold transition-all ${isCorrect
|
|
163
169
|
? 'bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md'
|
|
164
170
|
: 'bg-gray-100 text-gray-600 hover:bg-purple-100 hover:text-purple-600'}`, onClick: () => setCorrectAnswer(index), children: optionLetter }), _jsxs("div", { className: "flex-1 space-y-2", children: [_jsx(Input, { id: `option-input-${index}`, value: option, onChange: (e) => handleOptionChange(index, e.target.value), placeholder: `Nhập đáp án ${optionLetter}...`, className: "border-gray-200 bg-white transition-all focus:border-purple-300 focus:ring-purple-200" }), isCorrect && (_jsxs("span", { className: "inline-flex items-center gap-1.5 rounded-full bg-green-100 px-3 py-1 text-xs font-semibold text-green-700", children: [_jsx(CheckCircle2, { className: "h-3.5 w-3.5" }), "\u0110\u00E1p \u00E1n \u0111\u00FAng"] }))] }), _jsxs("div", { className: "flex items-center gap-2", children: [!isCorrect && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => setCorrectAnswer(index), className: "text-gray-400 hover:text-green-600", title: "Ch\u1ECDn l\u00E0m \u0111\u00E1p \u00E1n \u0111\u00FAng", children: _jsx(CheckCircle2, { className: "h-4 w-4" }) })), options.length > 2 && (_jsx(Button, { variant: "ghost", size: "sm", onClick: () => handleDeleteOption(index), className: "text-red-500 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] })] }) }, index));
|
|
165
|
-
}) }), correctAnswer === -1 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-amber-100", children: _jsx("span", { className: "text-lg", children: "\u26A0\uFE0F" }) }), _jsxs("div", { className: "text-sm text-amber-800", children: [_jsx("p", { className: "font-semibold", children: "Ch\u01B0a ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng" }), _jsx("p", { className: "mt-0.5 text-amber-700", children: "Click v\u00E0o \u00F4 ch\u1EEF c\u00E1i ho\u1EB7c icon \u2713 \u0111\u1EC3 ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng" })] })] })), sentence && options.filter((o) => o.trim()).length >= 2 && (_jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: [_jsx("p", { className: "text-xs font-semibold text-gray-600 uppercase tracking-wide", children: "Xem tr\u01B0\u1EDBc:" }), _jsxs("p", { className: "mt-2 text-sm text-gray-700", children: [sentence.split('{0}').map((part, index, arr) => (_jsxs("span", { children: [part, index < arr.length - 1 && (_jsx("span", { className: "mx-1 inline-block text-purple-600", children: "______" }))] }, index))), _jsxs("span", { className: "text-gray-500", children: [" (", options.filter((o) => o.trim()).join(' / '), ")"] })] }), correctAnswer >= 0 && options[correctAnswer] && (_jsxs("p", { className: "mt-2 flex items-center gap-2 text-sm text-green-600", children: [_jsx(CheckCircle2, { className: "h-4 w-4" }), sentence.replace('{0}', options[correctAnswer])] }))] }))] })] }),
|
|
171
|
+
}) }), correctAnswer === -1 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50 p-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-amber-100", children: _jsx("span", { className: "text-lg", children: "\u26A0\uFE0F" }) }), _jsxs("div", { className: "text-sm text-amber-800", children: [_jsx("p", { className: "font-semibold", children: "Ch\u01B0a ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng" }), _jsx("p", { className: "mt-0.5 text-amber-700", children: "Click v\u00E0o \u00F4 ch\u1EEF c\u00E1i ho\u1EB7c icon \u2713 \u0111\u1EC3 ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng" })] })] })), sentence && options.filter((o) => o.trim()).length >= 2 && (_jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-4", children: [_jsx("p", { className: "text-xs font-semibold text-gray-600 uppercase tracking-wide", children: "Xem tr\u01B0\u1EDBc:" }), _jsxs("p", { className: "mt-2 text-sm text-gray-700", children: [sentence.split('{0}').map((part, index, arr) => (_jsxs("span", { children: [part, index < arr.length - 1 && (_jsx("span", { className: "mx-1 inline-block text-purple-600", children: "______" }))] }, index))), _jsxs("span", { className: "text-gray-500", children: [" (", options.filter((o) => o.trim()).join(' / '), ")"] })] }), correctAnswer >= 0 && options[correctAnswer] && (_jsxs("p", { className: "mt-2 flex items-center gap-2 text-sm text-green-600", children: [_jsx(CheckCircle2, { className: "h-4 w-4" }), sentence.replace('{0}', options[correctAnswer])] }))] }))] })] }), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation })] }));
|
|
166
172
|
}
|
|
167
173
|
export function ChooseCorrectAdjectiveCreator(props) {
|
|
168
174
|
return _jsx(ChooseCorrectAdjectiveCreatorContent, { ...props });
|
package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js
CHANGED
|
@@ -110,18 +110,18 @@ export function ChooseTheCorrectAnswerClient({ questionData, onSubmit, isReviewM
|
|
|
110
110
|
.split('\n')
|
|
111
111
|
.map((line) => `<p class="last:mb-0">${line}</p>`)
|
|
112
112
|
.join(''),
|
|
113
|
-
} })), questionData.groupImageUrl && (_jsxs("div", { className: "mt-5 relative overflow-hidden rounded-xl border border-slate-200/60 bg-slate-50 shadow-sm transition-all hover:shadow-md", children: [_jsx("div", { className: "flex items-center justify-between border-b border-slate-200/60 bg-slate-100/50 px-4 py-2", children: _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-slate-300" }), _jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-slate-300" }), _jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-slate-300" }), _jsx("span", { className: "ml-2 text-[11px] font-semibold uppercase tracking-wider text-slate-500", children: "T\u00E0i li\u1EC7u \u0111\u00EDnh k\u00E8m" })] }) }), _jsx("div", { className: "flex items-center justify-center p-4 min-h-[160px]", children: _jsx(OptionImage, { src: questionData.groupImageUrl, alt: "Group illustration", className: "max-h-64 w-auto rounded-lg object-contain" }) })] }))] }))] })] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-xs", children: [_jsxs("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-
|
|
113
|
+
} })), questionData.groupImageUrl && (_jsxs("div", { className: "mt-5 relative overflow-hidden rounded-xl border border-slate-200/60 bg-slate-50 shadow-sm transition-all hover:shadow-md", children: [_jsx("div", { className: "flex items-center justify-between border-b border-slate-200/60 bg-slate-100/50 px-4 py-2", children: _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-slate-300" }), _jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-slate-300" }), _jsx("div", { className: "h-2.5 w-2.5 rounded-full bg-slate-300" }), _jsx("span", { className: "ml-2 text-[11px] font-semibold uppercase tracking-wider text-slate-500", children: "T\u00E0i li\u1EC7u \u0111\u00EDnh k\u00E8m" })] }) }), _jsx("div", { className: "flex items-center justify-center p-4 min-h-[160px]", children: _jsx(OptionImage, { src: questionData.groupImageUrl, alt: "Group illustration", className: "max-h-64 w-auto rounded-lg object-contain" }) })] }))] }))] })] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-xs", 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-md bg-gradient-to-br from-blue-500 to-indigo-600 text-white", children: _jsx(CircleHelp, { className: "h-3.5 w-3.5" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: isMultipleAnswers ? 'Chọn các đáp án đúng' : 'Chọn đáp án đúng' })] }), questionData.points !== undefined && (_jsx("div", { className: "flex items-center gap-1.5 rounded-full bg-blue-50 px-2.5 py-0.5 text-xs font-semibold text-blue-700", children: _jsxs("span", { children: [questionData.points, " \u0111i\u1EC3m"] }) }))] }), _jsxs("div", { className: "space-y-3 p-3", children: [(questionData.askerName || questionData.answererName) && (_jsxs("div", { className: "flex items-center gap-2 rounded-lg bg-gray-50 p-2.5 text-xs text-gray-600", children: [_jsx(Users, { className: "h-4 w-4 text-gray-400" }), questionData.askerName && (_jsxs("span", { children: [_jsx("strong", { children: "Ng\u01B0\u1EDDi h\u1ECFi:" }), " ", questionData.askerName] })), questionData.askerName && questionData.answererName && _jsx("span", { children: "\u2022" }), questionData.answererName && (_jsxs("span", { children: [_jsx("strong", { children: "Ng\u01B0\u1EDDi tr\u1EA3 l\u1EDDi:" }), " ", questionData.answererName] }))] })), showQuestion && (_jsx("div", { className: "rounded-md border border-blue-100 bg-gradient-to-r from-blue-50 to-indigo-50 px-3 py-2", children: isRichTextEmpty(questionData.question) ? (_jsx("h3", { className: "text-sm font-semibold text-gray-800", children: "C\u00E2u h\u1ECFi ch\u01B0a \u0111\u01B0\u1EE3c nh\u1EADp" })) : (_jsx(RichTextHtml, { html: questionData.question, className: "text-sm font-semibold text-gray-800" })) })), showImage && (imagePreviewUrl || cleanImageUrl) && (_jsxs("div", { className: "overflow-hidden rounded-lg border border-gray-200 bg-gradient-to-br from-slate-50 to-gray-100", children: [_jsx("div", { className: "border-b border-gray-100 px-3 py-1.5", children: _jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("div", { className: "h-2 w-2 rounded-full bg-red-400" }), _jsx("div", { className: "h-2 w-2 rounded-full bg-yellow-400" }), _jsx("div", { className: "h-2 w-2 rounded-full bg-green-400" }), _jsx("span", { className: "ml-2 text-xs font-medium text-gray-500", children: "H\u00ECnh \u1EA3nh" })] }) }), _jsx("div", { className: "flex items-center justify-center p-4", children: isLoading ? (_jsx("div", { className: "flex h-48 items-center justify-center", children: _jsx("div", { className: "h-8 w-8 animate-spin rounded-full border-4 border-gray-200 border-t-blue-500" }) })) : (
|
|
114
114
|
/* eslint-disable-next-line @next/next/no-img-element */
|
|
115
115
|
_jsx("img", { src: imagePreviewUrl || cleanImageUrl, alt: "Question illustration", className: "max-h-56 w-auto object-contain rounded-md", onError: (e) => {
|
|
116
116
|
const target = e.target;
|
|
117
117
|
target.onerror = null;
|
|
118
118
|
target.src = '/images/placeholder-image.svg';
|
|
119
|
-
} })) })] })), showOptions && (_jsxs("div", { className: "space-y-
|
|
119
|
+
} })) })] })), showOptions && (_jsxs("div", { className: "space-y-1.5", children: [_jsx("span", { className: "text-xs font-medium text-gray-500 uppercase tracking-wider", children: isMultipleAnswers ? 'Chọn một hoặc nhiều đáp án' : 'Chọn đáp án' }), displayOptions.map((option, index) => {
|
|
120
120
|
const isSelected = selectedAnswer.includes(index);
|
|
121
121
|
const isCorrectAnswer = isCorrect(index);
|
|
122
122
|
const letter = String.fromCharCode(65 + index);
|
|
123
123
|
const optionText = optionToString(option);
|
|
124
|
-
return (_jsxs("button", { type: "button", onClick: () => handleAnswerChange(index), disabled: isReviewMode, className: cn('group flex w-full items-center gap-
|
|
124
|
+
return (_jsxs("button", { type: "button", onClick: () => handleAnswerChange(index), disabled: isReviewMode, className: cn('group flex w-full items-center gap-2 rounded-md border px-3 py-1.5 text-left transition-all', isReviewMode
|
|
125
125
|
? isCorrectAnswer
|
|
126
126
|
? 'border-green-400 bg-green-50'
|
|
127
127
|
: isSelected
|
|
@@ -129,7 +129,7 @@ export function ChooseTheCorrectAnswerClient({ questionData, onSubmit, isReviewM
|
|
|
129
129
|
: 'border-gray-200 bg-gray-50'
|
|
130
130
|
: isSelected
|
|
131
131
|
? 'border-blue-500 bg-blue-50 shadow-sm ring-2 ring-blue-100'
|
|
132
|
-
: 'border-gray-200 bg-white hover:border-gray-300 hover:bg-gray-50 hover:shadow-sm'), children: [_jsx("div", { className: cn('flex h-
|
|
132
|
+
: 'border-gray-200 bg-white hover:border-gray-300 hover:bg-gray-50 hover:shadow-sm'), children: [_jsx("div", { className: cn('flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-full text-xs font-bold transition-all', isReviewMode
|
|
133
133
|
? isCorrectAnswer
|
|
134
134
|
? 'bg-green-500 text-white'
|
|
135
135
|
: isSelected
|
|
@@ -144,7 +144,7 @@ export function ChooseTheCorrectAnswerClient({ questionData, onSubmit, isReviewM
|
|
|
144
144
|
: 'text-gray-700') })), isReviewMode && (isCorrectAnswer || isSelected) && (_jsx("div", { className: cn('flex h-6 w-6 items-center justify-center rounded-full', isCorrectAnswer ? 'bg-green-500' : 'bg-red-500'), children: isCorrectAnswer
|
|
145
145
|
? _jsx(Check, { className: "h-4 w-4 text-white" })
|
|
146
146
|
: _jsx(X, { className: "h-4 w-4 text-white" }) }))] }, index));
|
|
147
|
-
})] })), !isReviewMode && !hasAnswered && (_jsxs("div", { className: "rounded-
|
|
147
|
+
})] })), !isReviewMode && !hasAnswered && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-amber-200 bg-amber-50 px-2.5 py-1.5", children: [_jsx("div", { className: "flex h-4 w-4 flex-shrink-0 items-center justify-center rounded-full bg-amber-400 text-white", children: _jsx("span", { className: "text-[10px] font-bold", children: "!" }) }), _jsx("span", { className: "text-xs text-amber-800", children: isMultipleAnswers ? 'Vui lòng chọn ít nhất một đáp án' : 'Vui lòng chọn một đáp án' })] }))] })] }), isReviewMode && questionData.explanation && (_jsxs("div", { className: "flex items-start gap-2 rounded-md border border-amber-200 bg-amber-50 px-3 py-2", children: [_jsx(Lightbulb, { className: "mt-0.5 h-3.5 w-3.5 shrink-0 text-amber-600" }), _jsxs("p", { className: "text-xs text-gray-700", children: [_jsx("span", { className: "font-semibold text-gray-800", children: "Gi\u1EA3i th\u00EDch: " }), questionData.explanation] })] })), isReviewMode && (_jsxs("div", { className: "overflow-hidden rounded-xl 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-gray-50 to-white px-3 py-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gradient-to-br from-slate-500 to-gray-600 text-white", children: _jsx(ClipboardList, { className: "h-3.5 w-3.5" }) }), _jsx("h4", { className: "text-sm font-semibold text-gray-800", children: "T\u1ED5ng k\u1EBFt" })] }), _jsxs("div", { className: "space-y-3 p-3", children: [_jsxs("div", { className: "flex items-center justify-between rounded-lg bg-gray-50 p-3", children: [_jsx("span", { className: "text-sm text-gray-600", children: "\u0110\u00E1p \u00E1n c\u1EE7a b\u1EA1n:" }), _jsx("span", { className: cn('text-sm font-semibold', isAnswerCorrect ? 'text-green-600' : hasAnswered ? 'text-red-600' : 'text-gray-500'), children: formatAnswerLabel(selectedAnswer) })] }), _jsxs("div", { className: "flex items-center justify-between rounded-lg bg-green-50 p-3 border border-green-100", children: [_jsx("span", { className: "text-sm text-gray-600", children: "\u0110\u00E1p \u00E1n \u0111\u00FAng:" }), _jsx("span", { className: "text-sm font-semibold text-green-600", children: formatAnswerLabel(normalizedCorrectAnswers) })] }), _jsxs("div", { className: cn('flex items-center justify-between rounded-lg p-3 border', isAnswerCorrect
|
|
148
148
|
? 'bg-green-50 border-green-200'
|
|
149
149
|
: 'bg-red-50 border-red-200'), children: [_jsx("span", { className: "text-sm text-gray-600", children: "K\u1EBFt qu\u1EA3:" }), _jsx("div", { className: cn('flex items-center gap-1.5 text-sm font-semibold', isAnswerCorrect ? 'text-green-600' : 'text-red-600'), children: isAnswerCorrect ? (_jsxs(_Fragment, { children: [_jsx(Check, { className: "h-4 w-4" }), _jsx("span", { children: "Ch\u00EDnh x\u00E1c" })] })) : (_jsxs(_Fragment, { children: [_jsx(X, { className: "h-4 w-4" }), _jsx("span", { children: "Sai" })] })) })] })] })] }))] }));
|
|
150
150
|
}
|
package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerCreator.js
CHANGED
|
@@ -6,13 +6,13 @@ import { Button } from '../../../../components/ui/button';
|
|
|
6
6
|
import { Label } from '../../../../components/ui/label';
|
|
7
7
|
import { Input } from '../../../../components/ui/input';
|
|
8
8
|
import { Textarea } from '../../../../components/ui/textarea';
|
|
9
|
-
import { PointsInput } from '../../../../components/ui/points-input';
|
|
10
9
|
import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
|
|
11
10
|
import { RadioGroup } from '../../../../components/ui/radio-group';
|
|
12
11
|
import { Switch } from '../../../../components/ui/switch';
|
|
13
12
|
import { usePresignedFileUrl, useDebouncedCallback } from '../../../../shared/lib/hooks';
|
|
14
13
|
import { useReactHookForm } from '../../../../shared/lib/hooks/useReactHookForm';
|
|
15
|
-
import {
|
|
14
|
+
import { Plus, Trash2, HelpCircle, CheckCircle2, Lightbulb, MessageSquare, ImageIcon, Type, Users, ListChecks, Pilcrow } from 'lucide-react';
|
|
15
|
+
import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
|
|
16
16
|
import { FileUpload } from '../../../../components/ui/file-upload';
|
|
17
17
|
import { RichTextEditor } from '../../../../components/shared/RichTextEditor';
|
|
18
18
|
import { hasRichTextFormatting, htmlToPlainEditorText, isRichTextEmpty } from '../../../../shared/lib/rich-text';
|
|
@@ -135,7 +135,6 @@ function ChooseTheCorrectAnswerCreatorContent({ initialData, onSave, onCancel, o
|
|
|
135
135
|
const [optionPreviewUrls, setOptionPreviewUrls] = useState({});
|
|
136
136
|
const [isImageInputVisible, setIsImageInputVisible] = useState(() => Boolean(apiData?.content?.imageUrl || apiData?.imageUrl));
|
|
137
137
|
const [isTipTapMode, setIsTipTapMode] = useState(() => shouldStartInTipTapMode(apiData));
|
|
138
|
-
const [isExplanationVisible, setIsExplanationVisible] = useState(() => Boolean(apiData?.explanation));
|
|
139
138
|
// ============ GROUP (shared title / content / image) ============
|
|
140
139
|
const { isGrouped, currentGroupNumber, isFirstInGroup, groupTitle, groupSubtitle, groupContent, imageUrl: groupImageUrl, articles, setGroupTitle, setGroupSubtitle, setGroupContent, setImageUrl: setGroupImageUrl, setArticles, } = useBasicQuestionGroup({
|
|
141
140
|
partId,
|
|
@@ -426,33 +425,40 @@ function ChooseTheCorrectAnswerCreatorContent({ initialData, onSave, onCancel, o
|
|
|
426
425
|
groupNumber: currentGroupNumber,
|
|
427
426
|
...(effectiveGroupType === 'ARTICLES' ? { articles } : {}),
|
|
428
427
|
};
|
|
429
|
-
return (_jsxs("div", { className: "space-y-4", children: [_jsx(
|
|
428
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(ChooseTheCorrectAnswerClient, { questionData: questionData, questionConfig: questionConfig, isReviewMode: true, userAnswer: watchedMultipleAnswers
|
|
430
429
|
? watchedCorrectAnswer
|
|
431
430
|
: watchedCorrectAnswer !== -1
|
|
432
431
|
? watchedCorrectAnswer
|
|
433
432
|
: undefined })] }));
|
|
434
433
|
}
|
|
435
434
|
// Creator UI
|
|
436
|
-
return (_jsxs("div", { className: "space-y-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
435
|
+
return (_jsxs("div", { className: "space-y-3", children: [isGrouped && effectiveGroupType === 'ARTICLES' && (_jsx(ArticlesGroupCard, { groupNumber: currentGroupNumber, articleCount: articleCount, articles: articles, onArticlesChange: setArticles, title: groupTitle, onTitleChange: setGroupTitle, isReadOnly: !isFirstInGroup, uploadIdPrefix: `choose-correct-answer-${partId ?? 'articles'}` })), isGrouped && effectiveGroupType === 'BASIC' && (_jsx(BasicQuestionGroupCard, { groupNumber: currentGroupNumber, title: groupTitle, subtitle: groupSubtitle, content: groupContent, imageUrl: groupImageUrl, onTitleChange: setGroupTitle, onSubtitleChange: setGroupSubtitle, onContentChange: setGroupContent, onImageChange: setGroupImageUrl, groupConfig: groupType ? groupConfig : undefined, isReadOnly: !isFirstInGroup, uploadIdPrefix: `choose-correct-answer-${partId ?? 'group'}` })), isGrouped && effectiveGroupType === 'DOCUMENT' && (_jsx(DocumentGroupCard, { groupNumber: currentGroupNumber, title: groupTitle, subtitle: groupSubtitle, content: groupContent, imageUrl: groupImageUrl, onTitleChange: setGroupTitle, onSubtitleChange: setGroupSubtitle, onContentChange: setGroupContent, onImageChange: setGroupImageUrl, groupConfig: groupType ? groupConfig : undefined, isReadOnly: !isFirstInGroup, uploadIdPrefix: `choose-correct-answer-${partId ?? 'document'}` })), _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(HelpCircle, { className: "h-3.5 w-3.5" }), title: "Ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng", points: {
|
|
436
|
+
value: watchedPoints,
|
|
437
|
+
onValueChange: (value) => form.setValue('points', value, { shouldValidate: true }),
|
|
438
|
+
error: form.hasError('points') ? form.getFieldError('points') : undefined,
|
|
439
|
+
}, example: {
|
|
440
|
+
checked: watchedIsExample,
|
|
441
|
+
onChange: (checked) => form.setValue('isExample', checked),
|
|
442
|
+
}, extraActions: _jsxs("button", { type: "button", disabled: Boolean(multipleAnswers), onClick: () => {
|
|
443
|
+
const enabled = !watchedMultipleAnswers;
|
|
444
|
+
const currentValue = form.getValues('correctAnswer');
|
|
445
|
+
const normalized = Array.isArray(currentValue)
|
|
446
|
+
? currentValue
|
|
447
|
+
: currentValue === -1 ? [] : [currentValue];
|
|
448
|
+
form.setValue('multipleAnswers', enabled, { shouldValidate: true });
|
|
449
|
+
form.setValue('correctAnswer', enabled ? normalized : (normalized[0] ?? -1), { shouldValidate: true });
|
|
450
|
+
}, className: `inline-flex h-8 items-center gap-1.5 rounded-md border px-2.5 text-xs font-medium transition-colors select-none ${watchedMultipleAnswers
|
|
451
|
+
? 'border-violet-300 bg-violet-50 text-violet-700'
|
|
452
|
+
: 'border-gray-200 bg-white text-gray-400 hover:text-gray-600'} ${Boolean(multipleAnswers) ? 'cursor-not-allowed opacity-50' : ''}`, children: [_jsx(ListChecks, { className: "h-3.5 w-3.5" }), "Nhi\u1EC1u \u0111\u00E1p \u00E1n"] }), headerExtra: headerExtra, preview: {
|
|
453
|
+
onClick: () => setIsClientMode(true),
|
|
454
|
+
disabled: watchedOptions.filter((o) => watchedOptionType === 'image'
|
|
455
|
+
? typeof o === 'string' && o.trim()
|
|
456
|
+
: !isRichTextEmpty(o)).length < 2,
|
|
457
|
+
} }), 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: [_jsxs(Label, { htmlFor: "askerName", className: "flex items-center gap-1 text-xs font-medium text-gray-600", children: ["T\u00EAn ng\u01B0\u1EDDi h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "askerName", ...form.register('askerName'), placeholder: "VD: John, Mary, Teacher...", className: `h-8 border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${form.hasError('askerName') ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), form.hasError('askerName') && (_jsx("p", { className: "text-xs text-red-600", children: form.getFieldError('askerName') }))] }), _jsxs("div", { className: "space-y-1", children: [_jsxs(Label, { htmlFor: "answererName", className: "flex items-center gap-1 text-xs font-medium text-gray-600", children: ["T\u00EAn ng\u01B0\u1EDDi tr\u1EA3 l\u1EDDi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "answererName", ...form.register('answererName'), placeholder: "VD: Tom, Anna, Student...", className: `h-8 border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${form.hasError('answererName') ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), form.hasError('answererName') && (_jsx("p", { className: "text-xs text-red-600", children: form.getFieldError('answererName') }))] })] })] })), (!questionConfig || questionConfig.length === 0 || questionConfig.includes('question')) && (_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-indigo-500" }), _jsx("span", { children: "C\u00E2u h\u1ECFi" }), _jsx("span", { className: "text-xs font-normal text-gray-500", children: "(tu\u1EF3 ch\u1ECDn)" })] }), _jsxs("button", { type: "button", onClick: () => setIsTipTapMode((enabled) => !enabled), title: isTipTapMode ? 'Chuyển về ô nhập thường' : 'Bật định dạng chữ (in đậm, danh sách, bảng)', className: `inline-flex h-8 items-center gap-1.5 rounded-md border px-2.5 text-xs font-medium transition-colors select-none ${isTipTapMode
|
|
458
|
+
? 'border-indigo-300 bg-indigo-50 text-indigo-700'
|
|
459
|
+
: 'border-gray-200 bg-white text-gray-400 hover:text-gray-600'}`, children: [_jsx(Pilcrow, { className: "h-3.5 w-3.5" }), "\u0110\u1ECBnh d\u1EA1ng"] })] }), _jsx(ChooseAnswerTextInput, { value: watchedQuestion ?? '', onChange: (html) => form.setValue('question', html, { shouldValidate: true }), isTipTapMode: isTipTapMode, placeholder: "Nh\u1EADp c\u00E2u h\u1ECFi..." }), form.hasError('question') && (_jsx("p", { className: "text-sm text-red-600", children: form.getFieldError('question') }))] })), (!questionConfig || questionConfig.length === 0 || questionConfig.includes('imageUrl') || questionConfig.includes('imageUrl ')) && !isImageInputVisible && (_jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: () => setIsImageInputVisible(true), className: "h-8 gap-1.5 border-indigo-200 px-2.5 text-xs text-indigo-600 hover:bg-indigo-50 hover:text-indigo-700", children: [_jsx(ImageIcon, { className: "h-3.5 w-3.5" }), watchedImageUrl ? 'Hiện hình ảnh' : 'Thêm hình ảnh'] })), (!questionConfig || questionConfig.length === 0 || questionConfig.includes('imageUrl') || questionConfig.includes('imageUrl ')) && isImageInputVisible && (_jsxs("div", { className: "space-y-2", children: [_jsx(FileUpload, { id: "question-image", label: "", accept: "image/*", value: watchedImageUrl || '', onChange: (url) => {
|
|
460
|
+
form.setValue('imageUrl', url, { shouldValidate: true });
|
|
461
|
+
}, onPresignedUrlChange: setImagePreviewUrl, maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" }), form.hasError('imageUrl') && (_jsx("p", { className: "mt-1 text-sm text-red-600", children: form.getFieldError('imageUrl') })), displayPreviewUrl && (_jsx(ImagePreview, { src: displayPreviewUrl, alt: "Preview", className: "mt-2 h-48 w-full max-w-md rounded-lg border border-gray-200" }))] })), 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(' · ') })] })), form.formState.errors.options?.message && (_jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-2", children: _jsx("p", { className: "text-xs text-red-600", children: form.formState.errors.options.message }) }))] }), (!questionConfig || questionConfig.length === 0 || questionConfig.includes('options')) && (_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("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-indigo-100", children: _jsx(Lightbulb, { className: "h-3.5 w-3.5 text-indigo-600" }) }), _jsxs(CardTitle, { className: "text-sm font-semibold text-gray-800", children: ["C\u00E1c l\u1EF1a ch\u1ECDn ", _jsxs("span", { className: "text-xs font-normal text-gray-500", children: ["(", watchedOptions.length, " \u0111\u00E1p \u00E1n)"] })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-1.5", children: [_jsxs("div", { className: "flex h-8 items-center gap-1.5 rounded-md border border-gray-200 bg-gray-50 px-2.5", children: [_jsx(Type, { className: `h-3.5 w-3.5 ${watchedOptionType === 'text' ? 'text-indigo-600' : 'text-gray-400'}` }), _jsx("span", { className: `text-xs font-medium ${watchedOptionType === 'text' ? 'text-indigo-600' : 'text-gray-400'}`, children: "Text" }), _jsx(Switch, { checked: watchedOptionType === 'image', disabled: true, className: watchedOptionType === 'image' ? '!bg-indigo-600 !opacity-100' : '!bg-indigo-600 !opacity-100', onCheckedChange: (checked) => {
|
|
456
462
|
const newType = checked ? 'image' : 'text';
|
|
457
463
|
const currentOptions = form.getValues('options');
|
|
458
464
|
const currentCorrectAnswer = form.getValues('correctAnswer');
|
|
@@ -481,7 +487,7 @@ function ChooseTheCorrectAnswerCreatorContent({ initialData, onSave, onCancel, o
|
|
|
481
487
|
} }), _jsx(ImageIcon, { className: `h-3.5 w-3.5 ${watchedOptionType === 'image' ? 'text-indigo-600' : 'text-gray-400'}` }), _jsx("span", { className: `text-xs font-medium ${watchedOptionType === 'image' ? 'text-indigo-600' : 'text-gray-400'}`, children: "\u1EA2nh" })] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => {
|
|
482
488
|
const currentOptions = form.getValues('options');
|
|
483
489
|
form.setValue('options', [...currentOptions, '']);
|
|
484
|
-
}, className: "gap-
|
|
490
|
+
}, className: "h-8 gap-1.5 border-green-200 px-2.5 text-xs text-green-600 hover:bg-green-50 hover:text-green-700", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm \u0111\u00E1p \u00E1n"] })] })] }) }), _jsxs(CardContent, { className: "space-y-2 px-3 pb-3", children: [watchedMultipleAnswers ? (_jsx("div", { className: "space-y-3", children: watchedOptions.map((option, index) => {
|
|
485
491
|
const optionError = form.formState.errors.options?.[index];
|
|
486
492
|
const selectedAnswers = Array.isArray(watchedCorrectAnswer)
|
|
487
493
|
? watchedCorrectAnswer
|
|
@@ -595,9 +601,9 @@ function ChooseTheCorrectAnswerCreatorContent({ initialData, onSave, onCancel, o
|
|
|
595
601
|
}
|
|
596
602
|
}, className: "text-red-500 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] })] }) }, index));
|
|
597
603
|
}) })), ((Array.isArray(watchedCorrectAnswer) && watchedCorrectAnswer.length === 0)
|
|
598
|
-
|| (!Array.isArray(watchedCorrectAnswer) && watchedCorrectAnswer === -1)) && (_jsxs("div", { className: "flex items-
|
|
599
|
-
|
|
600
|
-
|
|
604
|
+
|| (!Array.isArray(watchedCorrectAnswer) && watchedCorrectAnswer === -1)) && (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-amber-200 bg-amber-50 p-2 text-xs text-amber-800", children: [_jsx("span", { children: "\u26A0\uFE0F" }), _jsxs("p", { children: [_jsx("span", { className: "font-semibold", children: "Ch\u01B0a ch\u1ECDn \u0111\u00E1p \u00E1n \u0111\u00FAng." }), ' ', watchedMultipleAnswers
|
|
605
|
+
? 'Click chữ cái hoặc ✓ để chọn một hoặc nhiều đáp án.'
|
|
606
|
+
: 'Click chữ cái hoặc ✓ để chọn đáp án đúng.'] })] })), form.hasError('correctAnswer') && (_jsx("div", { className: "rounded-md border border-red-200 bg-red-50 p-2", children: _jsx("p", { className: "text-xs text-red-600", children: form.getFieldError('correctAnswer') }) })), _jsx(CompactExplanationToggle, { value: watchedExplanation ?? '', onValueChange: (value) => form.setValue('explanation', value, { shouldValidate: true }), defaultVisible: Boolean(apiData?.explanation) })] })] }))] }));
|
|
601
607
|
}
|
|
602
608
|
export function ChooseTheCorrectAnswerCreator(props) {
|
|
603
609
|
return _jsx(ChooseTheCorrectAnswerCreatorContent, { ...props });
|
|
@@ -5,7 +5,7 @@ import { Button } from '../../../../components/ui/button';
|
|
|
5
5
|
import { FileUpload } from '../../../../components/ui/file-upload';
|
|
6
6
|
import { ImagePreview } from '../../../../components/ui/image-preview';
|
|
7
7
|
import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
|
|
8
|
-
import { Trash2 } from 'lucide-react';
|
|
8
|
+
import { ImageIcon, Trash2 } from 'lucide-react';
|
|
9
9
|
function resolveDisplayUrl(imageUrl, previewOverride, fetchedPreviewUrl) {
|
|
10
10
|
if (previewOverride) {
|
|
11
11
|
return previewOverride;
|
|
@@ -22,7 +22,7 @@ export function ChooseAnswerGroupImageUploadItem({ index, itemIndex, imageUrl, o
|
|
|
22
22
|
const [previewOverride, setPreviewOverride] = useState('');
|
|
23
23
|
const { previewUrl: fetchedPreviewUrl } = usePresignedFileUrl(imageUrl);
|
|
24
24
|
const displayUrl = resolveDisplayUrl(imageUrl, previewOverride, fetchedPreviewUrl);
|
|
25
|
-
return (_jsxs("div", { className: "
|
|
25
|
+
return (_jsxs("div", { className: "flex items-center gap-2 rounded-md border border-gray-200 bg-white p-1.5", children: [_jsx("div", { className: "flex h-12 w-12 shrink-0 items-center justify-center overflow-hidden rounded-md border border-gray-100 bg-gray-50", children: displayUrl ? (_jsx(ImagePreview, { src: displayUrl, alt: `Hình ảnh ${index + 1}`, className: "h-12 w-12 rounded-md object-contain" })) : (_jsx(ImageIcon, { className: "h-4 w-4 text-gray-300" })) }), _jsx("div", { className: "min-w-0 flex-1", children: _jsx(FileUpload, { id: `choose-answer-group-image-${itemIndex}-${index}`, label: "", accept: "image/*", value: imageUrl, onChange: onChange, onPresignedUrlChange: setPreviewOverride, maxSize: 5, placeholder: "Upload \u1EA3nh ho\u1EB7c paste URL", autoUpload: true, prefix: "questions" }) }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", onClick: onRemove, title: "Xo\u00E1 \u1EA3nh n\u00E0y", className: "h-7 w-7 shrink-0 text-gray-400 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }));
|
|
26
26
|
}
|
|
27
27
|
export function ChooseAnswerGroupOptionImageField({ itemIndex, optionIndex, imageUrl, onChange, }) {
|
|
28
28
|
const [previewOverride, setPreviewOverride] = useState('');
|
|
@@ -37,7 +37,7 @@ export function ChooseTheCorrectAnswerGroupClient({ questionData, isReviewMode =
|
|
|
37
37
|
return (_jsxs("div", { className: "space-y-4", children: [questionData.instruction && (_jsx("div", { className: "rounded-xl border border-indigo-100 bg-indigo-50 px-4 py-3 text-sm font-medium text-indigo-800", children: questionData.instruction })), questionData.audioUrl && _jsx(ClientAudio, { url: questionData.audioUrl }), toChooseAnswerGroupImageUrls(questionData.imageUrl).length > 0 && (_jsx("div", { className: "flex flex-wrap justify-center gap-3", children: toChooseAnswerGroupImageUrls(questionData.imageUrl).map((url, imageIndex) => (_jsx(ClientImageItem, { url: url, alt: `Hình ảnh nhóm (${imageIndex + 1})` }, `group-image-${imageIndex}`))) })), questionData.passage && (_jsxs("div", { className: "rounded-xl border border-gray-200 bg-white p-5 shadow-xs", children: [_jsxs("div", { className: "mb-2.5 flex items-center gap-2 text-xs font-bold uppercase tracking-wider text-indigo-700", children: [_jsx(BookOpen, { className: "h-4 w-4" }), _jsx("span", { children: "B\u00E0i \u0111\u1ECDc (Reading Passage)" })] }), _jsx("div", { className: "prose prose-sm max-w-none text-gray-800 leading-relaxed", dangerouslySetInnerHTML: { __html: questionData.passage } })] })), items.map((item, itemIndex) => {
|
|
38
38
|
const selectedId = userAnswers[itemIndex];
|
|
39
39
|
const canSelect = !isReviewMode && !item.isExample && Boolean(onAnswerChange);
|
|
40
|
-
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-
|
|
40
|
+
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-blue-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: "space-y-3 p-3", children: [_jsx("p", { className: "text-sm font-semibold leading-relaxed text-gray-800", children: item.question || 'Câu hỏi chưa được nhập' }), item.audioUrl && _jsx(ClientAudio, { url: item.audioUrl }), toChooseAnswerGroupImageUrls(item.imageUrl).length > 0 && (_jsx("div", { className: "flex flex-wrap justify-center gap-3", children: toChooseAnswerGroupImageUrls(item.imageUrl).map((url, imageIndex) => (_jsx(ClientImageItem, { url: url, alt: `Hình ảnh câu ${item.questionNumber || itemIndex + 1} (${imageIndex + 1})` }, `${item.questionNumber}-${imageIndex}`))) })), item.optionType === 'image' ? (_jsx("div", { className: "grid gap-3 sm:grid-cols-2", children: item.options.map((option, optionIndex) => {
|
|
41
41
|
const isCorrect = option.id === item.correctAnswer;
|
|
42
42
|
const isSelected = option.id === selectedId;
|
|
43
43
|
const optionImageSrc = getChooseAnswerGroupOptionImageSrc(option);
|