@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
|
@@ -38,7 +38,7 @@ export function ReadPassageAndAnswerQuestionsClient({ questionData, userAnswer,
|
|
|
38
38
|
const isCorrect = isReviewMode && selectedAnswer === singleQuestion.correctAnswer;
|
|
39
39
|
const isWrong = isReviewMode && selectedAnswer >= 0 && selectedAnswer !== singleQuestion.correctAnswer;
|
|
40
40
|
const hasAnswered = selectedAnswer >= 0;
|
|
41
|
-
return (_jsxs("div", { className: "space-y-
|
|
41
|
+
return (_jsxs("div", { className: "space-y-3", children: [instructions && (_jsx("div", { className: "overflow-hidden rounded-xl border border-blue-200 bg-gradient-to-r from-blue-50 to-indigo-50 shadow-sm", children: _jsxs("div", { className: "flex items-center gap-3 px-3 py-2", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br from-blue-500 to-indigo-600 text-white shadow-sm", children: _jsx(FileText, { className: "h-4 w-4" }) }), _jsx("p", { className: "text-sm font-medium text-blue-900", children: instructions })] }) })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsxs("div", { className: "flex items-center gap-3 border-b border-gray-100 bg-gradient-to-r from-gray-50 to-white px-3 py-2", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-emerald-500 to-teal-600 text-white shadow-sm", children: _jsx(BookOpen, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "\u0110o\u1EA1n v\u0103n \u0111\u1ECDc hi\u1EC3u" })] }), _jsxs("div", { className: "p-3 space-y-3", children: [passageTitle && (_jsx("h3", { className: "text-center text-lg font-bold text-gray-900 border-b border-gray-100 pb-3", children: passageTitle })), _jsx("div", { className: "rounded-lg bg-gradient-to-br from-slate-50 to-gray-50 p-5 border border-gray-100", children: _jsx("p", { className: "whitespace-pre-wrap text-base leading-relaxed text-gray-700", children: passage }) })] })] }), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm transition-all hover:shadow-md", children: [_jsxs("div", { className: cn('flex items-center justify-between border-b px-3 py-2', isReviewMode && isCorrect && 'border-green-200 bg-gradient-to-r from-green-50 to-emerald-50', isReviewMode && isWrong && 'border-red-200 bg-gradient-to-r from-red-50 to-rose-50', !isReviewMode && '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 && isCorrect && 'bg-gradient-to-br from-green-500 to-emerald-600', isReviewMode && isWrong && 'bg-gradient-to-br from-red-500 to-rose-600', !isReviewMode && 'bg-gradient-to-br from-indigo-500 to-purple-600'), children: _jsx(ClipboardList, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-800", children: singleQuestion.question })] }), isReviewMode && hasAnswered && (_jsxs("div", { className: cn('flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold', isCorrect ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'), children: [isCorrect ? _jsx(Check, { className: "h-3.5 w-3.5" }) : _jsx(X, { className: "h-3.5 w-3.5" }), isCorrect ? 'Đúng' : 'Sai'] }))] }), _jsxs("div", { className: "p-3 space-y-3", children: [singleQuestion.options.map((option, optIndex) => {
|
|
42
42
|
const isSelected = selectedAnswer === optIndex;
|
|
43
43
|
const isCorrectOption = optIndex === singleQuestion.correctAnswer;
|
|
44
44
|
const letter = String.fromCharCode(65 + optIndex);
|
|
@@ -5,10 +5,10 @@ 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
|
-
import {
|
|
10
|
+
import { Plus, Trash2, BookOpen, Lightbulb, MessageSquare, CheckCircle2 } from 'lucide-react';
|
|
11
|
+
import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
|
|
12
12
|
import { ReadPassageAndAnswerQuestionsClient } from './ReadPassageAndAnswerQuestionsClient';
|
|
13
13
|
import { useDebouncedCallback } from '../../../../shared/lib/hooks';
|
|
14
14
|
import { useExamQuestionStore } from '../../../../shared/lib/stores/examQuestionStore';
|
|
@@ -486,16 +486,19 @@ function ReadPassageAndAnswerQuestionsContent({ initialData, onSave, onSaveToQue
|
|
|
486
486
|
singleQuestion: currentQuestion,
|
|
487
487
|
groups,
|
|
488
488
|
};
|
|
489
|
-
return (_jsxs("div", { className: "space-y-
|
|
489
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(ReadPassageAndAnswerQuestionsClient, { questionData: questionData, autoFillCorrectAnswers: true, isReviewMode: true, onSubmit: () => { }, explanation: explanation })] }));
|
|
490
490
|
}
|
|
491
|
-
return (_jsxs("div", { className: "space-y-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
491
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(BookOpen, { className: "h-3.5 w-3.5" }), title: "\u0110\u1ECDc \u0111o\u1EA1n v\u0103n v\u00E0 tr\u1EA3 l\u1EDDi c\u00E2u h\u1ECFi", points: {
|
|
492
|
+
value: currentQuestion.points || 1,
|
|
493
|
+
onValueChange: (value) => updateCurrentQuestion('points', value),
|
|
494
|
+
}, extraActions: _jsxs("span", { className: "inline-flex items-center rounded-full bg-purple-100 px-2 py-0.5 text-xs font-medium text-purple-700", children: ["Nh\u00F3m ", currentGroupNumber] }), preview: {
|
|
495
|
+
onClick: handlePreview,
|
|
496
|
+
} }), children: [_jsxs("div", { className: `rounded-xl border-2 p-5 transition-all ${validationErrors.passage ? 'border-red-300 bg-gradient-to-r from-red-50 to-rose-50' : 'border-amber-200 bg-gradient-to-r from-amber-50 to-yellow-50'}`, children: [_jsxs("div", { className: "mb-4 flex items-center gap-3", children: [_jsx("div", { className: `flex h-8 w-8 items-center justify-center rounded-lg ${validationErrors.passage ? 'bg-red-100' : 'bg-amber-100'}`, children: _jsx(BookOpen, { className: `h-4 w-4 ${validationErrors.passage ? 'text-red-600' : 'text-amber-600'}` }) }), _jsxs(Label, { className: `text-base font-semibold ${validationErrors.passage ? 'text-red-800' : 'text-amber-800'}`, children: ["\u0110o\u1EA1n v\u0103n \u0111\u1ECDc hi\u1EC3u ", _jsxs("span", { className: "font-normal text-amber-600", children: ["(d\u00F9ng chung cho nh\u00F3m ", currentGroupNumber, ")"] })] })] }), _jsxs("div", { className: "mb-4", children: [_jsx(Label, { htmlFor: "passageTitle", className: "text-sm font-medium text-gray-700", children: "Ti\u00EAu \u0111\u1EC1" }), _jsx(Input, { id: "passageTitle", value: passageTitle, onChange: (e) => setPassageTitle(e.target.value), placeholder: "V\u00ED d\u1EE5: Our Family Holiday in Nha Trang", className: "mt-1.5 border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200" })] }), _jsxs("div", { className: "mb-4", children: [_jsxs(Label, { htmlFor: "passage", className: "text-sm font-medium text-gray-700", children: ["N\u1ED9i dung \u0111o\u1EA1n v\u0103n ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Textarea, { id: "passage", value: passage, onChange: (e) => {
|
|
497
|
+
setPassage(e.target.value);
|
|
498
|
+
if (validationErrors.passage) {
|
|
499
|
+
setValidationErrors(prev => ({ ...prev, passage: undefined }));
|
|
500
|
+
}
|
|
501
|
+
}, placeholder: "Nh\u1EADp n\u1ED9i dung \u0111o\u1EA1n v\u0103n \u0111\u1ECDc hi\u1EC3u...", className: `mt-1.5 min-h-[150px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 ${validationErrors.passage ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}`, rows: 6 }), validationErrors.passage && (_jsx("p", { className: "mt-1.5 text-sm text-red-600", children: validationErrors.passage }))] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "instructions", className: "text-sm font-medium text-gray-700", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Input, { id: "instructions", value: instructions, onChange: (e) => setInstructions(e.target.value), placeholder: "V\u00ED d\u1EE5: Read the short passage and answer the question below.", className: "mt-1.5 border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200" })] })] }), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "flex items-start gap-3 rounded-xl border border-red-200 bg-gradient-to-r from-red-50 to-rose-50 p-4", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-red-100", children: _jsx("span", { className: "text-lg", children: "\u274C" }) }), _jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "font-semibold text-red-800", children: "L\u1ED7i:" }), _jsx("ul", { className: "mt-1 list-inside list-disc text-red-700", children: externalErrors.map((error, index) => (_jsx("li", { children: error }, index))) })] })] }))] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: [_jsx(CardHeader, { className: "px-3 py-2", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-8 w-8 items-center justify-center rounded-lg bg-indigo-100", children: _jsx(MessageSquare, { className: "h-4 w-4 text-indigo-600" }) }), _jsx(CardTitle, { className: "text-lg font-semibold text-gray-800", children: "N\u1ED9i dung c\u00E2u h\u1ECFi" }), _jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-purple-100 px-2.5 py-1 text-xs font-medium text-purple-700", children: ["\uD83D\uDCDA Nh\u00F3m ", currentGroupNumber] })] }) }), _jsxs(CardContent, { className: "space-y-2.5 px-3 pb-3", children: [_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { htmlFor: "question", className: "flex items-center gap-2 text-base font-semibold text-gray-800", children: [_jsx(MessageSquare, { className: "h-4 w-4 text-indigo-500" }), "C\u00E2u h\u1ECFi ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "question", value: currentQuestion.question || '', onChange: (e) => updateCurrentQuestion('question', e.target.value), placeholder: "Nh\u1EADp c\u00E2u h\u1ECFi...", className: `border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${validationErrors.question ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), validationErrors.question && (_jsx("p", { className: "text-sm text-red-600", children: validationErrors.question }))] }), _jsxs("div", { className: "space-y-4", 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-indigo-100", children: _jsx(Lightbulb, { className: "h-3.5 w-3.5 text-indigo-600" }) }), _jsxs(Label, { className: "text-base font-semibold text-gray-800", children: ["C\u00E1c l\u1EF1a ch\u1ECDn ", _jsxs("span", { className: "text-sm font-normal text-gray-500", children: ["(", currentQuestion.options.length, " \u0111\u00E1p \u00E1n)"] })] })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addOption, disabled: currentQuestion.options.length >= 10, 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"] })] }), _jsx(RadioGroup, { value: currentQuestion.correctAnswer >= 0 ? currentQuestion.correctAnswer.toString() : '', onValueChange: (value) => updateCurrentQuestion('correctAnswer', parseInt(value)), children: _jsx("div", { className: "space-y-3", children: currentQuestion.options.map((option, optIndex) => {
|
|
499
502
|
const optionError = validationErrors.options?.[optIndex];
|
|
500
503
|
const isCorrect = currentQuestion.correctAnswer === optIndex;
|
|
501
504
|
const optionLetter = String.fromCharCode(65 + optIndex);
|
|
@@ -504,7 +507,7 @@ function ReadPassageAndAnswerQuestionsContent({ initialData, onSave, onSaveToQue
|
|
|
504
507
|
: '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
|
|
505
508
|
? 'bg-gradient-to-br from-green-500 to-emerald-600 text-white shadow-md'
|
|
506
509
|
: 'bg-gray-100 text-gray-600 hover:bg-indigo-100 hover:text-indigo-600'}`, onClick: () => updateCurrentQuestion('correctAnswer', optIndex), children: optionLetter }), _jsxs("div", { className: "flex-1 space-y-2", children: [_jsx(Input, { id: `option-input-${optIndex}`, value: option, onChange: (e) => updateOption(optIndex, e.target.value), placeholder: `Nhập đáp án ${optionLetter}...`, className: `border-gray-200 bg-white transition-all focus:border-indigo-300 focus:ring-indigo-200 ${optionError ? 'border-red-400 focus:border-red-500 focus:ring-red-200' : ''}` }), optionError && (_jsx("p", { className: "text-sm text-red-600", children: optionError })), 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: () => updateCurrentQuestion('correctAnswer', optIndex), 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" }) })), currentQuestion.options.length > 2 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => deleteOption(optIndex), className: "text-red-500 hover:bg-red-50 hover:text-red-600", children: _jsx(Trash2, { className: "h-4 w-4" }) }))] })] }) }, optIndex));
|
|
507
|
-
}) }) }), currentQuestion.correctAnswer < 0 && (_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" })] })] })), validationErrors.correctAnswer && (_jsx("div", { className: "rounded-xl border border-red-200 bg-red-50 p-4", children: _jsx("p", { className: "text-sm text-red-600", children: validationErrors.correctAnswer }) }))] })] })] }),
|
|
510
|
+
}) }) }), currentQuestion.correctAnswer < 0 && (_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" })] })] })), validationErrors.correctAnswer && (_jsx("div", { className: "rounded-xl border border-red-200 bg-red-50 p-4", children: _jsx("p", { className: "text-sm text-red-600", children: validationErrors.correctAnswer }) }))] })] })] }), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation })] }));
|
|
508
511
|
}
|
|
509
512
|
export function ReadPassageAndAnswerQuestionsCreator(props) {
|
|
510
513
|
return _jsx(ReadPassageAndAnswerQuestionsContent, { ...props });
|
package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesClient.js
CHANGED
|
@@ -20,5 +20,5 @@ export function SortWordsIntoCategoriesClient({ questionData, isReviewMode = fal
|
|
|
20
20
|
return item;
|
|
21
21
|
});
|
|
22
22
|
const canEdit = Boolean(onPlacementsChange) && !isReviewMode;
|
|
23
|
-
return (_jsxs("div", { className: "space-y-4", children: [(questionData.title || questionData.instruction) && (_jsxs("div", { className: "rounded-xl border border-slate-200 bg-slate-50 px-
|
|
23
|
+
return (_jsxs("div", { className: "space-y-4", children: [(questionData.title || questionData.instruction) && (_jsxs("div", { className: "rounded-xl border border-slate-200 bg-slate-50 px-3 py-2", children: [questionData.title && (_jsx("p", { className: "text-xs font-semibold uppercase tracking-wide text-slate-500", children: questionData.title })), questionData.instruction && (_jsx("p", { className: "text-sm font-medium text-slate-800", children: questionData.instruction }))] })), _jsx(SortWordsIntoCategoriesBoard, { categories: questionData.categories, words: questionData.words, placements: placements, correctPlacements: isReviewMode ? correctPlacements : undefined, onChange: canEdit ? onPlacementsChange : undefined, disabled: !canEdit, isReviewMode: isReviewMode }), isReviewMode && questionData.explanation && (_jsxs("div", { className: "flex items-start gap-2 rounded-xl border border-amber-100 bg-amber-50 px-4 py-3 text-sm text-amber-800", children: [_jsx(Lightbulb, { className: "mt-0.5 h-4 w-4 flex-shrink-0" }), questionData.explanation] }))] }));
|
|
24
24
|
}
|
package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesCreator.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 { PointsInput } from '../../../../components/ui/points-input';
|
|
8
|
-
import { Textarea } from '../../../../components/ui/textarea';
|
|
9
8
|
import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
|
|
10
9
|
import { Switch } from '../../../../components/ui/switch';
|
|
11
10
|
import { useDebouncedCallback } from '../../../../shared/lib/hooks';
|
|
12
|
-
import {
|
|
11
|
+
import { Plus, Star, Trash2, Layers } from 'lucide-react';
|
|
12
|
+
import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
|
|
13
13
|
import { SortWordsIntoCategoriesClient } from './SortWordsIntoCategoriesClient';
|
|
14
14
|
import { createEmptyCategory, createEmptyWord, sumGradablePoints, } from './map-sort-words-into-categories-data';
|
|
15
15
|
import { SORT_WORDS_INTO_CATEGORIES_DEFAULT_INSTRUCTION } from '../../_shared/types/sort-words-into-categories.type';
|
|
@@ -165,7 +165,7 @@ export function SortWordsIntoCategoriesCreator({ initialData, onChange, external
|
|
|
165
165
|
setWords((prev) => prev.filter((_, i) => i !== index));
|
|
166
166
|
};
|
|
167
167
|
const displayErrors = [...errors, ...(externalErrors || [])];
|
|
168
|
-
return (_jsxs("div", { className: "space-y-
|
|
169
|
-
|
|
170
|
-
|
|
168
|
+
return (_jsxs("div", { className: "space-y-3", children: [isClientMode ? (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(SortWordsIntoCategoriesClient, { questionData: buildPayload(), isReviewMode: true })] })) : (_jsxs(_Fragment, { children: [_jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Layers, { className: "h-3.5 w-3.5" }), title: "S\u1EAFp x\u1EBFp t\u1EEB v\u00E0o nh\u00F3m", preview: { onClick: () => setIsClientMode(true) } }), children: [_jsxs("div", { className: "grid gap-2.5 md:grid-cols-2", children: [_jsxs("div", { children: [_jsx(Label, { htmlFor: "swc-title", children: "Ti\u00EAu \u0111\u1EC1 (optional)" }), _jsx(Input, { id: "swc-title", value: title, onChange: (event) => setTitle(event.target.value), placeholder: "VOCABULARY" })] }), _jsxs("div", { children: [_jsx(Label, { htmlFor: "swc-instruction", children: "H\u01B0\u1EDBng d\u1EABn" }), _jsx(Input, { id: "swc-instruction", value: instruction, onChange: (event) => setInstruction(event.target.value), placeholder: SORT_WORDS_INTO_CATEGORIES_DEFAULT_INSTRUCTION })] })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 px-3 py-2", children: [_jsx(CardTitle, { className: "text-sm font-semibold text-gray-800", children: "Nh\u00F3m (categories)" }), _jsxs(Button, { type: "button", size: "sm", variant: "outline", onClick: addCategory, className: "h-8", children: [_jsx(Plus, { className: "mr-1 h-3.5 w-3.5" }), " Th\u00EAm nh\u00F3m"] })] }), _jsx(CardContent, { className: "space-y-2.5 px-3 pb-3", children: categories.map((category, index) => (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Input, { value: category.name, onChange: (event) => updateCategory(index, { name: event.target.value }), placeholder: `Nhóm ${index + 1}` }), categories.length > 2 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeCategory(index), children: _jsx(Trash2, { className: "h-4 w-4 text-red-500" }) }))] }, category.id))) })] }), _jsxs(Card, { className: "overflow-hidden border-0 bg-white shadow-sm", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between space-y-0 px-3 py-2", children: [_jsx(CardTitle, { className: "text-sm font-semibold text-gray-800", children: "T\u1EEB / c\u1EE5m t\u1EEB" }), _jsxs(Button, { type: "button", size: "sm", variant: "outline", onClick: addWord, className: "h-8", children: [_jsx(Plus, { className: "mr-1 h-3.5 w-3.5" }), " Th\u00EAm t\u1EEB"] })] }), _jsx(CardContent, { className: "space-y-2.5 px-3 pb-3", children: words.map((word, index) => (_jsxs("div", { className: "grid gap-2 rounded-lg border p-3 md:grid-cols-[1fr_180px_auto]", children: [_jsx(Input, { value: word.text, onChange: (event) => updateWord(index, { text: event.target.value }), placeholder: `Từ #${index + 1} (VD: press a button)` }), _jsx("select", { className: "h-10 rounded-md border border-input bg-background px-3 text-sm", value: word.categoryId, onChange: (event) => updateWord(index, { categoryId: event.target.value }), children: categories.map((category) => (_jsx("option", { value: category.id, children: category.name || category.id }, category.id))) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs("div", { className: "flex items-center gap-1", children: [_jsx(Switch, { id: `swc-example-${index}`, checked: word.isExample === true, onCheckedChange: (checked) => updateWord(index, { isExample: checked }) }), _jsxs(Label, { htmlFor: `swc-example-${index}`, className: "flex cursor-pointer items-center gap-1 text-xs", children: [_jsx(Star, { className: "h-3 w-3 text-amber-500" }), "Example"] })] }), !word.isExample && (_jsx(PointsInput, { value: word.points, allowZero: true, onChange: (event) => updateWord(index, {
|
|
169
|
+
points: Number.parseInt(event.target.value, 10) || 1,
|
|
170
|
+
}) })), words.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => removeWord(index), children: _jsx(Trash2, { className: "h-4 w-4 text-red-500" }) }))] })] }, word.id))) })] })] }), _jsx(CompactExplanationToggle, { value: explanation, onValueChange: setExplanation, defaultVisible: Boolean(initialData?.explanation) })] })), displayErrors.length > 0 && (_jsx("ul", { className: "list-disc space-y-1 rounded-md border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-700", children: displayErrors.map((error) => (_jsx("li", { children: error }, error))) }))] }));
|
|
171
171
|
}
|
package/dist/components/questions/types/speaking-conversation/SpeakingConversationCreator.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
-
import { useRef, useEffect, useMemo } from 'react';
|
|
3
|
+
import { useRef, useEffect, useMemo, useState } from 'react';
|
|
4
4
|
import { Button } from '../../../../components/ui/button';
|
|
5
5
|
import { Label } from '../../../../components/ui/label';
|
|
6
6
|
import { Input } from '../../../../components/ui/input';
|
|
7
7
|
import { Textarea } from '../../../../components/ui/textarea';
|
|
8
8
|
import { Switch } from '../../../../components/ui/switch';
|
|
9
9
|
import { PointsInput } from '../../../../components/ui/points-input';
|
|
10
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
|
|
11
10
|
import { Badge } from '../../../../components/ui/badge';
|
|
12
11
|
import { FileUpload } from '../../../../components/ui/file-upload';
|
|
13
|
-
import {
|
|
12
|
+
import { CompactCreatorHeader, CompactExplanationToggle, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
|
|
13
|
+
import { SpeakingConversationPreviewClient } from './SpeakingConversationPreviewClient';
|
|
14
14
|
import { useReactHookForm } from '../../../../shared/lib/hooks/useReactHookForm';
|
|
15
15
|
import { useDebouncedCallback } from '../../../../shared/lib/hooks/useDebouncedCallback';
|
|
16
16
|
import { speakingConversationFormSchema, } from '../../../../shared/lib/validation/speaking-conversation.validation';
|
|
17
|
-
import { Users, Mic, Volume2, MessageSquareText,
|
|
17
|
+
import { Users, Mic, Volume2, MessageSquareText, Info, Plus, Trash2, Image, User, UserCheck, } from 'lucide-react';
|
|
18
18
|
import { useExamQuestionStore } from '../../../../shared/lib/stores/examQuestionStore';
|
|
19
19
|
import { generateObjectId } from '../../_shared/utils/id-generator';
|
|
20
20
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
@@ -103,6 +103,7 @@ export function SpeakingConversationCreator({ initialData, onSave, onChange, ext
|
|
|
103
103
|
zodSchema: speakingConversationFormSchema,
|
|
104
104
|
mode: 'onBlur',
|
|
105
105
|
});
|
|
106
|
+
const [isClientMode, setIsClientMode] = useState(false);
|
|
106
107
|
const previousInitialDataRef = useRef(initialData);
|
|
107
108
|
const isInitializingRef = useRef(false);
|
|
108
109
|
const onChangeRef = useRef(onChange);
|
|
@@ -335,39 +336,34 @@ export function SpeakingConversationCreator({ initialData, onSave, onChange, ext
|
|
|
335
336
|
form.setValue('images', updated, { shouldValidate: true });
|
|
336
337
|
};
|
|
337
338
|
// ─── Render ───────────────────────────────────────────────────────────────
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
: 'Đáp án tham chiếu để giáo viên chấm thủ công' })] })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: handleAddExpectedAnswer, className: "gap-1.5 border-green-200 text-green-700 hover:bg-green-50", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm \u0111\u00E1p \u00E1n"] })] }) }), _jsxs(CardContent, { className: "space-y-3", children: [_jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-blue-100 bg-blue-50/60 px-3 py-2.5", children: [_jsx(Info, { className: "h-4 w-4 text-blue-500 mt-0.5 flex-shrink-0" }), _jsx("p", { className: "text-xs text-blue-700", children: isPartnerResponder
|
|
369
|
-
? 'Câu hỏi mẫu: có thể nhập nội dung Partner nói để ghi nhớ. Không dùng để chấm điểm.'
|
|
370
|
-
: 'Giáo viên dùng các đáp án này làm tham chiếu khi chấm thủ công câu trả lời của học sinh.' })] }), watchedExpectedAnswers.map((answer, index) => (_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-full bg-green-100 text-xs font-semibold text-green-700 mt-1.5 flex-shrink-0", children: index + 1 }), _jsx(Textarea, { value: answer || '', onChange: (e) => handleExpectedAnswerChange(index, e.target.value), placeholder: index === 0
|
|
371
|
-
? 'VD: My name is Bingo. I am 8 years old.'
|
|
372
|
-
: 'Thêm đáp án mẫu khác...', rows: 2, className: "min-h-[60px] border-gray-200 bg-white transition-all focus:border-green-300 focus:ring-green-200 resize-none flex-1" }), watchedExpectedAnswers.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "mt-1.5 h-7 w-7 text-red-400 hover:text-red-600 hover:bg-red-50 flex-shrink-0", onClick: () => handleRemoveExpectedAnswer(index), "aria-label": `Xóa đáp án ${index + 1}`, children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }, index)))] })] }), _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 th\u00EAm v\u1EC1 c\u00E2u tr\u1EA3 l\u1EDDi k\u1EF3 v\u1ECDng, d\u00F9ng khi review k\u1EBFt qu\u1EA3." })] })] }) }), _jsx(CardContent, { children: _jsx(Textarea, { value: watchedExplanation || '', onChange: (e) => form.setValue('explanation', e.target.value, { shouldValidate: true }), placeholder: "VD: C\u00E2u tr\u1EA3 l\u1EDDi \u0111\u00FAng: 'My name is...' v\u00EC c\u00E2u h\u1ECFi \u0111ang h\u1ECFi v\u1EC1 t\u00EAn v\u00E0 tu\u1ED5i.", rows: 3, className: "min-h-[80px] border-gray-200 bg-white transition-all focus:border-amber-300 focus:ring-amber-200 resize-none" }) })] }), externalErrors && externalErrors.length > 0 && (_jsx("div", { className: "flex items-start gap-3 rounded-xl border border-red-200 bg-red-50 p-4", children: _jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "font-semibold text-red-800", children: "C\u1EA7n s\u1EEDa c\u00E1c l\u1ED7i sau:" }), _jsx("ul", { className: "mt-1 list-inside list-disc text-red-700", children: externalErrors.map((error, index) => (_jsx("li", { children: error }, index))) })] }) }))] }));
|
|
339
|
+
if (isClientMode) {
|
|
340
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(SpeakingConversationPreviewClient, { questionData: {
|
|
341
|
+
questionText: watchedQuestionText,
|
|
342
|
+
responder: watchedResponder,
|
|
343
|
+
interlocutorAudioUrl: watchedInterlocutorAudioUrl,
|
|
344
|
+
partnerAudioUrl: watchedPartnerAudioUrl,
|
|
345
|
+
partnerName: watchedPartnerName,
|
|
346
|
+
partnerAvatarUrl: watchedPartnerAvatarUrl,
|
|
347
|
+
images: watchedImages,
|
|
348
|
+
isBackup: watchedIsBackup,
|
|
349
|
+
expectedAnswers: watchedExpectedAnswers,
|
|
350
|
+
}, isReviewMode: true })] }));
|
|
351
|
+
}
|
|
352
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsx(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(Users, { className: "h-3.5 w-3.5" }), title: "D\u1EEF li\u1EC7u nh\u00F3m", extraActions: _jsxs(_Fragment, { children: [isGrouped && (_jsxs(Badge, { variant: "outline", className: "border-teal-300 bg-teal-50 text-teal-700 text-xs", children: ["Nh\u00F3m ", currentGroupNumber] })), !isFirstInGroup && (_jsx(Badge, { variant: "outline", className: "border-gray-300 bg-gray-50 text-gray-500 text-xs", children: "Ch\u1EC9 \u0111\u1ECDc" })), _jsx(PointsInput, { value: watchedPoints, onValueChange: (value) => form.setValue('points', value, { shouldValidate: true }), disabled: isPartnerResponder, error: form.hasError('points') ? form.getFieldError('points') : undefined })] }), preview: { onClick: () => setIsClientMode(true) } }), children: isFirstInGroup ? (_jsxs(_Fragment, { children: [_jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-700", children: [_jsx(User, { className: "h-4 w-4 text-teal-500" }), "T\u00EAn nh\u00E2n v\u1EADt Partner"] }), _jsx(Input, { ...form.register('partnerName'), placeholder: "VD: Bingo", className: "border-gray-200 focus:border-teal-300 focus:ring-teal-200" }), _jsx("p", { className: "text-xs text-gray-400", children: "T\u00EAn hi\u1EC3n th\u1ECB c\u1EE7a nh\u00E2n v\u1EADt \u1EA3o (Partner) \u2014 d\u00F9ng trong giao di\u1EC7n thi." })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-700", children: [_jsx(User, { className: "h-4 w-4 text-teal-500" }), "Avatar Partner"] }), _jsx(FileUpload, { id: "partner-avatar-url", label: "", accept: "image/*", value: watchedPartnerAvatarUrl || '', onChange: (url) => form.setValue('partnerAvatarUrl', url, { shouldValidate: true }), maxSize: 5, placeholder: "Upload \u1EA3nh avatar Partner (PNG, JPG, ...)", autoUpload: true, prefix: "speaking/partner-avatar" })] }), _jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-700", children: [_jsx(Image, { className: "h-4 w-4 text-teal-500" }), "H\u00ECnh \u1EA3nh phase (t\u00F9y ch\u1ECDn)"] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: handleAddImage, className: "gap-1.5 border-teal-200 text-teal-700 hover:bg-teal-50 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5" }), "Th\u00EAm h\u00ECnh"] })] }), watchedImages.length === 0 ? (_jsxs("div", { className: "rounded-lg border-2 border-dashed border-teal-200 bg-teal-50/40 py-6 text-center", children: [_jsx(Image, { className: "mx-auto h-8 w-8 text-teal-300 mb-2" }), _jsx("p", { className: "text-sm text-gray-400", children: "Kh\u00F4ng c\u00F3 h\u00ECnh \u1EA3nh \u2014 d\u00F9ng cho Part 2 phases c\u00F3 tranh" })] })) : (_jsx("div", { className: "space-y-3", children: watchedImages.map((img, index) => (_jsxs("div", { className: "rounded-lg border border-gray-200 bg-gray-50 p-3 space-y-2", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("span", { className: "text-xs font-semibold text-gray-600", children: ["H\u00ECnh ", index + 1] }), _jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "h-6 w-6 text-red-400 hover:text-red-600 hover:bg-red-50", onClick: () => handleRemoveImage(index), children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }), _jsx(FileUpload, { id: `image-url-${index}`, label: "", accept: "image/*", value: img.url, onChange: (url) => handleImageChange(index, 'url', url), maxSize: 10, placeholder: "Upload h\u00ECnh \u1EA3nh", autoUpload: true, prefix: "speaking/phase-images" }), _jsx(Input, { value: img.label, onChange: (e) => handleImageChange(index, 'label', e.target.value), placeholder: "Nh\u00E3n h\u00ECnh (VD: Park, Bedroom, Library ...)", className: "border-gray-200 text-sm focus:border-teal-300" })] }, img.id))) }))] })] })) : (
|
|
353
|
+
/* Read-only group data view for non-first questions */
|
|
354
|
+
_jsxs("div", { className: "space-y-3", children: [_jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-gray-100 bg-gray-50 px-4 py-3", children: [_jsx(User, { className: "h-4 w-4 text-teal-500 flex-shrink-0" }), _jsxs("div", { children: [_jsx("p", { className: "text-xs text-gray-500", children: "T\u00EAn Partner" }), _jsx("p", { className: "text-sm font-medium text-gray-800", children: groupData.partnerName || (_jsx("span", { className: "italic text-gray-400", children: "Ch\u01B0a \u0111\u1EB7t t\u00EAn" })) })] })] }), groupData.images && groupData.images.length > 0 && (_jsxs("div", { className: "rounded-lg border border-gray-100 bg-gray-50 px-4 py-3", children: [_jsxs("p", { className: "text-xs text-gray-500 mb-2", children: ["H\u00ECnh \u1EA3nh (", groupData.images.length, ")"] }), _jsx("div", { className: "flex flex-wrap gap-2", children: groupData.images.map((img) => (_jsx("span", { className: "inline-flex items-center rounded-full bg-teal-100 px-2 py-0.5 text-xs text-teal-700", children: img.label || img.id }, img.id))) })] }))] })) }), _jsxs(CompactFieldCard, { header: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100", children: _jsx(Mic, { className: "h-3.5 w-3.5 text-gray-600" }) }), _jsxs("div", { className: "flex-1", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("p", { className: "text-sm font-semibold text-gray-800", children: "N\u1ED9i dung c\u00E2u h\u1ECFi" }), isPartnerResponder && (_jsx(Badge, { className: "bg-orange-100 text-orange-700 border-orange-200 border text-xs font-semibold", children: "C\u00E2u h\u1ECFi m\u1EABu (Example)" }))] }), _jsx("p", { className: "text-sm text-gray-500 mt-0.5", children: isPartnerResponder
|
|
355
|
+
? 'Partner trả lời — học sinh chỉ nghe, không ghi âm'
|
|
356
|
+
: 'Student trả lời — học sinh ghi âm câu trả lời' })] })] }), children: [_jsxs("div", { className: `rounded-xl border p-4 space-y-4 ${isPartnerResponder ? 'border-orange-200 bg-orange-50/40' : 'border-indigo-200 bg-indigo-50/40'}`, children: [_jsx("div", { className: "flex items-center justify-between", children: _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { className: "text-base font-semibold text-gray-800", children: "Ng\u01B0\u1EDDi tr\u1EA3 l\u1EDDi" }), _jsx("p", { className: "text-sm text-gray-500", children: "Ai s\u1EBD tr\u1EA3 l\u1EDDi c\u00E2u h\u1ECFi n\u00E0y trong b\u00E0i thi?" })] }) }), _jsxs("div", { className: "flex gap-3", children: [_jsxs("button", { type: "button", onClick: () => form.setValue('responder', 'PARTNER', { shouldValidate: true }), className: `flex flex-1 items-center gap-3 rounded-xl border-2 p-3 transition-all ${isPartnerResponder
|
|
357
|
+
? 'border-orange-400 bg-orange-50 shadow-sm'
|
|
358
|
+
: 'border-gray-200 bg-white hover:border-gray-300'}`, children: [_jsx("div", { className: `flex h-9 w-9 items-center justify-center rounded-full ${isPartnerResponder ? 'bg-orange-500' : 'bg-gray-200'}`, children: _jsx(User, { className: `h-4 w-4 ${isPartnerResponder ? 'text-white' : 'text-gray-500'}` }) }), _jsxs("div", { className: "text-left", children: [_jsx("p", { className: `text-sm font-semibold ${isPartnerResponder ? 'text-orange-800' : 'text-gray-700'}`, children: "Partner tr\u1EA3 l\u1EDDi" }), _jsx("p", { className: `text-xs ${isPartnerResponder ? 'text-orange-600' : 'text-gray-500'}`, children: "C\u00E2u m\u1EABu \u2014 is_example=true, points=0" })] })] }), _jsxs("button", { type: "button", onClick: () => form.setValue('responder', 'STUDENT', { shouldValidate: true }), className: `flex flex-1 items-center gap-3 rounded-xl border-2 p-3 transition-all ${!isPartnerResponder
|
|
359
|
+
? 'border-indigo-400 bg-indigo-50 shadow-sm'
|
|
360
|
+
: 'border-gray-200 bg-white hover:border-gray-300'}`, children: [_jsx("div", { className: `flex h-9 w-9 items-center justify-center rounded-full ${!isPartnerResponder ? 'bg-indigo-500' : 'bg-gray-200'}`, children: _jsx(UserCheck, { className: `h-4 w-4 ${!isPartnerResponder ? 'text-white' : 'text-gray-500'}` }) }), _jsxs("div", { className: "text-left", children: [_jsx("p", { className: `text-sm font-semibold ${!isPartnerResponder ? 'text-indigo-800' : 'text-gray-700'}`, children: "Student tr\u1EA3 l\u1EDDi" }), _jsx("p", { className: `text-xs ${!isPartnerResponder ? 'text-indigo-600' : 'text-gray-500'}`, children: "C\u00E2u th\u1EF1c \u2014 h\u1ECDc sinh ghi \u00E2m" })] })] })] }), form.hasError('responder') && (_jsx("p", { className: "text-sm text-red-600", children: form.getFieldError('responder') }))] }), isPartnerResponder && (_jsx("p", { className: "text-xs text-orange-600", children: "C\u00E2u h\u1ECFi m\u1EABu (Partner) lu\u00F4n c\u00F3 0 \u0111i\u1EC3m" })), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-700", children: [_jsx(Volume2, { className: "h-4 w-4 text-blue-500" }), "Audio Interlocutor", _jsx("span", { className: "text-xs font-normal text-gray-400", children: "(t\u00F9y ch\u1ECDn)" })] }), _jsx(FileUpload, { id: "interlocutor-audio-url", label: "", accept: "audio/*", value: watchedInterlocutorAudioUrl || '', onChange: (url) => form.setValue('interlocutorAudioUrl', url, { shouldValidate: true }), maxSize: 20, placeholder: "Upload audio c\u00E2u h\u1ECFi c\u1EE7a Interlocutor (MP3, WAV...)", autoUpload: true, prefix: "speaking/interlocutor" }), _jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-blue-100 bg-blue-50/60 px-3 py-2", children: [_jsx(Info, { className: "h-4 w-4 text-blue-500 mt-0.5 flex-shrink-0" }), _jsx("p", { className: "text-xs text-blue-700", children: "Audio c\u1EE7a Interlocutor (ng\u01B0\u1EDDi h\u1ECFi). Ph\u00E1t tr\u01B0\u1EDBc khi Partner ho\u1EB7c Student tr\u1EA3 l\u1EDDi." })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-700", children: [_jsx(Volume2, { className: "h-4 w-4 text-orange-500" }), "Audio Partner", isPartnerResponder && _jsx("span", { className: "text-red-500", children: "*" }), !isPartnerResponder && (_jsx("span", { className: "text-xs font-normal text-gray-400", children: "(t\u00F9y ch\u1ECDn)" }))] }), _jsx(FileUpload, { id: "partner-audio-url", label: "", accept: "audio/*", value: watchedPartnerAudioUrl || '', onChange: (url) => form.setValue('partnerAudioUrl', url, { shouldValidate: true }), maxSize: 20, placeholder: "Upload audio tr\u1EA3 l\u1EDDi c\u1EE7a Partner (MP3, WAV...)", autoUpload: true, prefix: "speaking/partner" }), form.hasError('partnerAudioUrl') && (_jsx("p", { className: "text-sm text-red-600", children: form.getFieldError('partnerAudioUrl') })), _jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-orange-100 bg-orange-50/60 px-3 py-2", children: [_jsx(Info, { className: "h-4 w-4 text-orange-500 mt-0.5 flex-shrink-0" }), _jsx("p", { className: "text-xs text-orange-700", children: isPartnerResponder
|
|
361
|
+
? 'Bắt buộc khi responder = Partner. Phát sau audio Interlocutor.'
|
|
362
|
+
: 'Tùy chọn khi responder = Student — dùng cho Talk Together (Partner nói trước, Student trả lời sau).' })] })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs(Label, { className: "flex items-center gap-2 text-sm font-semibold text-gray-700", children: [_jsx(MessageSquareText, { className: "h-4 w-4 text-gray-500" }), "Text c\u00E2u h\u1ECFi (t\u00F9y ch\u1ECDn)"] }), _jsx(Textarea, { ...form.register('questionText'), placeholder: "VD: What's your name? How old are you?", rows: 2, className: "min-h-[60px] border-gray-200 focus:border-indigo-300 focus:ring-indigo-200 resize-none" }), _jsx("p", { className: "text-xs text-gray-400", children: "Ch\u1EC9 hi\u1EC3n th\u1ECB cho admin/gi\u00E1o vi\u00EAn khi review. H\u1ECDc sinh ch\u1EC9 nghe audio." })] }), _jsxs("div", { className: "flex items-center justify-between rounded-lg border border-gray-200 bg-gray-50 px-4 py-3", children: [_jsxs("div", { className: "space-y-0.5", children: [_jsx(Label, { className: "text-sm font-semibold text-gray-700", children: "C\u00E2u h\u1ECFi d\u1EF1 ph\u00F2ng" }), _jsx("p", { className: "text-xs text-gray-400", children: "\u0110\u00E1nh d\u1EA5u c\u00E2u h\u1ECFi n\u00E0y l\u00E0 d\u1EF1 ph\u00F2ng (backup)" })] }), _jsx(Switch, { checked: watchedIsBackup, onCheckedChange: (checked) => form.setValue('isBackup', checked, { shouldValidate: true }) })] })] }), _jsxs(CompactFieldCard, { header: _jsxs("div", { className: "flex items-center justify-between", 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-gray-100", children: _jsx(MessageSquareText, { className: "h-3.5 w-3.5 text-gray-600" }) }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("p", { className: "text-sm font-semibold text-gray-800", children: "\u0110\u00E1p \u00E1n mong mu\u1ED1n" }), _jsx("span", { className: "inline-flex items-center rounded-full border border-green-200 bg-green-50 px-2.5 py-0.5 text-xs font-medium text-green-600", children: "Kh\u00F4ng b\u1EAFt bu\u1ED9c" })] }), _jsx("p", { className: "text-sm text-gray-500 mt-0.5", children: isPartnerResponder
|
|
363
|
+
? 'Câu mẫu — đáp án dùng để ghi nhớ nội dung (không chấm điểm)'
|
|
364
|
+
: 'Đáp án tham chiếu để giáo viên chấm thủ công' })] })] }), _jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: handleAddExpectedAnswer, className: "gap-1.5 border-green-200 text-green-700 hover:bg-green-50", children: [_jsx(Plus, { className: "h-4 w-4" }), "Th\u00EAm \u0111\u00E1p \u00E1n"] })] }), children: [_jsxs("div", { className: "flex items-start gap-2 rounded-lg border border-blue-100 bg-blue-50/60 px-3 py-2.5", children: [_jsx(Info, { className: "h-4 w-4 text-blue-500 mt-0.5 flex-shrink-0" }), _jsx("p", { className: "text-xs text-blue-700", children: isPartnerResponder
|
|
365
|
+
? 'Câu hỏi mẫu: có thể nhập nội dung Partner nói để ghi nhớ. Không dùng để chấm điểm.'
|
|
366
|
+
: 'Giáo viên dùng các đáp án này làm tham chiếu khi chấm thủ công câu trả lời của học sinh.' })] }), watchedExpectedAnswers.map((answer, index) => (_jsxs("div", { className: "flex items-start gap-2", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-full bg-green-100 text-xs font-semibold text-green-700 mt-1.5 flex-shrink-0", children: index + 1 }), _jsx(Textarea, { value: answer || '', onChange: (e) => handleExpectedAnswerChange(index, e.target.value), placeholder: index === 0
|
|
367
|
+
? 'VD: My name is Bingo. I am 8 years old.'
|
|
368
|
+
: 'Thêm đáp án mẫu khác...', rows: 2, className: "min-h-[60px] border-gray-200 bg-white transition-all focus:border-green-300 focus:ring-green-200 resize-none flex-1" }), watchedExpectedAnswers.length > 1 && (_jsx(Button, { type: "button", variant: "ghost", size: "icon", className: "mt-1.5 h-7 w-7 text-red-400 hover:text-red-600 hover:bg-red-50 flex-shrink-0", onClick: () => handleRemoveExpectedAnswer(index), "aria-label": `Xóa đáp án ${index + 1}`, children: _jsx(Trash2, { className: "h-4 w-4" }) }))] }, index)))] }), _jsx(CompactExplanationToggle, { value: watchedExplanation || '', onValueChange: (value) => form.setValue('explanation', value, { shouldValidate: true }), defaultVisible: Boolean(watchedExplanation) }), externalErrors && externalErrors.length > 0 && (_jsx("div", { className: "flex items-start gap-3 rounded-xl border border-red-200 bg-red-50 p-4", children: _jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "font-semibold text-red-800", children: "C\u1EA7n s\u1EEDa c\u00E1c l\u1ED7i sau:" }), _jsx("ul", { className: "mt-1 list-inside list-disc text-red-700", children: externalErrors.map((error, index) => (_jsx("li", { children: error }, index))) })] }) }))] }));
|
|
373
369
|
}
|
package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js
CHANGED
|
@@ -33,9 +33,9 @@ export function SpeakingConversationPreviewClient({ questionData, isReviewMode =
|
|
|
33
33
|
: questionData.images.length === 4
|
|
34
34
|
? 'grid-cols-4'
|
|
35
35
|
: 'grid-cols-3';
|
|
36
|
-
return (_jsxs("div", { className: "space-y-4", children: [questionData.isBackup && (_jsxs("div", { className: "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-md bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold shadow-sm", children: [_jsx("span", { children: "\uD83D\uDD04" }), " C\u00E2u h\u1ECFi d\u1EF1 ph\u00F2ng"] })), _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-
|
|
36
|
+
return (_jsxs("div", { className: "space-y-4", children: [questionData.isBackup && (_jsxs("div", { className: "inline-flex items-center gap-1.5 px-3 py-1.5 rounded-md bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold shadow-sm", children: [_jsx("span", { children: "\uD83D\uDD04" }), " C\u00E2u h\u1ECFi d\u1EF1 ph\u00F2ng"] })), _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-gray-50 px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100 text-gray-600", children: _jsx(MessageCircle, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "H\u1ED9i tho\u1EA1i Speaking (KFS)" })] }), _jsxs("div", { className: `inline-flex items-center gap-1.5 rounded-full px-3 py-1 text-xs font-semibold ${isPartnerQuestion
|
|
37
37
|
? 'bg-orange-100 text-orange-700 border border-orange-200'
|
|
38
|
-
: 'bg-green-100 text-green-700 border border-green-200'}`, children: [_jsx(Mic, { className: "h-3 w-3" }), isPartnerQuestion ? 'Bạn thi (ví dụ)' : 'Học sinh trả lời'] })] }), _jsxs("div", { className: "p-
|
|
38
|
+
: 'bg-green-100 text-green-700 border border-green-200'}`, children: [_jsx(Mic, { className: "h-3 w-3" }), isPartnerQuestion ? 'Bạn thi (ví dụ)' : 'Học sinh trả lời'] })] }), _jsxs("div", { className: "p-3 space-y-3", children: [(questionData.partnerName || questionData.partnerAvatarUrl) && (_jsxs("div", { className: "flex items-center gap-2 rounded-lg bg-orange-50 border border-orange-100 px-4 py-2.5", children: [_jsx("span", { className: "text-xs font-medium text-orange-600", children: "Nh\u00E2n v\u1EADt b\u1EA1n thi:" }), _jsx(PartnerAvatar, { avatarUrl: questionData.partnerAvatarUrl, name: questionData.partnerName })] })), questionData.questionText && (_jsxs("div", { className: "rounded-lg bg-blue-50 border border-blue-200 px-4 py-3", children: [_jsx("p", { className: "text-xs font-medium text-blue-600 mb-1", children: "N\u1ED9i dung c\u00E2u h\u1ECFi:" }), _jsx("p", { className: "text-sm font-semibold text-gray-800", children: questionData.questionText })] })), _jsxs("div", { className: "space-y-3", children: [questionData.interlocutorAudioUrl && (_jsx(ConversationAudioPlayer, { audioUrl: questionData.interlocutorAudioUrl, label: "Audio ng\u01B0\u1EDDi h\u1ECFi (Interlocutor)" })), questionData.partnerAudioUrl && (_jsx(ConversationAudioPlayer, { audioUrl: questionData.partnerAudioUrl, label: "Audio b\u1EA1n thi (Partner)" }))] }), hasImages && (_jsxs("div", { children: [_jsx("p", { className: "text-xs font-medium text-gray-500 mb-2", children: "H\u00ECnh \u1EA3nh th\u1EA3o lu\u1EADn:" }), _jsx("div", { className: `grid ${gridCols} gap-3`, style: { maxWidth: '500px' }, children: questionData.images.map((img) => (_jsx(PreviewImageItem, { url: img.url, label: img.label }, img.id))) })] })), _jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-dashed border-gray-300 bg-gray-50/80 px-4 py-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-indigo-50 ring-2 ring-indigo-100/50", children: _jsx(Mic, { className: "w-4 h-4 text-indigo-400" }) }), _jsx("p", { className: "text-xs text-gray-400", children: isPartnerQuestion
|
|
39
39
|
? 'Câu hỏi ví dụ — Bạn thi sẽ trả lời, học sinh lắng nghe'
|
|
40
40
|
: 'Học sinh sẽ nhấn nút ghi âm và nói câu trả lời tại đây' })] }), isReviewMode && questionData.expectedAnswers && questionData.expectedAnswers.length > 0 && (_jsxs("div", { className: "rounded-xl border border-green-200 bg-green-50/80 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-3", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), _jsx("h4", { className: "text-sm font-semibold text-green-800", children: "C\u00E1c \u0111\u00E1p \u00E1n mong mu\u1ED1n" })] }), _jsx("div", { className: "space-y-2", children: questionData.expectedAnswers.map((answer, index) => (_jsxs("div", { className: "flex items-center gap-2.5 rounded-lg bg-white/70 border border-green-100 px-3 py-2", children: [_jsx("span", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-green-100 text-green-700 text-xs font-bold", children: index + 1 }), _jsx("span", { className: "text-sm text-green-800", children: answer })] }, index))) })] }))] })] })] }));
|
|
41
41
|
}
|
|
@@ -23,5 +23,5 @@ export function mapQuestionToSpeakingCueCardData(question) {
|
|
|
23
23
|
}
|
|
24
24
|
export function SpeakingCueCardClient({ questionData, isReviewMode = false, }) {
|
|
25
25
|
const answers = questionData.expectedAnswers || [];
|
|
26
|
-
return (_jsxs("div", { className: "space-y-4", children: [questionData.isBackup && (_jsxs("div", { className: "inline-flex self-center items-center gap-1.5 px-3 py-1.5 rounded-md bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold shadow-sm", children: [_jsx("span", { children: "\uD83D\uDD04" }), " C\u00E2u h\u1ECFi d\u1EF1 ph\u00F2ng"] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsx("div", { className: "flex items-center justify-between border-b border-gray-100 bg-
|
|
26
|
+
return (_jsxs("div", { className: "space-y-4", children: [questionData.isBackup && (_jsxs("div", { className: "inline-flex self-center items-center gap-1.5 px-3 py-1.5 rounded-md bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold shadow-sm", children: [_jsx("span", { children: "\uD83D\uDD04" }), " C\u00E2u h\u1ECFi d\u1EF1 ph\u00F2ng"] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsx("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gray-50 px-3 py-2", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100 text-gray-600", children: _jsx(CreditCard, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "Th\u1EBB g\u1EE3i \u00FD (Cue Card)" })] }) }), _jsxs("div", { className: "p-3 space-y-3", children: [_jsx("div", { className: "rounded-xl border border-purple-100 bg-purple-50/40 px-5 py-4", children: _jsx(RichTextHtml, { html: questionData.cueCardHtml, className: "text-sm text-slate-800", fallback: _jsx("p", { className: "text-sm text-gray-400", children: "Ch\u01B0a c\u00F3 n\u1ED9i dung cue card" }) }) }), _jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-dashed border-gray-300 bg-gray-50/80 px-4 py-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-purple-50 ring-2 ring-purple-100/50", children: _jsx(Mic, { className: "w-4 h-4 text-purple-400" }) }), _jsx("p", { className: "text-xs text-gray-400", children: "H\u1ECDc sinh \u0111\u1ECDc th\u1EBB g\u1EE3i \u00FD, chu\u1EA9n b\u1ECB, r\u1ED3i tr\u1EA3 l\u1EDDi b\u1EB1ng gi\u1ECDng n\u00F3i" })] }), isReviewMode && answers.length > 0 && (_jsxs("div", { className: "rounded-xl border border-green-200 bg-green-50/80 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-3", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), _jsx("h4", { className: "text-sm font-semibold text-green-800", children: "\u0110\u00E1p \u00E1n m\u1EABu" })] }), _jsx("div", { className: "space-y-2", children: answers.map((answer, index) => (_jsxs("div", { className: "flex items-center gap-2.5 rounded-lg bg-white/70 border border-green-100 px-3 py-2", children: [_jsx("span", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-green-100 text-green-700 text-xs font-bold", children: index + 1 }), _jsx("span", { className: "text-sm text-green-800", children: answer })] }, `${answer}-${index}`))) })] })), isReviewMode && questionData.explanation && (_jsxs("div", { className: "rounded-xl border border-amber-200 bg-amber-50 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(Info, { className: "h-4 w-4 text-amber-600" }), _jsx("p", { className: "text-sm font-semibold text-amber-800", children: "Gi\u1EA3i th\u00EDch" })] }), _jsx("p", { className: "text-sm text-amber-700 leading-relaxed whitespace-pre-wrap", children: questionData.explanation })] }))] })] })] }));
|
|
27
27
|
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
|
4
|
-
import { Card, CardContent, CardHeader, CardTitle } from '../../../../components/ui/card';
|
|
5
|
-
import { Input } from '../../../../components/ui/input';
|
|
6
4
|
import { Label } from '../../../../components/ui/label';
|
|
7
5
|
import { CueCardEditor } from '../../../../components/shared/CueCardEditor';
|
|
8
|
-
import { CreatorGuide } from '../../components/CreatorGuide';
|
|
9
6
|
import { CreditCard } from 'lucide-react';
|
|
7
|
+
import { CompactCreatorHeader, CompactFieldCard, CompactPreviewBanner, } from '../../_shared/components/compact';
|
|
8
|
+
import { SpeakingCueCardClient } from './SpeakingCueCardClient';
|
|
10
9
|
export function SpeakingCueCardCreator({ initialData, onChange, onUnsavedChangesChange, externalErrors, validationRef, }) {
|
|
11
10
|
const [htmlContent, setHtmlContent] = useState(initialData?.cueCardHtml || '');
|
|
12
11
|
const [points, setPoints] = useState(initialData?.points ?? 5);
|
|
12
|
+
const [isClientMode, setIsClientMode] = useState(false);
|
|
13
13
|
const isInitializingRef = useRef(true);
|
|
14
14
|
const previousInitialDataRef = useRef(initialData);
|
|
15
15
|
const previousPayloadRef = useRef(null);
|
|
@@ -66,9 +66,8 @@ export function SpeakingCueCardCreator({ initialData, onChange, onUnsavedChanges
|
|
|
66
66
|
};
|
|
67
67
|
}
|
|
68
68
|
}, [validationRef, htmlContent, points, buildPayload]);
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
] }), _jsxs(Card, { className: "overflow-hidden border border-slate-200 bg-white shadow-sm", children: [_jsx(CardHeader, { className: "border-b border-slate-100 bg-slate-50/50 pb-4", 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-purple-100 text-purple-700", children: _jsx(CreditCard, { className: "h-4 w-4" }) }), _jsx(CardTitle, { className: "text-lg font-semibold text-slate-800", children: "C\u00E2u h\u1ECFi Th\u1EBB g\u1EE3i \u00FD (Cue Card)" })] }) }), _jsxs(CardContent, { className: "p-0", children: [_jsxs("div", { className: "p-6 space-y-8", children: [_jsxs("div", { className: "w-full xl:w-1/3 space-y-1.5 animate-in fade-in zoom-in-95 duration-200", children: [_jsxs(Label, { htmlFor: "points", className: "text-sm font-semibold text-slate-700", children: ["\u0110i\u1EC3m s\u1ED1 ", _jsx("span", { className: "text-red-500", children: "*" })] }), _jsx(Input, { id: "points", type: "number", step: "1", min: "1", value: points, onChange: (e) => setPoints(parseInt(e.target.value) || 0), className: "w-24 border-slate-200 focus:border-blue-400 focus:ring-blue-100" })] }), _jsx("hr", { className: "border-slate-100" }), _jsx("div", { className: "space-y-3", children: _jsx(CueCardEditor, { value: htmlContent, onChange: setHtmlContent, label: _jsxs(Label, { className: "text-sm font-semibold text-slate-700", children: ["N\u1ED9i dung Cue Card ", _jsx("span", { className: "text-red-500", children: "*" })] }) }) })] }), externalErrors && externalErrors.length > 0 && (_jsx("div", { className: "border-t border-red-100 bg-red-50 p-6", children: _jsx("div", { className: "flex items-start gap-3", children: _jsxs("div", { className: "text-sm", children: [_jsx("p", { className: "font-semibold text-red-800", children: "C\u1EA7n ph\u1EA3i s\u1EEDa c\u00E1c l\u1ED7i sau:" }), _jsx("ul", { className: "mt-1 list-inside list-disc text-red-700", children: externalErrors.map((error, index) => (_jsx("li", { children: error }, index))) })] }) }) }))] })] })] }));
|
|
69
|
+
if (isClientMode) {
|
|
70
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(CompactPreviewBanner, { onBack: () => setIsClientMode(false) }), _jsx(SpeakingCueCardClient, { questionData: { cueCardHtml: htmlContent }, isReviewMode: true })] }));
|
|
71
|
+
}
|
|
72
|
+
return (_jsx("div", { className: "space-y-3", children: _jsxs(CompactFieldCard, { header: _jsx(CompactCreatorHeader, { icon: _jsx(CreditCard, { className: "h-3.5 w-3.5" }), title: "C\u00E2u h\u1ECFi Th\u1EBB g\u1EE3i \u00FD (Cue Card)", points: { value: points, onValueChange: setPoints }, preview: { onClick: () => setIsClientMode(true) } }), children: [_jsx(CueCardEditor, { value: htmlContent, onChange: setHtmlContent, label: _jsxs(Label, { className: "text-sm font-semibold text-slate-700", children: ["N\u1ED9i dung Cue Card ", _jsx("span", { className: "text-red-500", children: "*" })] }) }), externalErrors && externalErrors.length > 0 && (_jsxs("div", { className: "rounded-md border border-red-100 bg-red-50 px-3 py-2 text-xs", children: [_jsx("p", { className: "font-semibold text-red-800", children: "C\u1EA7n ph\u1EA3i s\u1EEDa c\u00E1c l\u1ED7i sau:" }), _jsx("ul", { className: "mt-1 list-inside list-disc text-red-700", children: externalErrors.map((error, index) => (_jsx("li", { children: error }, index))) })] }))] }) }));
|
|
74
73
|
}
|
package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js
CHANGED
|
@@ -4,7 +4,7 @@ import { Mic, CheckCircle2, Info, ImageIcon, FileQuestion } from 'lucide-react';
|
|
|
4
4
|
import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
|
|
5
5
|
export function SpeakingDescribeImageClient({ questionData, isReviewMode = false, }) {
|
|
6
6
|
const { previewUrl: imageUrl } = usePresignedFileUrl(questionData.imageUrl || '');
|
|
7
|
-
return (_jsxs("div", { className: "space-y-4", children: [questionData.isBackup && (_jsxs("div", { className: "inline-flex self-center items-center gap-1.5 px-3 py-1.5 rounded-md bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold shadow-sm", children: [_jsx("span", { children: "\uD83D\uDD04" }), " C\u00E2u h\u1ECFi d\u1EF1 ph\u00F2ng"] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsx("div", { className: "flex items-center justify-between border-b border-gray-100 bg-
|
|
7
|
+
return (_jsxs("div", { className: "space-y-4", children: [questionData.isBackup && (_jsxs("div", { className: "inline-flex self-center items-center gap-1.5 px-3 py-1.5 rounded-md bg-amber-50 text-amber-700 border border-amber-200 text-xs font-bold shadow-sm", children: [_jsx("span", { children: "\uD83D\uDD04" }), " C\u00E2u h\u1ECFi d\u1EF1 ph\u00F2ng"] })), _jsxs("div", { className: "overflow-hidden rounded-xl border border-gray-200 bg-white shadow-sm", children: [_jsx("div", { className: "flex items-center justify-between border-b border-gray-100 bg-gray-50 px-3 py-2", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex h-7 w-7 items-center justify-center rounded-md bg-gray-100 text-gray-600", children: _jsx(Mic, { className: "h-4 w-4" }) }), _jsx("span", { className: "text-sm font-medium text-gray-700", children: "M\u00F4 t\u1EA3 h\u00ECnh \u1EA3nh (Speaking)" })] }) }), _jsxs("div", { className: "p-3 space-y-3", children: [questionData.question && (_jsxs("div", { className: "flex gap-3", children: [_jsx(FileQuestion, { className: "h-5 w-5 text-indigo-500 shrink-0 mt-0.5" }), _jsx("div", { className: "text-base font-semibold text-gray-800", children: questionData.question })] })), imageUrl ? (_jsx("div", { className: "w-full max-w-3xl mx-auto rounded-xl overflow-hidden shadow-sm border border-gray-200 bg-gray-50 flex justify-center", children: _jsx("img", { src: imageUrl, alt: "Scene", className: "w-[80%] max-h-[400px] object-contain flex-shrink-0" }) })) : (_jsxs("div", { className: "flex flex-col items-center justify-center py-10 rounded-xl border border-dashed border-gray-300 bg-gray-50", children: [_jsx(ImageIcon, { className: "h-8 w-8 text-gray-400 mb-2" }), _jsx("span", { className: "text-sm text-gray-500", children: "Ch\u01B0a t\u1EA3i h\u00ECnh \u1EA3nh m\u00F4 t\u1EA3" })] })), _jsxs("div", { className: "flex items-center gap-3 rounded-lg border border-dashed border-gray-300 bg-gray-50/80 px-4 py-3", children: [_jsx("div", { className: "flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-full bg-indigo-50 ring-2 ring-indigo-100/50", children: _jsx(Mic, { className: "w-4 h-4 text-indigo-400" }) }), _jsx("p", { className: "text-xs text-gray-400", children: isReviewMode && questionData.audioUrl
|
|
8
8
|
? "Audio ghi âm của học sinh (đã lưu)"
|
|
9
9
|
: "Học sinh sẽ nhìn hình ảnh và nhấn nút micro để mô tả" })] }), isReviewMode && questionData.expectedAnswers && questionData.expectedAnswers.length > 0 && (_jsxs("div", { className: "rounded-xl border border-green-200 bg-green-50/80 p-4 mt-6", children: [_jsxs("div", { className: "flex items-center gap-2 mb-3", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), _jsx("h4", { className: "text-sm font-semibold text-green-800", children: "C\u00E1c \u0111\u00E1p \u00E1n t\u1EF1 \u0111\u1ED9ng (Tham chi\u1EBFu AI)" })] }), _jsx("div", { className: "space-y-2", children: questionData.expectedAnswers.map((answer, index) => (_jsxs("div", { className: "flex items-center gap-2.5 rounded-lg bg-white/70 border border-green-100 px-3 py-2", children: [_jsx("span", { className: "flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-green-100 text-green-700 text-xs font-bold", children: index + 1 }), _jsx("span", { className: "text-sm text-green-800", children: answer })] }, index))) })] })), isReviewMode && questionData.explanation && (_jsxs("div", { className: "rounded-xl border border-amber-200 bg-amber-50 p-4 mt-2", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(Info, { className: "h-4 w-4 text-amber-600" }), _jsx("p", { className: "text-sm font-semibold text-amber-800", children: "Gi\u1EA3i th\u00EDch gi\u00E1o vi\u00EAn" })] }), _jsx("p", { className: "text-sm text-amber-700 leading-relaxed whitespace-pre-wrap", children: questionData.explanation })] }))] })] })] }));
|
|
10
10
|
}
|