@tinyweb_dev/oe-exam-sdk 1.0.8 → 1.0.10
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/take/components/QuestionRenderer.js +1 -5
- package/dist/components/exams/take/components/question-renderers/ChooseThenAnswerGroupRenderer.js +1 -1
- package/dist/components/exams/take/components/question-renderers/MoversCrossOutWordGroupRenderer.js +24 -10
- package/dist/components/exams/take/components/question-renderers/MoversCrosswordPuzzleRenderer.js +1 -1
- package/dist/components/exams/take/components/question-renderers/MoversFillInBlankGroupRenderer.js +2 -1
- package/dist/components/exams/take/components/speaking/renderers/SpeakingSpontaneousQaGroupRenderer.js +12 -2
- package/dist/components/exams/take/hooks/useSpontaneousQaGroupRendererSetup.js +2 -1
- package/dist/components/exams/take/utils/answer-transformers.js +16 -1
- package/dist/components/exams/take/utils/cross-out-word-group.transformer.js +4 -6
- package/dist/components/exams/take/utils/question-transformers.d.ts +5 -0
- package/dist/components/exams/take/utils/question-transformers.js +6 -0
- package/dist/components/questions/_shared/components/compact/KidChoiceRow.d.ts +12 -0
- package/dist/components/questions/_shared/components/compact/KidChoiceRow.js +33 -0
- package/dist/components/questions/_shared/components/compact/KidInstruction.d.ts +5 -0
- package/dist/components/questions/_shared/components/compact/KidInstruction.js +9 -0
- package/dist/components/questions/_shared/components/compact/KidPrompt.d.ts +5 -0
- package/dist/components/questions/_shared/components/compact/KidPrompt.js +9 -0
- package/dist/components/questions/_shared/components/compact/KidQuestionCard.d.ts +12 -0
- package/dist/components/questions/_shared/components/compact/KidQuestionCard.js +7 -0
- package/dist/components/questions/_shared/components/compact/index.d.ts +4 -0
- package/dist/components/questions/_shared/components/compact/index.js +4 -0
- package/dist/components/questions/_shared/types/answer-the-question-group.type.d.ts +3 -0
- package/dist/components/questions/_shared/types/choose-then-answer-group.type.d.ts +17 -0
- package/dist/components/questions/_shared/types/cross-out-word-group.type.d.ts +1 -0
- package/dist/components/questions/_shared/types/crossword-puzzle.type.d.ts +5 -1
- package/dist/components/questions/_shared/types/spontaneous-qa-group.type.d.ts +17 -0
- package/dist/components/questions/_shared/types/true-false-correct-group.type.d.ts +1 -0
- package/dist/components/questions/_shared/types/true-false-group.type.d.ts +1 -0
- package/dist/components/questions/types/answer-the-question/AnswerTheQuestionClient.js +18 -19
- package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupClient.d.ts +1 -1
- package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupClient.js +16 -17
- package/dist/components/questions/types/answer-the-question-group/AnswerTheQuestionGroupCreator.js +7 -4
- package/dist/components/questions/types/answer-the-question-group/map-answer-the-question-group-data.js +2 -0
- package/dist/components/questions/types/answer-the-question-group/transform.js +12 -5
- package/dist/components/questions/types/arrange-letters-into-words/ArrangeLettersIntoWordsClient.js +17 -11
- package/dist/components/questions/types/choose-correct-adjective/ChooseCorrectAdjectiveClient.js +15 -42
- package/dist/components/questions/types/choose-the-correct-answer/ChooseTheCorrectAnswerClient.js +15 -36
- package/dist/components/questions/types/choose-the-correct-answer-group/ChooseTheCorrectAnswerGroupClient.js +9 -32
- package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupClient.d.ts +1 -1
- package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupClient.js +76 -11
- package/dist/components/questions/types/choose-then-answer-group/ChooseThenAnswerGroupCreator.js +54 -6
- package/dist/components/questions/types/choose-then-answer-group/map-choose-then-answer-group-data.d.ts +4 -1
- package/dist/components/questions/types/choose-then-answer-group/map-choose-then-answer-group-data.js +39 -2
- package/dist/components/questions/types/choose-then-answer-group/transform.js +27 -14
- package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.d.ts +1 -1
- package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupClient.js +76 -7
- package/dist/components/questions/types/cross-out-word-group/CrossOutWordGroupCreator.js +24 -44
- package/dist/components/questions/types/cross-out-word-group/index.d.ts +1 -0
- package/dist/components/questions/types/cross-out-word-group/index.js +1 -0
- package/dist/components/questions/types/cross-out-word-group/map-cross-out-word-group-data.js +9 -9
- package/dist/components/questions/types/cross-out-word-group/tokenize.d.ts +2 -0
- package/dist/components/questions/types/cross-out-word-group/tokenize.js +11 -0
- package/dist/components/questions/types/cross-out-word-group/transform.js +2 -4
- package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.d.ts +2 -1
- package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleClient.js +20 -8
- package/dist/components/questions/types/crossword-puzzle/CrosswordPuzzleCreator.js +68 -11
- package/dist/components/questions/types/crossword-puzzle/transform.js +59 -30
- package/dist/components/questions/types/fill-in-blank/FillInBlankClient.js +26 -31
- package/dist/components/questions/types/fill-in-blank-group/FillInBlankAnswerKey.d.ts +6 -0
- package/dist/components/questions/types/fill-in-blank-group/FillInBlankAnswerKey.js +17 -0
- package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupClient.js +32 -28
- package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupCreator.js +109 -26
- package/dist/components/questions/types/fill-in-blank-group/FillInBlankGroupMediaField.js +21 -11
- package/dist/components/questions/types/fill-in-blank-group/blank-utils.d.ts +1 -0
- package/dist/components/questions/types/fill-in-blank-group/blank-utils.js +4 -1
- package/dist/components/questions/types/fill-missing-words-in-grid/FillMissingWordsInGridClient.js +32 -31
- package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixClient.d.ts +12 -2
- package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixClient.js +188 -46
- package/dist/components/questions/types/find-words-in-matrix/FindWordsInMatrixCreator.js +6 -2
- package/dist/components/questions/types/find-words-in-matrix/transform.js +5 -0
- package/dist/components/questions/types/label-the-picture/LabelThePictureClient.js +4 -3
- package/dist/components/questions/types/listen-and-choose-from-answer-group/ListenAndChooseFromAnswerGroupClient.js +11 -30
- package/dist/components/questions/types/listen-and-speak-answer/ListenAndSpeakAnswerClient.js +7 -6
- package/dist/components/questions/types/listen-and-speak-compare-images/ListenAndSpeakCompareImagesClient.js +2 -1
- package/dist/components/questions/types/listen-and-speak-image-group/ListenAndSpeakImageGroupClient.js +14 -13
- package/dist/components/questions/types/listen-and-speak-info-exchange/ListenAndSpeakInfoExchangeClient.js +4 -3
- package/dist/components/questions/types/listen-and-speak-odd-one-out/ListenAndSpeakOddOneOutClient.js +4 -3
- package/dist/components/questions/types/listen-and-speak-question-list/ListenAndSpeakQuestionListClient.js +11 -10
- package/dist/components/questions/types/listen-and-speak-with-story-images/ListenAndSpeakWithStoryImagesClient.js +4 -3
- package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js +13 -11
- package/dist/components/questions/types/match-columns-to-make-sentences/MatchColumnsToMakeSentencesClient.js +17 -14
- package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureClient.js +16 -12
- package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureGroupClient.js +14 -14
- package/dist/components/questions/types/match-word-to-picture-group/MatchWordToPictureGroupClient.js +13 -13
- package/dist/components/questions/types/matching-with-lines-group/MatchingWithLinesGroupClient.js +7 -6
- package/dist/components/questions/types/read-passage-and-answer-questions/ReadPassageAndAnswerQuestionsClient.js +8 -26
- package/dist/components/questions/types/sort-words-into-categories/SortWordsIntoCategoriesClient.js +2 -1
- package/dist/components/questions/types/speaking-conversation/SpeakingConversationPreviewClient.js +6 -5
- package/dist/components/questions/types/speaking-cue-card/SpeakingCueCardClient.js +2 -1
- package/dist/components/questions/types/speaking-describe-image/SpeakingDescribeImageClient.js +5 -4
- package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.d.ts +1 -1
- package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupClient.js +54 -11
- package/dist/components/questions/types/spontaneous-qa-group/SpontaneousQaGroupCreator.js +11 -7
- package/dist/components/questions/types/spontaneous-qa-group/creator-helpers.js +8 -2
- package/dist/components/questions/types/spontaneous-qa-group/flatten.d.ts +1 -0
- package/dist/components/questions/types/spontaneous-qa-group/flatten.js +4 -2
- package/dist/components/questions/types/spontaneous-qa-group/map-spontaneous-qa-group-data.js +5 -3
- package/dist/components/questions/types/spontaneous-qa-group/register.js +5 -2
- package/dist/components/questions/types/spontaneous-qa-group/transform.js +6 -2
- package/dist/components/questions/types/true-false/TrueFalseClient.js +5 -54
- package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupClient.d.ts +1 -1
- package/dist/components/questions/types/true-false-correct-group/TrueFalseCorrectGroupClient.js +39 -19
- package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.d.ts +1 -1
- package/dist/components/questions/types/true-false-group/TrueFalseGroupClient.js +20 -19
- package/dist/components/questions/types/word-fill-paragraph/WordFillParagraphClient.js +76 -81
- package/dist/components/questions/types/word-fill-structured-form/WordFillStructuredFormClient.js +17 -16
- package/dist/components/questions/types/word-order-and-match-group/WordOrderAndMatchGroupClient.js +31 -36
- package/dist/components/questions/types/word-ordering/WordOrderingClient.js +15 -14
- package/dist/components/questions/types/word-ordering-group/WordOrderingGroupClient.js +9 -8
- package/dist/components/questions/types/write-correct-verb-form/WriteCorrectVerbFormClient.js +16 -13
- package/dist/components/questions/viewer/AnswerTheQuestionGroupViewer.d.ts +3 -2
- package/dist/components/questions/viewer/AnswerTheQuestionGroupViewer.js +12 -5
- package/dist/components/questions/viewer/ChooseThenAnswerGroupViewer.d.ts +3 -2
- package/dist/components/questions/viewer/ChooseThenAnswerGroupViewer.js +4 -3
- package/dist/components/questions/viewer/CrossOutWordGroupViewer.d.ts +3 -1
- package/dist/components/questions/viewer/CrossOutWordGroupViewer.js +12 -2
- package/dist/components/questions/viewer/FillInBlankGroupViewer.d.ts +6 -2
- package/dist/components/questions/viewer/FillInBlankGroupViewer.js +13 -7
- package/dist/components/questions/viewer/QuestionViewer.d.ts +9 -1
- package/dist/components/questions/viewer/QuestionViewer.js +23 -10
- package/dist/components/questions/viewer/SpontaneousQaGroupViewer.d.ts +7 -1
- package/dist/components/questions/viewer/SpontaneousQaGroupViewer.js +14 -2
- package/dist/components/questions/viewer/TrueFalseCorrectGroupViewer.d.ts +3 -2
- package/dist/components/questions/viewer/TrueFalseCorrectGroupViewer.js +14 -4
- package/dist/components/questions/viewer/TrueFalseGroupViewer.d.ts +5 -2
- package/dist/components/questions/viewer/TrueFalseGroupViewer.js +14 -4
- package/dist/components/questions/viewer/WordOrderAndMatchGroupViewer.d.ts +6 -2
- package/dist/components/questions/viewer/WordOrderAndMatchGroupViewer.js +26 -6
- package/dist/components/results/renderers/ReviewAnswerTheQuestionGroupRenderer.js +1 -1
- package/dist/components/results/renderers/review-question-body-movers.js +1 -1
- package/dist/components/results/review-data.js +8 -1
- package/dist/shared/lib/utils/question-reverse-transform.js +39 -5
- package/dist/shared/types/questions/answer-the-question-group.d.ts +8 -3
- package/dist/shared/types/questions/answer-the-question-group.js +5 -0
- package/dist/shared/types/questions/choose-then-answer-group.d.ts +3 -0
- package/dist/shared/types/questions/crossword-puzzle.d.ts +1 -0
- package/dist/shared/types/questions/find-words-in-matrix.d.ts +1 -0
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { Mic, Volume2, CheckCircle2, Images, Play, Pause, ImageIcon, Info } from 'lucide-react';
|
|
4
4
|
import { Button } from '../../../../components/ui/button';
|
|
5
5
|
import { usePresignedFileUrl, useAudioPreview } from '../../../../shared/lib/hooks';
|
|
6
|
+
import { KidQuestionCard } from '../../_shared/components/compact';
|
|
6
7
|
import { cn } from '../../../../shared/lib/utils';
|
|
7
8
|
// ─── Sub-component: Custom Audio Player ──────────────────────────────────────
|
|
8
9
|
function QuestionAudioPlayer({ audioUrl }) {
|
|
@@ -18,17 +19,17 @@ function ImageWithPresigned({ url, alt }) {
|
|
|
18
19
|
// ─── Main Client Component ───────────────────────────────────────────────────
|
|
19
20
|
export function ListenAndSpeakImageGroupClient({ questionData, isReviewMode = false, }) {
|
|
20
21
|
const { audioUrl, activeImageId, groupImages = [], expectedAnswers = [], explanation } = questionData;
|
|
21
|
-
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(
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
22
|
+
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(KidQuestionCard, { title: "Nghe v\u00E0 n\u00F3i (Nh\u00F3m h\u00ECnh \u1EA3nh)", icon: _jsx(Images, { className: "h-3.5 w-3.5" }), children: [audioUrl ? (_jsx(QuestionAudioPlayer, { audioUrl: audioUrl })) : (_jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-4 py-3", children: [_jsx(Volume2, { className: "h-4 w-4 text-gray-400" }), _jsx("span", { className: "text-xs text-gray-400", children: "Ch\u01B0a c\u00F3 audio c\u00E2u h\u1ECFi" })] })), groupImages.length > 0 ? (_jsx("div", { className: "rounded-xl border border-gray-100 bg-gradient-to-b from-gray-50/80 to-white p-5", children: _jsx("div", { className: "flex flex-row justify-center gap-4 sm:gap-5 flex-wrap", children: groupImages.map((img, index) => {
|
|
23
|
+
const isActive = img.id === activeImageId;
|
|
24
|
+
const letter = String.fromCharCode(65 + index);
|
|
25
|
+
return (_jsxs("div", { className: cn('flex flex-col items-center gap-2 transition-transform duration-300', isActive && 'scale-[1.03]'), children: [_jsxs("div", { className: cn('relative w-28 h-28 sm:w-36 sm:h-36 rounded-xl overflow-hidden flex items-center justify-center bg-white transition-all duration-300', isActive
|
|
26
|
+
? 'border-[3px] border-blue-400 shadow-lg shadow-blue-100/70 ring-2 ring-blue-200/40'
|
|
27
|
+
: 'border-2 border-gray-200 shadow-sm hover:shadow-md hover:border-gray-300'), children: [img.url ? (_jsx(ImageWithPresigned, { url: img.url, alt: img.label || `Image ${index + 1}` })) : (_jsx("div", { className: "w-full h-full bg-gray-50 flex items-center justify-center text-gray-300", children: _jsx(ImageIcon, { className: "h-10 w-10" }) })), _jsx("div", { className: cn('absolute top-1.5 left-1.5 flex h-6 w-6 items-center justify-center rounded-lg text-xs font-bold shadow-sm', isActive
|
|
28
|
+
? 'bg-blue-500 text-white'
|
|
29
|
+
: 'bg-white/90 text-gray-500 border border-gray-200'), children: letter }), isActive && (_jsx("div", { className: "absolute top-1.5 right-1.5 w-5 h-5 rounded-full bg-blue-500 flex items-center justify-center shadow-sm", children: _jsx(CheckCircle2, { className: "h-3.5 w-3.5 text-white" }) }))] }), _jsxs("div", { className: "flex flex-col items-center gap-0.5", children: [_jsx("span", { className: cn('text-sm font-semibold px-2.5 py-0.5 rounded-md transition-colors', isActive
|
|
30
|
+
? 'text-blue-700 bg-blue-50'
|
|
31
|
+
: 'text-gray-600'), children: img.label || `Hình ${index + 1}` }), isActive && (_jsx("span", { className: "text-[10px] font-medium text-blue-500 animate-pulse", children: "\u0110ang h\u1ECFi" }))] })] }, img.id));
|
|
32
|
+
}) }) })) : (
|
|
33
|
+
/* Empty state for no images */
|
|
34
|
+
_jsxs("div", { className: "flex flex-col items-center justify-center py-10 px-4 rounded-xl border-2 border-dashed border-gray-200 bg-gray-50/50", children: [_jsx(ImageIcon, { className: "h-12 w-12 text-gray-300 mb-3" }), _jsx("p", { className: "text-sm font-medium text-gray-500", children: "Ch\u01B0a c\u00F3 h\u00ECnh \u1EA3nh n\u00E0o" }), _jsx("p", { className: "text-xs text-gray-400 mt-1", children: "H\u00ECnh \u1EA3nh s\u1EBD hi\u1EC3n th\u1ECB t\u1EA1i \u0111\u00E2y" })] })), _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: "H\u1ECDc sinh s\u1EBD nh\u1EA5n n\u00FAt ghi \u00E2m v\u00E0 n\u00F3i c\u00E2u tr\u1EA3 l\u1EDDi t\u1EA1i \u0111\u00E2y" })] }), isReviewMode && 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: 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 && explanation && (_jsxs("div", { className: "rounded-xl border border-indigo-200 bg-indigo-50 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(Info, { className: "h-4 w-4 text-indigo-600" }), _jsx("p", { className: "text-sm font-semibold text-indigo-800", children: "Gi\u1EA3i th\u00EDch" })] }), _jsx("p", { className: "text-sm text-indigo-700 leading-relaxed", children: explanation })] }))] })] }));
|
|
34
35
|
}
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { Volume2, Mic, CheckCircle2, Play, Pause, Info, ArrowLeftRight } from 'lucide-react';
|
|
4
4
|
import { Button } from '../../../../components/ui/button';
|
|
5
5
|
import { usePresignedFileUrl, useAudioPreview } from '../../../../shared/lib/hooks';
|
|
6
|
+
import { KidQuestionCard } from '../../_shared/components/compact';
|
|
6
7
|
// ─── Sub-component: Custom Audio Player ──────────────────────────────────────
|
|
7
8
|
function QuestionAudioPlayer({ audioUrl }) {
|
|
8
9
|
const { previewUrl } = usePresignedFileUrl(audioUrl);
|
|
@@ -32,7 +33,7 @@ export function ListenAndSpeakInfoExchangeClient({ questionData, isReviewMode =
|
|
|
32
33
|
.map((f) => ({ key: f.key, value: f.answer }));
|
|
33
34
|
const examinerRows = (qData.examinerFields || [])
|
|
34
35
|
.map((f) => ({ key: f.key, value: f.answer }));
|
|
35
|
-
return (_jsx("div", { className: "space-y-4", children: _jsxs(
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
return (_jsx("div", { className: "space-y-4", children: _jsxs(KidQuestionCard, { title: `Nghe và nói (Trao đổi thông tin — ${viewMode === 'ANSWER' ? 'Trả lời' : 'Đặt câu hỏi'})`, icon: _jsx(ArrowLeftRight, { className: "h-3.5 w-3.5" }), children: [audioUrl ? (_jsx(QuestionAudioPlayer, { audioUrl: audioUrl })) : (_jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-4 py-3", children: [_jsx(Volume2, { className: "h-4 w-4 text-gray-400" }), _jsx("span", { className: "text-xs text-gray-400", children: "Ch\u01B0a c\u00F3 audio c\u00E2u h\u1ECFi" })] })), _jsxs("div", { className: "flex flex-col lg:flex-row gap-4", children: [imageUrl && (_jsx("div", { className: "lg:w-1/2", children: _jsx(ImagePreview, { imageUrl: imageUrl, title: title }) })), _jsxs("div", { className: "flex-1 space-y-4", children: [_jsx(InfoTable, { label: "CANDIDATE'S COPY", title: title, rows: candidateRows, headerColor: "bg-teal-600" }), _jsx(InfoTable, { label: "EXAMINER'S COPY", title: title, rows: examinerRows, headerColor: "bg-amber-600" })] })] }), _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: viewMode === 'ANSWER'
|
|
37
|
+
? 'Học sinh sẽ nhấn nút ghi âm và nói câu trả lời tại đây'
|
|
38
|
+
: 'Học sinh sẽ nhấn nút ghi âm và đặt câu hỏi tại đây' })] }), isReviewMode && 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: viewMode === 'ANSWER' ? 'Các đáp án mong muốn' : 'Các câu hỏi mong muốn' })] }), _jsx("div", { className: "space-y-2", children: 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 && explanation && (_jsxs("div", { className: "rounded-xl border border-indigo-200 bg-indigo-50 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(Info, { className: "h-4 w-4 text-indigo-600" }), _jsx("p", { className: "text-sm font-semibold text-indigo-800", children: "Gi\u1EA3i th\u00EDch" })] }), _jsx("p", { className: "text-sm text-indigo-700 leading-relaxed", children: explanation })] }))] }) }));
|
|
38
39
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { Mic, Volume2, CheckCircle2, Play, Pause, ImageIcon, Info, CircleOff } from 'lucide-react';
|
|
4
4
|
import { Button } from '../../../../components/ui/button';
|
|
5
|
+
import { KidQuestionCard } from '../../_shared/components/compact';
|
|
5
6
|
import { usePresignedFileUrl, useAudioPreview } from '../../../../shared/lib/hooks';
|
|
6
7
|
// ─── Sub-component: Custom Audio Player ──────────────────────────────────────
|
|
7
8
|
function QuestionAudioPlayer({ audioUrl }) {
|
|
@@ -20,7 +21,7 @@ function OddOneOutImage({ imageUrl, index }) {
|
|
|
20
21
|
// ─── Main Client Component ───────────────────────────────────────────────────
|
|
21
22
|
export function ListenAndSpeakOddOneOutClient({ questionData, isReviewMode = false, }) {
|
|
22
23
|
const { audioUrl, images, oddOneOutIndex, expectedAnswers = [], explanation } = questionData;
|
|
23
|
-
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(
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
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(KidQuestionCard, { title: "Nghe v\u00E0 n\u00F3i (T\u00ECm h\u00ECnh kh\u00E1c lo\u1EA1i)", icon: _jsx(CircleOff, { className: "h-3.5 w-3.5" }), children: [images && images.length > 0 && (_jsx("div", { className: "bg-gradient-to-br from-amber-50/80 to-orange-50/60 rounded-2xl p-6 border border-amber-100/80 shadow-sm space-y-4", children: images.length === 4 ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "grid grid-cols-3 gap-4", children: images.slice(0, 3).map((imgUrl, index) => (_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-1.5 mb-1", children: [_jsx("span", { className: `flex h-6 w-6 items-center justify-center rounded-md text-xs font-bold ${isReviewMode && oddOneOutIndex === index ? 'bg-red-200 text-red-800' : 'bg-amber-200 text-amber-800'}`, children: index + 1 }), isReviewMode && oddOneOutIndex === index && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-red-100 border border-red-200 px-1.5 py-0.5 text-[10px] font-bold text-red-700", children: [_jsx(CircleOff, { className: "h-2.5 w-2.5" }), "Kh\u00E1c lo\u1EA1i"] }))] }), _jsx("div", { className: `relative aspect-square rounded-xl overflow-hidden border-2 bg-white shadow-sm ${isReviewMode && oddOneOutIndex === index ? 'border-red-400 ring-2 ring-red-200' : 'border-gray-200'}`, children: imgUrl ? (_jsx(OddOneOutImage, { imageUrl: imgUrl, index: index })) : (_jsx("div", { className: "w-full h-full flex items-center justify-center text-gray-400 text-sm", children: _jsx(ImageIcon, { className: "h-6 w-6" }) })) })] }, index))) }), _jsx("div", { className: "flex justify-center", children: _jsx("div", { className: "w-1/3", children: _jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-1.5 mb-1", children: [_jsx("span", { className: `flex h-6 w-6 items-center justify-center rounded-md text-xs font-bold ${isReviewMode && oddOneOutIndex === 3 ? 'bg-red-200 text-red-800' : 'bg-amber-200 text-amber-800'}`, children: "4" }), isReviewMode && oddOneOutIndex === 3 && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-red-100 border border-red-200 px-1.5 py-0.5 text-[10px] font-bold text-red-700", children: [_jsx(CircleOff, { className: "h-2.5 w-2.5" }), "Kh\u00E1c lo\u1EA1i"] }))] }), _jsx("div", { className: `relative aspect-square rounded-xl overflow-hidden border-2 bg-white shadow-sm ${isReviewMode && oddOneOutIndex === 3 ? 'border-red-400 ring-2 ring-red-200' : 'border-gray-200'}`, children: images[3] ? (_jsx(OddOneOutImage, { imageUrl: images[3], index: 3 })) : (_jsx("div", { className: "w-full h-full flex items-center justify-center text-gray-400 text-sm", children: _jsx(ImageIcon, { className: "h-6 w-6" }) })) })] }) }) })] })) : (
|
|
25
|
+
/* Fallback: equal grid for other counts */
|
|
26
|
+
_jsx("div", { className: "grid grid-cols-2 gap-4", children: images.map((imgUrl, index) => (_jsxs("div", { className: "space-y-1", children: [_jsxs("div", { className: "flex items-center gap-1.5 mb-1", children: [_jsx("span", { className: `flex h-6 w-6 items-center justify-center rounded-md text-xs font-bold ${isReviewMode && oddOneOutIndex === index ? 'bg-red-200 text-red-800' : 'bg-amber-200 text-amber-800'}`, children: index + 1 }), isReviewMode && oddOneOutIndex === index && (_jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-red-100 border border-red-200 px-1.5 py-0.5 text-[10px] font-bold text-red-700", children: [_jsx(CircleOff, { className: "h-2.5 w-2.5" }), "Kh\u00E1c lo\u1EA1i"] }))] }), _jsx("div", { className: `relative aspect-square rounded-xl overflow-hidden border-2 bg-white shadow-sm ${isReviewMode && oddOneOutIndex === index ? 'border-red-400 ring-2 ring-red-200' : 'border-gray-200'}`, children: imgUrl ? (_jsx(OddOneOutImage, { imageUrl: imgUrl, index: index })) : (_jsx("div", { className: "w-full h-full flex items-center justify-center text-gray-400 text-sm", children: _jsx(ImageIcon, { className: "h-6 w-6" }) })) })] }, index))) })) })), audioUrl ? (_jsx(QuestionAudioPlayer, { audioUrl: audioUrl })) : (_jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-4 py-3", children: [_jsx(Volume2, { className: "h-4 w-4 text-gray-400" }), _jsx("span", { className: "text-xs text-gray-400", children: "Ch\u01B0a c\u00F3 audio c\u00E2u h\u1ECFi" })] })), _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: "H\u1ECDc sinh s\u1EBD nh\u1EA5n n\u00FAt ghi \u00E2m v\u00E0 n\u00F3i c\u00E2u tr\u1EA3 l\u1EDDi t\u1EA1i \u0111\u00E2y" })] }), isReviewMode && 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: 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 && explanation && (_jsxs("div", { className: "rounded-xl border border-indigo-200 bg-indigo-50 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(Info, { className: "h-4 w-4 text-indigo-600" }), _jsx("p", { className: "text-sm font-semibold text-indigo-800", children: "Gi\u1EA3i th\u00EDch" })] }), _jsx("p", { className: "text-sm text-indigo-700 leading-relaxed", children: explanation })] }))] })] }));
|
|
26
27
|
}
|
|
@@ -3,6 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { Mic, Volume2, CheckCircle2, Play, Pause, ListChecks, Info } from 'lucide-react';
|
|
4
4
|
import { Button } from '../../../../components/ui/button';
|
|
5
5
|
import { usePresignedFileUrl, useAudioPreview } from '../../../../shared/lib/hooks';
|
|
6
|
+
import { KidQuestionCard } from '../../_shared/components/compact';
|
|
6
7
|
import { cn } from '../../../../shared/lib/utils';
|
|
7
8
|
// ─── Sub-component: Custom Audio Player ──────────────────────────────────────
|
|
8
9
|
function QuestionAudioPlayer({ audioUrl }) {
|
|
@@ -13,14 +14,14 @@ function QuestionAudioPlayer({ audioUrl }) {
|
|
|
13
14
|
// ─── Main Client Component ───────────────────────────────────────────────────
|
|
14
15
|
export function ListenAndSpeakQuestionListClient({ questionData, isReviewMode = false, }) {
|
|
15
16
|
const { audioUrl, expectedAnswers, activeQuestionId, questions = [], explanation } = questionData;
|
|
16
|
-
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(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
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(KidQuestionCard, { title: "Nghe v\u00E0 n\u00F3i (Danh s\u00E1ch c\u00E2u h\u1ECFi)", icon: _jsx(ListChecks, { className: "h-3.5 w-3.5" }), children: [audioUrl ? (_jsx(QuestionAudioPlayer, { audioUrl: audioUrl })) : (_jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-4 py-3", children: [_jsx(Volume2, { className: "h-4 w-4 text-gray-400" }), _jsx("span", { className: "text-xs text-gray-400", children: "Ch\u01B0a c\u00F3 audio c\u00E2u h\u1ECFi" })] })), questions.length > 0 ? (_jsx("div", { className: "rounded-xl border border-gray-100 bg-gradient-to-b from-gray-50/80 to-white p-5 space-y-2.5", children: questions.map((q, idx) => {
|
|
18
|
+
const isActive = q.id === activeQuestionId;
|
|
19
|
+
return (_jsxs("div", { className: cn('flex items-center gap-3 rounded-lg px-4 py-3 transition-all duration-200', isActive
|
|
20
|
+
? 'bg-blue-50 border-[2px] border-blue-300 shadow-sm shadow-blue-100/50'
|
|
21
|
+
: 'bg-white border border-gray-200 hover:border-gray-300 hover:shadow-sm'), children: [_jsx("div", { className: cn('flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-lg text-xs font-bold shadow-sm', isActive
|
|
22
|
+
? 'bg-blue-500 text-white'
|
|
23
|
+
: 'bg-gray-100 text-gray-500 border border-gray-200'), children: idx + 1 }), _jsx("span", { className: cn('text-sm font-medium flex-1', isActive ? 'text-blue-800' : 'text-gray-700'), children: q.text || _jsx("span", { className: "italic text-gray-400", children: "(Ch\u01B0a nh\u1EADp n\u1ED9i dung)" }) }), isActive && (_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsx("span", { className: "text-[10px] font-medium text-blue-500 animate-pulse", children: "\u0110ang h\u1ECFi" }), _jsx(CheckCircle2, { className: "h-4 w-4 text-blue-500" })] }))] }, q.id));
|
|
24
|
+
}) })) : (
|
|
25
|
+
/* Empty state */
|
|
26
|
+
_jsxs("div", { className: "flex flex-col items-center justify-center py-10 px-4 rounded-xl border-2 border-dashed border-gray-200 bg-gray-50/50", children: [_jsx(ListChecks, { className: "h-12 w-12 text-gray-300 mb-3" }), _jsx("p", { className: "text-sm font-medium text-gray-500", children: "Ch\u01B0a c\u00F3 c\u00E2u h\u1ECFi n\u00E0o" }), _jsx("p", { className: "text-xs text-gray-400 mt-1", children: "Danh s\u00E1ch c\u00E2u h\u1ECFi s\u1EBD hi\u1EC3n th\u1ECB t\u1EA1i \u0111\u00E2y" })] })), _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: "H\u1ECDc sinh s\u1EBD nh\u1EA5n n\u00FAt ghi \u00E2m v\u00E0 n\u00F3i c\u00E2u tr\u1EA3 l\u1EDDi t\u1EA1i \u0111\u00E2y" })] }), isReviewMode && expectedAnswers && 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: 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 && explanation && (_jsxs("div", { className: "rounded-xl border border-indigo-200 bg-indigo-50 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(Info, { className: "h-4 w-4 text-indigo-600" }), _jsx("p", { className: "text-sm font-semibold text-indigo-800", children: "Gi\u1EA3i th\u00EDch" })] }), _jsx("p", { className: "text-sm text-indigo-700 leading-relaxed", children: explanation })] }))] })] }));
|
|
26
27
|
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Mic, Volume2, CheckCircle2, Play, Pause, ImageIcon, Info, BookImage } from 'lucide-react';
|
|
4
4
|
import { Button } from '../../../../components/ui/button';
|
|
5
|
+
import { KidQuestionCard } from '../../_shared/components/compact';
|
|
5
6
|
import { usePresignedFileUrl, useAudioPreview } from '../../../../shared/lib/hooks';
|
|
6
7
|
// ─── Sub-component: Custom Audio Player ──────────────────────────────────────
|
|
7
8
|
function QuestionAudioPlayer({ audioUrl }) {
|
|
@@ -20,7 +21,7 @@ function StoryImagePreview({ imageUrl, index }) {
|
|
|
20
21
|
// ─── Main Client Component ───────────────────────────────────────────────────
|
|
21
22
|
export function ListenAndSpeakWithStoryImagesClient({ questionData, isReviewMode = false, }) {
|
|
22
23
|
const { audioUrl, images, expectedAnswers = [], explanation } = questionData;
|
|
23
|
-
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(
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
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(KidQuestionCard, { title: "Nghe v\u00E0 n\u00F3i (K\u1EC3 chuy\u1EC7n theo h\u00ECnh)", icon: _jsx(BookImage, { className: "h-3.5 w-3.5" }), children: [audioUrl ? (_jsx(QuestionAudioPlayer, { audioUrl: audioUrl })) : (_jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-gray-200 bg-gray-50 px-4 py-3", children: [_jsx(Volume2, { className: "h-4 w-4 text-gray-400" }), _jsx("span", { className: "text-xs text-gray-400", children: "Ch\u01B0a c\u00F3 audio c\u00E2u h\u1ECFi" })] })), images && images.length > 0 && (_jsx("div", { className: "bg-gradient-to-br from-purple-50/80 to-fuchsia-50/60 rounded-2xl p-6 border border-purple-100/80 shadow-sm", children: _jsx("div", { className: "grid gap-4", style: {
|
|
25
|
+
gridTemplateColumns: `repeat(${Math.min(images.length, 4)}, 1fr)`,
|
|
26
|
+
}, children: images.map((imgUrl, index) => (_jsxs("div", { className: "space-y-2", children: [_jsx("div", { className: "flex items-center gap-1.5", children: _jsx("span", { className: "flex h-6 w-6 items-center justify-center rounded-md bg-purple-200 text-purple-800 text-xs font-bold", children: index + 1 }) }), _jsx("div", { className: "relative aspect-square rounded-xl overflow-hidden border-[3px] border-purple-300 bg-white shadow-md ring-2 ring-purple-100", children: imgUrl ? (_jsx(StoryImagePreview, { imageUrl: imgUrl, index: index })) : (_jsx("div", { className: "w-full h-full flex items-center justify-center text-gray-400", children: _jsx(ImageIcon, { className: "h-6 w-6" }) })) })] }, index))) }) })), _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: "H\u1ECDc sinh s\u1EBD nh\u1EA5n n\u00FAt ghi \u00E2m v\u00E0 n\u00F3i c\u00E2u tr\u1EA3 l\u1EDDi t\u1EA1i \u0111\u00E2y" })] }), isReviewMode && 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: 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 && explanation && (_jsxs("div", { className: "rounded-xl border border-indigo-200 bg-indigo-50 p-4", children: [_jsxs("div", { className: "flex items-center gap-2 mb-2", children: [_jsx(Info, { className: "h-4 w-4 text-indigo-600" }), _jsx("p", { className: "text-sm font-semibold text-indigo-800", children: "Gi\u1EA3i th\u00EDch" })] }), _jsx("p", { className: "text-sm text-indigo-700 leading-relaxed", children: explanation })] }))] })] }));
|
|
26
27
|
}
|
package/dist/components/questions/types/match-by-writing-answer/MatchByWritingAnswerClient.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Card, CardContent } from '../../../../components/ui/card';
|
|
4
3
|
import { Input } from '../../../../components/ui/input';
|
|
5
4
|
import { Label } from '../../../../components/ui/label';
|
|
6
|
-
import { CheckCircle2, XCircle,
|
|
5
|
+
import { CheckCircle2, XCircle, Grid3X3 } from 'lucide-react';
|
|
7
6
|
import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
|
|
8
7
|
import { useState } from 'react';
|
|
8
|
+
import { KidQuestionCard } from '../../_shared/components/compact';
|
|
9
9
|
function OptionCard({ option }) {
|
|
10
10
|
const { previewUrl } = usePresignedFileUrl(option.imageUrl || '');
|
|
11
11
|
const displayUrl = previewUrl || option.imageUrl;
|
|
@@ -18,7 +18,7 @@ export function MatchByWritingAnswerClient({ questionData, onSubmit, isReviewMod
|
|
|
18
18
|
const [answer, setAnswer] = useState(userAnswer || '');
|
|
19
19
|
const { previewUrl: questionImageUrl } = usePresignedFileUrl(questionData.imageUrl || '');
|
|
20
20
|
if (!questionData.question) {
|
|
21
|
-
return (_jsx(
|
|
21
|
+
return (_jsx(KidQuestionCard, { title: "Gh\u00E9p \u0111\u00E1p \u00E1n b\u1EB1ng c\u00E1ch vi\u1EBFt", children: _jsx("p", { className: "text-gray-500", children: "Kh\u00F4ng c\u00F3 d\u1EEF li\u1EC7u c\u00E2u h\u1ECFi" }) }));
|
|
22
22
|
}
|
|
23
23
|
const isExample = questionData.isExample || false;
|
|
24
24
|
const displayedAnswer = autoFillCorrectAnswers || isExample
|
|
@@ -27,12 +27,14 @@ export function MatchByWritingAnswerClient({ questionData, onSubmit, isReviewMod
|
|
|
27
27
|
const isCorrect = isReviewMode && userAnswer !== undefined
|
|
28
28
|
? userAnswer.trim().toLowerCase() === questionData.correctAnswer.trim().toLowerCase()
|
|
29
29
|
: undefined;
|
|
30
|
-
return (_jsxs(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
return (_jsxs(KidQuestionCard, { icon: _jsx(Grid3X3, { className: "h-3.5 w-3.5" }), title: "Gh\u00E9p \u0111\u00E1p \u00E1n b\u1EB1ng c\u00E1ch vi\u1EBFt", isExample: isExample, review: isReviewMode && isCorrect !== undefined ? (isCorrect ? 'correct' : 'wrong') : null, children: [questionData.options && questionData.options.length > 0 && (_jsxs("div", { className: "rounded-xl border border-purple-200 bg-gradient-to-r from-purple-50 to-violet-50 p-4", children: [_jsx("p", { className: "mb-3 text-sm font-semibold text-purple-700", children: "C\u00E1c l\u1EF1a ch\u1ECDn:" }), _jsx("div", { className: `grid gap-3 ${questionData.options.some((o) => o.imageUrl) ? 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4' : 'grid-cols-1 sm:grid-cols-2'}`, children: questionData.options.map((option, index) => (_jsx(OptionCard, { option: option }, index))) })] })), _jsxs("div", { className: "rounded-xl border border-gray-200 bg-gradient-to-r from-gray-50 to-slate-50 p-5", children: [_jsxs("div", { className: "flex items-center gap-4", children: [(questionImageUrl || questionData.imageUrl) ? (_jsx("div", { className: "relative h-20 w-20 flex-shrink-0 overflow-hidden rounded-lg border border-gray-200 bg-gray-50 shadow-sm", children: _jsx("img", { src: questionImageUrl || questionData.imageUrl, alt: questionData.question, className: "h-full w-full object-contain" }) })) : null, _jsxs("div", { className: "flex-1 space-y-2", children: [_jsx(Label, { className: "text-base font-semibold text-gray-800", children: questionData.question }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Input, { value: displayedAnswer, onChange: (e) => {
|
|
31
|
+
setAnswer(e.target.value);
|
|
32
|
+
onSubmit?.(e.target.value);
|
|
33
|
+
}, placeholder: "Vi\u1EBFt \u0111\u00E1p \u00E1n...", disabled: isReviewMode || isExample, className: `max-w-xs border-2 text-lg font-medium ${isReviewMode
|
|
34
|
+
? isCorrect
|
|
35
|
+
? 'border-green-400 bg-green-50 text-green-700'
|
|
36
|
+
: 'border-red-400 bg-red-50 text-red-700'
|
|
37
|
+
: displayedAnswer
|
|
38
|
+
? 'border-blue-500 bg-blue-50 ring-2 ring-blue-100 focus:border-blue-500 focus:ring-blue-100'
|
|
39
|
+
: 'border-gray-200 bg-white focus:border-blue-500 focus:ring-blue-100'}` }), isReviewMode && isCorrect !== undefined && (_jsx("span", { className: "flex items-center gap-1", children: isCorrect ? (_jsx(CheckCircle2, { className: "h-5 w-5 text-green-500" })) : (_jsx(XCircle, { className: "h-5 w-5 text-red-500" })) }))] })] })] }), isReviewMode && isCorrect === false && (_jsxs("div", { className: "mt-3 flex items-center gap-2 rounded-lg bg-green-50 border border-green-200 px-4 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), _jsxs("span", { className: "text-sm text-green-700", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng: ", _jsx("strong", { children: questionData.correctAnswer })] })] })), autoFillCorrectAnswers && (_jsxs("div", { className: "mt-3 flex items-center gap-2 rounded-lg bg-blue-50 border border-blue-200 px-4 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-blue-600" }), _jsxs("span", { className: "text-sm text-blue-700", children: ["\u0110\u00E1p \u00E1n: ", _jsx("strong", { children: questionData.correctAnswer })] })] }))] }), explanation && isReviewMode && (_jsxs("div", { className: "rounded-xl border border-blue-200 bg-blue-50 p-4", children: [_jsx("p", { className: "text-sm font-semibold text-blue-800 mb-1", children: "Gi\u1EA3i th\u00EDch:" }), _jsx("p", { className: "text-sm text-blue-700", children: explanation })] }))] }));
|
|
38
40
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { Lightbulb
|
|
3
|
+
import { Lightbulb } from 'lucide-react';
|
|
4
4
|
import { cn } from '../../../../shared/lib/utils';
|
|
5
5
|
import { assembleMatchColumnsSentence, getMatchColumnsChoiceColumns, getMatchColumnsStemColumn, } from './map-match-columns-to-make-sentences-data';
|
|
6
|
+
import { KidChoiceRow, KidInstruction, KidPrompt, KidQuestionCard, optionLetter, } from '../../_shared/components/compact';
|
|
6
7
|
function emptyAnswer(stemFragmentId) {
|
|
7
8
|
return { fragmentIds: stemFragmentId ? [stemFragmentId] : [], sentence: '' };
|
|
8
9
|
}
|
|
@@ -50,7 +51,7 @@ export function MatchColumnsToMakeSentencesClient({ questionData, isReviewMode =
|
|
|
50
51
|
});
|
|
51
52
|
onAnswerChange?.(next);
|
|
52
53
|
};
|
|
53
|
-
return (_jsxs("div", { className: "space-y-4", children: [
|
|
54
|
+
return (_jsxs("div", { className: "space-y-4", children: [questionData.title ? _jsx(KidPrompt, { children: questionData.title }) : null, _jsx(KidInstruction, { children: questionData.instruction }), _jsx("div", { className: "overflow-x-auto rounded-xl border border-gray-200 bg-white", children: _jsxs("table", { className: "min-w-full text-sm", children: [_jsx("thead", { children: _jsx("tr", { className: "bg-slate-50 text-left text-xs uppercase tracking-wide text-slate-500", children: columns.map((column) => (_jsxs("th", { className: "px-3 py-2 font-semibold", children: [column.label || column.id, column.role === 'stem' ? ' (stem)' : ''] }, column.id))) }) }), _jsx("tbody", { children: _jsx("tr", { className: "align-top", children: columns.map((column) => (_jsx("td", { className: "px-3 py-2", children: _jsx("ul", { className: "space-y-1", children: column.fragments.map((fragment) => (_jsxs("li", { className: "text-slate-700", children: [_jsxs("span", { className: "mr-1 font-semibold text-slate-500", children: [fragment.id, "."] }), fragment.text || '—'] }, fragment.id))) }) }, `${column.id}-bank`))) }) })] }) }), _jsx("div", { className: "space-y-3", children: items.map((item, itemIndex) => {
|
|
54
55
|
const current = resolveAnswer(itemIndex);
|
|
55
56
|
const canSelect = canEdit && !item.isExample;
|
|
56
57
|
const stemText = stemColumn?.fragments.find((fragment) => fragment.id === item.stemFragmentId)?.text
|
|
@@ -58,17 +59,19 @@ export function MatchColumnsToMakeSentencesClient({ questionData, isReviewMode =
|
|
|
58
59
|
|| '—';
|
|
59
60
|
const isCorrect = isReviewMode &&
|
|
60
61
|
current.sentence.trim().toLowerCase() === item.sentence.trim().toLowerCase();
|
|
61
|
-
return (_jsxs(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
62
|
+
return (_jsxs(KidQuestionCard, { title: `Câu ${item.questionNumber || itemIndex + 1}`, points: item.points || 0, isExample: item.isExample, review: isReviewMode && !item.isExample ? (isCorrect ? 'correct' : 'wrong') : null, children: [_jsx(KidPrompt, { children: stemText }), choiceColumns.map((column, choiceIndex) => {
|
|
63
|
+
const selectedId = current.fragmentIds[choiceIndex + 1] || '';
|
|
64
|
+
const correctId = item.fragmentIds[choiceIndex + 1] || '';
|
|
65
|
+
return (_jsxs("div", { className: "space-y-1.5", children: [_jsx("p", { className: "text-xs font-medium uppercase tracking-wider text-gray-500", children: column.label || column.id }), column.fragments.map((fragment, fragmentIndex) => {
|
|
66
|
+
const isSelected = selectedId === fragment.id;
|
|
67
|
+
return (_jsxs(KidChoiceRow, { letter: optionLetter(fragmentIndex), selected: isSelected, isReviewMode: isReviewMode, isCorrect: isReviewMode && fragment.id === correctId, disabled: !canSelect, onClick: canSelect
|
|
68
|
+
? () => handleChoiceChange(itemIndex, choiceIndex, isSelected ? '' : fragment.id)
|
|
69
|
+
: undefined, children: [fragment.id, ". ", fragment.text] }, fragment.id));
|
|
70
|
+
})] }, `${itemIndex}-${column.id}`));
|
|
71
|
+
}), _jsx("p", { className: cn('text-sm', isReviewMode
|
|
72
|
+
? isCorrect
|
|
73
|
+
? 'text-green-700'
|
|
74
|
+
: 'text-red-700'
|
|
75
|
+
: 'text-slate-600'), children: current.sentence || 'Chưa ghép câu' }), isReviewMode && !item.isExample && (_jsxs("p", { className: "text-xs text-slate-500", children: ["\u0110\u00E1p \u00E1n: ", item.sentence] }))] }, `${item.questionNumber}-${itemIndex}`));
|
|
73
76
|
}) }), isReviewMode && questionData.explanation && (_jsxs("div", { className: "flex items-start gap-2 rounded-xl border border-amber-100 bg-amber-50 px-4 py-3 text-sm text-amber-800", children: [_jsx(Lightbulb, { className: "mt-0.5 h-4 w-4 flex-shrink-0" }), questionData.explanation] }))] }));
|
|
74
77
|
}
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
4
|
-
import { Label } from '../../../../components/ui/label';
|
|
5
|
-
import { BookOpen, CheckCircle2, XCircle } from 'lucide-react';
|
|
3
|
+
import { CheckCircle2 } from 'lucide-react';
|
|
6
4
|
import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
|
|
7
5
|
import { MATCH_WORD_TO_PICTURE_DEFAULT_INSTRUCTION, } from '../../_shared/types/match-word-to-picture.type';
|
|
8
6
|
import { useState } from 'react';
|
|
7
|
+
import { KidChoiceRow, KidInstruction, KidQuestionCard, optionLetter, } from '../../_shared/components/compact';
|
|
9
8
|
export function MatchWordToPictureClient({ questionData, onSubmit, isReviewMode = false, userAnswer, autoFillCorrectAnswers = false, explanation, }) {
|
|
10
9
|
const [answer, setAnswer] = useState(userAnswer || '');
|
|
11
10
|
const { previewUrl: questionImageUrl } = usePresignedFileUrl(questionData.imageUrl || '');
|
|
12
11
|
const wordBank = questionData.wordBank || [];
|
|
13
12
|
if (!questionData.imageUrl) {
|
|
14
|
-
return (_jsx(
|
|
13
|
+
return (_jsx(KidQuestionCard, { title: "Gh\u00E9p t\u1EEB v\u1EDBi tranh", children: _jsx("p", { className: "text-gray-500", children: "Kh\u00F4ng c\u00F3 d\u1EEF li\u1EC7u c\u00E2u h\u1ECFi" }) }));
|
|
15
14
|
}
|
|
16
15
|
const isExample = questionData.isExample || false;
|
|
17
16
|
const displayedAnswer = autoFillCorrectAnswers || isExample
|
|
@@ -20,12 +19,17 @@ export function MatchWordToPictureClient({ questionData, onSubmit, isReviewMode
|
|
|
20
19
|
const isCorrect = isReviewMode && userAnswer !== undefined
|
|
21
20
|
? userAnswer.trim().toLowerCase() === questionData.correctAnswer.trim().toLowerCase()
|
|
22
21
|
: undefined;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
22
|
+
const canSelect = !isReviewMode && !isExample;
|
|
23
|
+
const handleSelect = (word) => {
|
|
24
|
+
if (!canSelect)
|
|
25
|
+
return;
|
|
26
|
+
const next = displayedAnswer === word ? '' : word;
|
|
27
|
+
setAnswer(next);
|
|
28
|
+
onSubmit?.(next);
|
|
29
|
+
};
|
|
30
|
+
return (_jsxs(KidQuestionCard, { title: "Gh\u00E9p t\u1EEB v\u1EDBi tranh", isExample: isExample, review: isReviewMode && isCorrect !== undefined ? (isCorrect ? 'correct' : 'wrong') : null, children: [_jsx(KidInstruction, { children: questionData.instruction || MATCH_WORD_TO_PICTURE_DEFAULT_INSTRUCTION }), wordBank.length > 0 && (_jsxs("div", { className: "rounded-xl border border-blue-100 bg-blue-50/60 p-3", children: [_jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wider text-blue-700", children: "Word bank" }), _jsx("div", { className: "flex flex-wrap gap-2", children: wordBank.map((word, index) => (_jsx("span", { className: "rounded-full border border-gray-200 bg-white px-3 py-1 text-sm font-semibold text-slate-700", children: word }, `${word}-${index}`))) })] })), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx("div", { className: "relative h-24 w-24 flex-shrink-0 overflow-hidden rounded-lg border border-gray-200 bg-gray-50 shadow-sm", children: _jsx("img", { src: questionImageUrl || questionData.imageUrl, alt: questionData.correctAnswer || 'Picture', className: "h-full w-full object-contain" }) }), _jsx("p", { className: "text-sm font-semibold text-gray-800", children: "Ch\u1ECDn t\u1EEB ph\u00F9 h\u1EE3p" })] }), _jsx("div", { className: "space-y-1.5", children: wordBank.map((word, index) => {
|
|
31
|
+
const isSelected = displayedAnswer === word;
|
|
32
|
+
const isWordCorrect = word.trim().toLowerCase() === questionData.correctAnswer.trim().toLowerCase();
|
|
33
|
+
return (_jsx(KidChoiceRow, { letter: optionLetter(index), selected: isSelected, isReviewMode: isReviewMode, isCorrect: isReviewMode && isWordCorrect, disabled: !canSelect, onClick: canSelect ? () => handleSelect(word) : undefined, children: word }, `${word}-${index}`));
|
|
34
|
+
}) }), isReviewMode && isCorrect === false && (_jsxs("div", { className: "flex items-center gap-2 rounded-lg border border-green-200 bg-green-50 px-4 py-2", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-green-600" }), _jsxs("span", { className: "text-sm text-green-700", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng: ", _jsx("strong", { children: questionData.correctAnswer })] })] })), explanation && isReviewMode && (_jsxs("div", { className: "rounded-xl border border-blue-200 bg-blue-50 p-4", children: [_jsx("p", { className: "mb-1 text-sm font-semibold text-blue-800", children: "Gi\u1EA3i th\u00EDch:" }), _jsx("p", { className: "text-sm text-blue-700", children: explanation })] }))] }));
|
|
31
35
|
}
|
package/dist/components/questions/types/match-word-to-picture/MatchWordToPictureGroupClient.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import {
|
|
3
|
+
import { Lightbulb } from 'lucide-react';
|
|
4
4
|
import { cn } from '../../../../shared/lib/utils';
|
|
5
5
|
import { usePresignedFileUrl } from '../../../../shared/lib/hooks';
|
|
6
|
+
import { KidChoiceRow, KidInstruction, KidQuestionCard, optionLetter, } from '../../_shared/components/compact';
|
|
6
7
|
function normalizeWord(value) {
|
|
7
8
|
return value.trim().toLowerCase();
|
|
8
9
|
}
|
|
@@ -19,7 +20,7 @@ function ClientImageItem({ url, alt }) {
|
|
|
19
20
|
if (!src && !isLoading) {
|
|
20
21
|
return (_jsx("div", { className: "flex h-28 w-28 items-center justify-center rounded-lg border border-gray-200 bg-gray-50 text-xs text-gray-400", children: "Kh\u00F4ng t\u1EA3i \u0111\u01B0\u1EE3c \u1EA3nh" }));
|
|
21
22
|
}
|
|
22
|
-
return (_jsx("div", { className: "relative h-28 w-28 flex-shrink-0 overflow-hidden rounded-lg border border-gray-200 bg-gray-50", children: isLoading ? (_jsx("div", { className: "flex h-full w-full items-center justify-center", children: _jsx("div", { className: "h-6 w-6 animate-spin rounded-full border-2 border-gray-200 border-t-
|
|
23
|
+
return (_jsx("div", { className: "relative h-28 w-28 flex-shrink-0 overflow-hidden rounded-lg border border-gray-200 bg-gray-50", children: isLoading ? (_jsx("div", { className: "flex h-full w-full items-center justify-center", children: _jsx("div", { className: "h-6 w-6 animate-spin rounded-full border-2 border-gray-200 border-t-blue-500" }) })) : (_jsx("img", { src: src, alt: alt || 'Picture', className: "h-full w-full object-contain" })) }));
|
|
23
24
|
}
|
|
24
25
|
export function MatchWordToPictureGroupClient({ questionData, isReviewMode = false, userAnswers = [], onAnswerChange, }) {
|
|
25
26
|
const items = questionData.items || [];
|
|
@@ -40,11 +41,11 @@ export function MatchWordToPictureGroupClient({ questionData, isReviewMode = fal
|
|
|
40
41
|
usedWords.add(normalizeWord(value));
|
|
41
42
|
}
|
|
42
43
|
});
|
|
43
|
-
return (_jsxs("div", { className: "space-y-4", children: [
|
|
44
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx(KidInstruction, { children: questionData.instruction }), wordBank.length > 0 && (_jsxs("div", { className: "rounded-xl border border-blue-100 bg-blue-50/60 p-3", children: [_jsx("p", { className: "mb-2 text-xs font-semibold uppercase tracking-wider text-blue-700", children: "Word bank" }), _jsx("div", { className: "flex flex-wrap gap-2", children: wordBank.map((word, index) => {
|
|
44
45
|
const isUsed = usedWords.has(normalizeWord(word));
|
|
45
46
|
return (_jsx("span", { className: cn('rounded-full border px-3 py-1 text-sm font-semibold', isUsed
|
|
46
|
-
? 'border-
|
|
47
|
-
: 'border-
|
|
47
|
+
? 'border-blue-500 bg-blue-50 text-blue-800 ring-2 ring-blue-100'
|
|
48
|
+
: 'border-gray-200 bg-white text-slate-700'), children: word }, `${word}-${index}`));
|
|
48
49
|
}) })] })), items.map((item, itemIndex) => {
|
|
49
50
|
const selectedWord = item.isExample
|
|
50
51
|
? item.correctAnswer
|
|
@@ -52,14 +53,13 @@ export function MatchWordToPictureGroupClient({ questionData, isReviewMode = fal
|
|
|
52
53
|
const canSelect = !isReviewMode && !item.isExample && Boolean(onAnswerChange);
|
|
53
54
|
const isCorrect = normalizeWord(selectedWord) === normalizeWord(item.correctAnswer) &&
|
|
54
55
|
Boolean(item.correctAnswer);
|
|
55
|
-
return (
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
})] }), isReviewMode && !item.isExample && (_jsx("span", { className: "flex items-center gap-1", children: isCorrect ? (_jsx(Check, { className: "h-5 w-5 text-green-500" })) : (_jsx(X, { className: "h-5 w-5 text-red-500" })) }))] }), isReviewMode && !item.isExample && !isCorrect && (_jsxs("p", { className: "text-sm text-green-700", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng: ", _jsx("strong", { children: item.correctAnswer || '—' })] }))] })] })] }, `${item.questionNumber}-${itemIndex}`));
|
|
56
|
+
return (_jsx(KidQuestionCard, { title: `Câu ${item.questionNumber || itemIndex + 1}`, points: item.points || 0, isExample: item.isExample, review: isReviewMode && !item.isExample ? (isCorrect ? 'correct' : 'wrong') : null, children: _jsxs("div", { className: "flex flex-col gap-3 sm:flex-row sm:items-start", children: [_jsx(ClientImageItem, { url: item.imageUrl, alt: `Hình ảnh câu ${item.questionNumber || itemIndex + 1}` }), _jsxs("div", { className: "min-w-0 flex-1 space-y-1.5", children: [_jsx("p", { className: "text-sm font-semibold text-gray-800", children: "Ch\u1ECDn t\u1EEB ph\u00F9 h\u1EE3p" }), wordBank.map((word, wordIndex) => {
|
|
57
|
+
const isSelected = normalizeWord(word) === normalizeWord(selectedWord);
|
|
58
|
+
const isUsedByOther = usedWords.has(normalizeWord(word)) && !isSelected;
|
|
59
|
+
const isWordCorrect = normalizeWord(word) === normalizeWord(item.correctAnswer);
|
|
60
|
+
return (_jsx(KidChoiceRow, { letter: optionLetter(wordIndex), selected: isSelected, isReviewMode: isReviewMode, isCorrect: isReviewMode && isWordCorrect, disabled: !canSelect || isUsedByOther, onClick: canSelect && !isUsedByOther
|
|
61
|
+
? () => handleSelect(itemIndex, isSelected ? '' : word)
|
|
62
|
+
: undefined, children: word }, `${word}-${wordIndex}`));
|
|
63
|
+
}), isReviewMode && !item.isExample && !isCorrect && (_jsxs("p", { className: "text-sm text-green-700", children: ["\u0110\u00E1p \u00E1n \u0111\u00FAng: ", _jsx("strong", { children: item.correctAnswer || '—' })] }))] })] }) }, `${item.questionNumber}-${itemIndex}`));
|
|
64
64
|
}), 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] }))] }));
|
|
65
65
|
}
|